2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * $Id: flashutl.c,v 1.52 2009-05-19 02:06:58 Exp $
24 #define DECLARE_FLASHES
35 #define ERR2 0x30 /* Mask for err UNUSED */
36 #define DONE 0x80 /* Mask for done */
37 #define WBUFSIZE 32 /* Write Buffer size */
38 #define FLASH_TRIES 700000 /* retry count */
39 #define CMD_ADDR ((unsigned long)0xFFFFFFFF)
41 /* 'which' param for block() */
42 #define BLOCK_BASE 0 /* Base of block */
43 #define BLOCK_LIM 1 /* Limit of block */
45 #define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
48 static si_t
*sih
= NULL
;
49 static chipcregs_t
*cc
= NULL
;
52 uint8
*flashutl_base
= NULL
;
53 flash_desc_t
*flashutl_desc
= NULL
;
54 flash_cmds_t
*flashutl_cmd
= NULL
;
55 uint8 flashutl_wsz
= sizeof(uint16
);
57 static void scmd(uint16 cmd
, unsigned long off
);
58 static void cmd(uint16 cmd
, unsigned long off
);
59 static void flash_reset(void);
60 static int flash_poll(unsigned long off
, uint16 data
);
61 static unsigned long block(unsigned long addr
, int which
);
62 static int flash_eraseblk(unsigned long off
);
63 static int flash_write(unsigned long off
, uint8
*src
, uint nbytes
);
64 static uint16 INLINE
flash_readword(unsigned long addr
);
65 static void INLINE
flash_writeword(unsigned long addr
, uint16 data
);
67 int sysFlashErase(uint off
, unsigned int numbytes
);
69 /* Read the flash ID and set the globals */
71 sysFlashInit(char *flash_str
)
74 uint32 fltype
= PFLASH
;
75 uint16 flash_vendid
= 0;
76 uint16 flash_devid
= 0;
78 struct sflash
*sflash
;
81 * Check for serial flash.
83 sih
= si_kattach(SI_OSH
);
88 cc
= (chipcregs_t
*)si_setcoreidx(sih
, SI_CC_IDX
);
91 flashutl_base
= (void *)OSL_UNCACHED((uintptr
)SI_FLASH2
);
93 fltype
= R_REG(osh
, &cc
->capabilities
) & CC_CAP_FLASH_MASK
;
94 if (fltype
== SFLASH_ST
|| fltype
== SFLASH_AT
) {
96 flashutl_base
= (void *)OSL_UNCACHED((uintptr
)SI_FLASH2
);
98 flashutl_base
= (void *)OSL_CACHED((uintptr
)SI_FLASH2
);
99 sflash
= sflash_init(sih
, cc
);
100 flashutl_cmd
= &sflash_cmd_t
;
101 flashutl_desc
= &sflash_desc
;
102 flashutl_desc
->size
= sflash
->size
;
104 sprintf(flash_str
, "SFLASH %d kB", sflash
->size
/1024);
108 flashutl_wsz
= (R_REG(osh
, &cc
->flash_config
) & CC_CFG_DS
) ? sizeof(uint16
) : sizeof(uint8
);
109 ASSERT(flashutl_wsz
== sizeof(uint8
) || flashutl_wsz
== sizeof(uint16
));
112 * Parallel flash support
113 * Some flashes have different unlock addresses, try each it turn
116 fltype
== PFLASH
&& idx
< ARRAYSIZE(flash_cmds
);
118 flashutl_cmd
= &flash_cmds
[idx
];
119 if (flashutl_cmd
->type
== OLD
)
122 if (flashutl_cmd
->read_id
) {
123 cmd(flashutl_cmd
->read_id
, CMD_ADDR
);
124 /* Delay for turn around time */
130 flash_vendid
= flash_readword(FLASH_ADDR(0)^6);
131 flash_devid
= flash_readword(FLASH_ADDR(2)^6);
132 #else /* !74K, bcm33xx */
133 flash_vendid
= flash_readword(FLASH_ADDR(2));
134 flash_devid
= flash_readword(FLASH_ADDR(0));
135 #endif /* BCMHND74K */
137 flash_vendid
= flash_readword(FLASH_ADDR(0));
138 flash_devid
= flash_readword(FLASH_ADDR(2));
141 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
142 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
143 * to form a word for unique identification of format xxyy, where
144 * xx = 5th addr and yy = 6th addr
146 if ((flash_vendid
== 1) &&
147 ((flash_devid
== 0x227e && flashutl_wsz
== sizeof(uint16
)) ||
148 (flash_devid
== 0x7e && flashutl_wsz
== sizeof(uint8
)))) {
150 uint16 flash_devid_5th
;
153 flash_devid_5th
= flash_readword(FLASH_ADDR(0x1c)^6) << 8;
154 flash_devid
= (flash_readword(FLASH_ADDR(0x1e)^6) & 0xff) | flash_devid_5th
;
155 #else /* !74K, bcm33xx */
156 flash_devid_5th
= flash_readword(FLASH_ADDR(0x1e)) << 8;
157 flash_devid
= (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th
;
158 #endif /* BCMHND74K */
160 flash_devid_5th
= flash_readword(FLASH_ADDR(0x1c)) << 8;
161 flash_devid
= (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th
;
165 flashutl_desc
= flashes
;
166 while (flashutl_desc
->mfgid
!= 0 &&
167 !(flashutl_desc
->mfgid
== flash_vendid
&&
168 flashutl_desc
->devid
== flash_devid
)) {
171 if (flashutl_desc
->mfgid
!= 0)
175 if (flashutl_desc
->mfgid
== 0) {
176 flashutl_desc
= NULL
;
179 flashutl_cmd
= flash_cmds
;
180 while (flashutl_cmd
->type
!= 0 && flashutl_cmd
->type
!= flashutl_desc
->type
)
182 if (flashutl_cmd
->type
== 0)
186 if (flashutl_cmd
!= NULL
) {
190 if (flashutl_desc
== NULL
) {
192 sprintf(flash_str
, "UNKNOWN 0x%x 0x%x", flash_vendid
, flash_devid
);
193 DPRINT(("Flash type UNKNOWN\n"));
198 strcpy(flash_str
, flashutl_desc
->desc
);
199 DPRINT(("Flash type \"%s\"\n", flashutl_desc
->desc
));
205 flash_eraseblk(unsigned long addr
)
210 a
= (unsigned long)addr
;
211 if (a
>= flashutl_desc
->size
)
214 a
= block(a
, BLOCK_BASE
);
216 /* Ensure blocks are unlocked (for intel chips) */
217 if (flashutl_cmd
->type
== BSC
) {
218 scmd((unsigned char)INTEL_UNLOCK1
, a
);
219 scmd((unsigned char)INTEL_UNLOCK2
, a
);
222 if (flashutl_cmd
->pre_erase
)
223 cmd(flashutl_cmd
->pre_erase
, CMD_ADDR
);
224 if (flashutl_cmd
->erase_block
)
225 cmd(flashutl_cmd
->erase_block
, a
);
226 if (flashutl_cmd
->confirm
)
227 scmd(flashutl_cmd
->confirm
, a
);
229 if (flashutl_wsz
== sizeof(uint8
))
230 st
= flash_poll(a
, 0xff);
232 st
= flash_poll(a
, 0xffff);
237 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
238 a
, block((unsigned long)addr
, BLOCK_LIM
)));
242 DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a
, block((unsigned long)addr
, BLOCK_LIM
)));
248 flash_write(unsigned long off
, uint8
*src
, uint nbytes
)
254 ASSERT(flashutl_desc
!= NULL
);
256 if (off
>= flashutl_desc
->size
)
259 ASSERT(!(off
& (flashutl_wsz
- 1)));
261 dest
= (uint8
*)FLASH_ADDR(off
);
265 if ((flashutl_desc
->type
== SCS
) &&
266 flashutl_cmd
->write_buf
&&
267 ((off
& (WBUFSIZE
- 1)) == 0)) {
268 /* issue write command */
269 if (flashutl_cmd
->write_buf
)
270 cmd(flashutl_cmd
->write_buf
, off
);
271 if ((st
= flash_poll(off
, DONE
)))
274 len
= MIN(nbytes
, WBUFSIZE
);
277 /* write (length - 1) */
278 cmd(len
/ sizeof(uint16
) - 1, off
);
281 for (i
= 0; i
< len
; i
+= sizeof(uint16
),
282 dest
+= sizeof(uint16
), src
+= sizeof(uint16
))
283 *(uint16
*)dest
= *(uint16
*)src
;
286 * BCM4710 endianness is word consistent but
287 * byte/short scrambled. This write buffer
288 * mechanism appears to be sensitive to the
289 * order of the addresses hence we need to
290 * unscramble them. We may also need to pad
291 * the source with two bytes of 0xffff in case
292 * an odd number of shorts are presented.
295 /* write (padded length - 1) */
296 cmd((ROUNDUP(len
, sizeof(uint32
)) / sizeof(uint16
)) - 1, off
);
298 /* write data (plus pad if necessary) */
299 for (i
= 0; i
< ROUNDUP(len
, sizeof(uint32
)); i
+= sizeof(uint32
),
300 dest
+= sizeof(uint32
), src
+= sizeof(uint32
)) {
301 *((uint16
*)dest
+ 1) = ((i
+ sizeof(uint16
)) < len
) ?
302 *((uint16
*)src
+ 1) : 0xffff;
303 *(uint16
*)dest
= *(uint16
*)src
;
308 if (flashutl_cmd
->confirm
)
309 cmd(flashutl_cmd
->confirm
, off
);
311 if ((st
= flash_poll(off
, DONE
)))
314 /* issue write command */
315 if (flashutl_cmd
->write_word
)
316 cmd(flashutl_cmd
->write_word
, CMD_ADDR
);
319 data
= flash_readword((unsigned long)src
);
320 flash_writeword((unsigned long)dest
, data
);
323 if ((st
= flash_poll(off
, data
)))
326 len
= MIN(nbytes
, flashutl_wsz
);
341 flash_readword(unsigned long addr
)
343 if (flashutl_wsz
== sizeof(uint8
))
344 return *(uint8
*)addr
;
346 return *(uint16
*)addr
;
350 flash_writeword(unsigned long addr
, uint16 data
)
352 if (flashutl_wsz
== sizeof(uint8
))
353 *(uint8
*)addr
= (uint8
)data
;
355 *(uint16
*)addr
= data
;
358 /* Writes a single command to the flash. */
360 scmd(uint16 cmd
, unsigned long off
)
362 /* cmd |= cmd << 8; */
364 flash_writeword(FLASH_ADDR(off
), cmd
);
367 /* Writes a command to flash, performing an unlock if needed. */
369 cmd(uint16 cmd
, unsigned long off
)
372 unlock_cmd_t
*ul
= NULL
;
374 ASSERT(flashutl_cmd
!= NULL
);
376 switch (flashutl_cmd
->type
) {
378 ul
= &unlock_cmd_amd
;
381 ul
= &unlock_cmd_sst
;
387 if (flashutl_cmd
->need_unlock
) {
389 for (i
= 0; i
< UNLOCK_CMD_WORDS
; i
++)
390 flash_writeword(FLASH_ADDR(ul
->addr
[i
]), ul
->cmd
[i
]);
393 /* cmd |= cmd << 8; */
395 if (off
== CMD_ADDR
) {
396 switch (flashutl_cmd
->type
) {
412 #else /* !74K, bcm33xx */
414 #endif /* BCMHND74K */
417 flash_writeword(FLASH_ADDR(off
), cmd
);
423 ASSERT(flashutl_cmd
!= NULL
);
425 if (flashutl_cmd
->clear_csr
)
426 scmd(flashutl_cmd
->clear_csr
, 0);
427 if (flashutl_cmd
->read_array
)
428 scmd(flashutl_cmd
->read_array
, 0);
432 flash_poll(unsigned long off
, uint16 data
)
435 int cnt
= FLASH_TRIES
;
438 ASSERT(flashutl_desc
!= NULL
);
440 if (flashutl_desc
->type
== AMD
|| flashutl_desc
->type
== SST
) {
441 /* AMD style poll checkes the address being written */
442 addr
= FLASH_ADDR(off
);
443 while ((st
= flash_readword(addr
)) != data
&& cnt
!= 0) {
448 DPRINT(("%s: timeout, off %lx, read 0x%x, expected 0x%x\n",
449 __FUNCTION__
, off
, st
, data
));
453 /* INTEL style poll is at second word of the block being written */
454 addr
= FLASH_ADDR(block(off
, BLOCK_BASE
)+sizeof(uint16
));
455 while (((st
= flash_readword(addr
)) & DONE
) == 0 && cnt
!= 0) {
460 DPRINT(("%s: timeout, error status = 0x%x\n", __FUNCTION__
, st
));
469 block(unsigned long addr
, int which
)
471 unsigned long b
, l
, sb
;
475 ASSERT(flashutl_desc
!= NULL
);
477 ASSERT(addr
< (unsigned long)flashutl_desc
->size
);
479 b
= addr
/ flashutl_desc
->bsize
;
480 /* check for an address a full size block */
481 if (b
>= flashutl_desc
->ff
&& b
<= flashutl_desc
->lf
) {
482 if (which
== BLOCK_LIM
) b
++;
483 return (b
* flashutl_desc
->bsize
);
486 /* search for the sub-block */
487 if (flashutl_desc
->ff
== 0) {
488 /* sub blocks are at the end of the flash */
489 sb
= flashutl_desc
->bsize
* (flashutl_desc
->lf
+ 1);
491 /* sub blocks are at the start of the flash */
495 sblocks
= flashutl_desc
->subblocks
;
496 for (i
= 0; i
< flashutl_desc
->nsub
; i
++) {
498 l
= sb
+ sblocks
[i
+1];
499 if (addr
>= b
&& addr
< l
) {
500 if (which
== BLOCK_BASE
)
511 nvWrite(unsigned short *data
, unsigned int len
)
513 uint off
= flashutl_desc
->size
- NVRAM_SPACE
;
514 sysFlashWrite(off
, (uchar
*)data
, len
);
518 nvWriteChars(unsigned char *data
, unsigned int len
)
520 uint off
= flashutl_desc
->size
- NVRAM_SPACE
;
523 if (flashutl_cmd
->type
== SFLASH
)
524 err
= sflash_commit(sih
, cc
, off
, len
, data
);
526 err
= flash_write(off
, data
, len
);
529 DPRINT(("nvWriteChars failed\n"));
531 DPRINT(("nvWriteChars succeeded\n"));
535 sysFlashErase(uint off
, unsigned int numbytes
)
537 unsigned long end
= off
+ numbytes
;
540 if (flashutl_cmd
->type
== SFLASH
) {
541 err
= sflash_commit(sih
, cc
, off
, numbytes
, NULL
);
544 err
= flash_eraseblk(off
);
547 off
= block(off
, BLOCK_LIM
);
552 DPRINT(("Block erase at 0x%x failed\n", off
));
560 sysFlashWrite(uint off
, uchar
*src
, uint numbytes
)
564 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes
, off
));
566 if (flashutl_cmd
->type
== SFLASH
)
567 err
= sflash_commit(sih
, cc
, off
, numbytes
, src
);
569 if (!sysFlashErase(off
, numbytes
))
571 err
= flash_write(off
, src
, numbytes
);
575 DPRINT(("Flash write failed\n"));
577 DPRINT(("Flash write succeeded\n"));
583 sysFlashRead(uint off
, uchar
*buf
, uint numbytes
)
585 uint read
, total_read
= 0;
587 if (flashutl_cmd
->type
== SFLASH
) {
589 read
= sflash_read(sih
, cc
, off
, numbytes
, buf
);
596 ASSERT(!(off
& (flashutl_wsz
- 1)));
597 ASSERT(!(numbytes
& (flashutl_wsz
- 1)));
600 flash_writeword((unsigned long)buf
, flash_readword(FLASH_ADDR(off
)));
601 numbytes
-= flashutl_wsz
;
604 total_read
+= flashutl_wsz
;