1/* 2 * flashutl.c - Flash Read/write/Erase routines 3 * 4 * Copyright 2007, Broadcom Corporation 5 * All Rights Reserved. 6 * 7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY 8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM 9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. 11 * 12 * $Id: flashutl.c,v 1.1.1.1 2008/10/15 03:31:34 james26_jang Exp $ 13 */ 14 15#include <typedefs.h> 16#include <osl.h> 17 18#define DECLARE_FLASHES 19#include <bcmutils.h> 20#include <sbutils.h> 21#include <sbconfig.h> 22#include <flash.h> 23#include <sflash.h> 24#include <flashutl.h> 25#include <bcmnvram.h> 26 27#define DPRINT(x) 28 29#define ERR2 0x30 /* Mask for err UNUSED */ 30#define DONE 0x80 /* Mask for done */ 31#define WBUFSIZE 32 /* Write Buffer size */ 32#define FLASH_TRIES 4000000 /* retry count */ 33#define CMD_ADDR ((unsigned long)0xFFFFFFFF) 34 35/* 'which' param for block() */ 36#define BLOCK_BASE 0 /* Base of block */ 37#define BLOCK_LIM 1 /* Limit of block */ 38 39#define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off)) 40 41/* Local vars */ 42static sb_t *sbh = NULL; 43static chipcregs_t *cc = NULL; 44 45/* Global vars */ 46uint8 *flashutl_base = NULL; 47flash_desc_t *flashutl_desc = NULL; 48flash_cmds_t *flashutl_cmd = NULL; 49uint8 flashutl_wsz = sizeof(uint16); 50 51static void scmd(uint16 cmd, unsigned long off); 52static void cmd(uint16 cmd, unsigned long off); 53static void flash_reset(void); 54static int flash_poll(unsigned long off, uint16 data); 55static unsigned long block(unsigned long addr, int which); 56static int flash_eraseblk(unsigned long off); 57static int flash_write(unsigned long off, uint8 *src, uint nbytes); 58static uint16 INLINE flash_readword(unsigned long addr); 59static void INLINE flash_writeword(unsigned long addr, uint16 data); 60 61int sysFlashErase(uint off, unsigned int numbytes); 62 63/* Read the flash ID and set the globals */ 64int 65sysFlashInit(char *flash_str) 66{ 67 osl_t *osh; 68 uint32 fltype = PFLASH; 69 uint16 flash_vendid = 0; 70 uint16 flash_devid = 0; 71 int idx; 72 struct sflash *sflash; 73 74 /* 75 * Check for serial flash. 76 */ 77 sbh = sb_kattach(SB_OSH); 78 ASSERT(sbh); 79 80 osh = sb_osh(sbh); 81 82 flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH1); 83 flashutl_wsz = sizeof(uint16); 84 cc = (chipcregs_t *)sb_setcore(sbh, SB_CC, 0); 85 if (cc) { 86 flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH2); 87 flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ? 88 sizeof(uint16) : sizeof(uint8); 89 /* Select SFLASH ? */ 90 fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK; 91 if (fltype == SFLASH_ST || fltype == SFLASH_AT) { 92 sflash = sflash_init(sbh, cc); 93 flashutl_cmd = &sflash_cmd_t; 94 flashutl_desc = &sflash_desc; 95 flashutl_desc->size = sflash->size; 96 if (flash_str) 97 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024); 98 return (0); 99 } 100 } 101 102 ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16)); 103 104 /* 105 * Parallel flash support 106 * Some flashes have different unlock addresses, try each it turn 107 */ 108 for (idx = 0; 109 fltype == PFLASH && idx < ARRAYSIZE(flash_cmds); 110 idx ++) { 111 flashutl_cmd = &flash_cmds[idx]; 112 if (flashutl_cmd->type == OLD) 113 continue; 114 115 if (flashutl_cmd->read_id) 116 cmd(flashutl_cmd->read_id, CMD_ADDR); 117 118#ifdef MIPSEB 119 flash_vendid = flash_readword(FLASH_ADDR(2)); 120 flash_devid = flash_readword(FLASH_ADDR(0)); 121#else 122 flash_vendid = flash_readword(FLASH_ADDR(0)); 123 flash_devid = flash_readword(FLASH_ADDR(2)); 124#endif /* MIPSEB */ 125 126 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to 127 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr) 128 * to form a word for unique identification of format xxyy, where 129 * xx = 5th addr and yy = 6th addr 130 */ 131 if ((flash_vendid == 1) && (flash_devid == 0x227e)) { 132 /* Get real devid */ 133 uint16 flash_devid_5th; 134#ifdef MIPSEB 135 flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8; 136 flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th; 137#else 138 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8; 139 flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th; 140#endif /* MIPSEB */ 141 } 142 143 flashutl_desc = flashes; 144 while (flashutl_desc->mfgid != 0 && 145 !(flashutl_desc->mfgid == flash_vendid && 146 flashutl_desc->devid == flash_devid)) { 147 flashutl_desc++; 148 } 149 if (flashutl_desc->mfgid != 0) 150 break; 151 } 152 153 if (flashutl_desc->mfgid == 0) { 154 flashutl_desc = NULL; 155 flashutl_cmd = NULL; 156 } else { 157 flashutl_cmd = flash_cmds; 158 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type) 159 flashutl_cmd++; 160 if (flashutl_cmd->type == 0) 161 flashutl_cmd = NULL; 162 } 163 164 if (flashutl_cmd != NULL) { 165 flash_reset(); 166 } 167 168 if (flashutl_desc == NULL) { 169 if (flash_str) 170 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid); 171 DPRINT(("Flash type UNKNOWN\n")); 172 return 1; 173 } 174 175 if (flash_str) 176 strcpy(flash_str, flashutl_desc->desc); 177 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc)); 178 179 return 0; 180} 181 182static int 183flash_eraseblk(unsigned long addr) 184{ 185 unsigned long a; 186 uint16 st; 187 188 a = (unsigned long)addr; 189 if (a >= flashutl_desc->size) 190 return 1; 191 192 a = block(a, BLOCK_BASE); 193 194 /* Ensure blocks are unlocked (for intel chips) */ 195 if (flashutl_cmd->type == BSC) { 196 scmd((unsigned char)INTEL_UNLOCK1, a); 197 scmd((unsigned char)INTEL_UNLOCK2, a); 198 } 199 200 if (flashutl_cmd->pre_erase) 201 cmd(flashutl_cmd->pre_erase, CMD_ADDR); 202 if (flashutl_cmd->erase_block) 203 cmd(flashutl_cmd->erase_block, a); 204 if (flashutl_cmd->confirm) 205 scmd(flashutl_cmd->confirm, a); 206 207 if (flashutl_wsz == sizeof(uint8)) 208 st = flash_poll(a, 0xff); 209 else 210 st = flash_poll(a, 0xffff); 211 212 flash_reset(); 213 214 if (st) { 215 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n", 216 a, block((unsigned long)addr, BLOCK_LIM))); 217 return st; 218 } 219 220 DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a, block((unsigned long)addr, BLOCK_LIM))); 221 222 return 0; 223} 224 225static int 226flash_write(unsigned long off, uint8 *src, uint nbytes) 227{ 228 uint8 *dest; 229 uint16 st, data; 230 uint i, len; 231 232 ASSERT(flashutl_desc != NULL); 233 234 if (off >= flashutl_desc->size) 235 return 1; 236 237 ASSERT(!(off & (flashutl_wsz - 1))); 238 239 dest = (uint8*)FLASH_ADDR(off); 240 st = 0; 241 242 while (nbytes) { 243 if ((flashutl_desc->type == SCS) && 244 flashutl_cmd->write_buf && 245 ((off & (WBUFSIZE - 1)) == 0)) { 246 /* issue write command */ 247 if (flashutl_cmd->write_buf) 248 cmd(flashutl_cmd->write_buf, off); 249 if ((st = flash_poll(off, DONE))) 250 continue; 251 252 len = MIN(nbytes, WBUFSIZE); 253 254#ifndef MIPSEB 255 /* write (length - 1) */ 256 cmd(len / sizeof(uint16) - 1, off); 257 258 /* write data */ 259 for (i = 0; i < len; i += sizeof(uint16), 260 dest += sizeof(uint16), src += sizeof(uint16)) 261 *(uint16 *)dest = *(uint16 *)src; 262#else 263 /* 264 * BCM4710 endianness is word consistent but 265 * byte/short scrambled. This write buffer 266 * mechanism appears to be sensitive to the 267 * order of the addresses hence we need to 268 * unscramble them. We may also need to pad 269 * the source with two bytes of 0xffff in case 270 * an odd number of shorts are presented. 271 */ 272 273 /* write (padded length - 1) */ 274 cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off); 275 276 /* write data (plus pad if necessary) */ 277 for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32), 278 dest += sizeof(uint32), src += sizeof(uint32)) { 279 *((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ? 280 *((uint16 *)src + 1) : 0xffff; 281 *(uint16 *)dest = *(uint16 *)src; 282 } 283#endif /* MIPSEB */ 284 285 /* write confirm */ 286 if (flashutl_cmd->confirm) 287 cmd(flashutl_cmd->confirm, off); 288 289 if ((st = flash_poll(off, DONE))) 290 break; 291 } else { 292 /* issue write command */ 293 if (flashutl_cmd->write_word) 294 cmd(flashutl_cmd->write_word, CMD_ADDR); 295 296 /* write data */ 297 data = flash_readword((unsigned long)src); 298 flash_writeword((unsigned long)dest, data); 299 300 /* poll for done */ 301 if ((st = flash_poll(off, data))) 302 break; 303 304 len = MIN(nbytes, flashutl_wsz); 305 dest += len; 306 src += len; 307 } 308 309 nbytes -= len; 310 off += len; 311 } 312 313 flash_reset(); 314 315 return st; 316} 317 318static uint16 INLINE 319flash_readword(unsigned long addr) 320{ 321 if (flashutl_wsz == sizeof(uint8)) 322 return *(uint8*)addr; 323 else 324 return *(uint16*)addr; 325} 326 327static void INLINE 328flash_writeword(unsigned long addr, uint16 data) 329{ 330 if (flashutl_wsz == sizeof(uint8)) 331 *(uint8*)addr = (uint8)data; 332 else 333 *(uint16*)addr = data; 334} 335 336/* Writes a single command to the flash. */ 337static void 338scmd(uint16 cmd, unsigned long off) 339{ 340 /* cmd |= cmd << 8; */ 341 342 flash_writeword(FLASH_ADDR(off), cmd); 343} 344 345/* Writes a command to flash, performing an unlock if needed. */ 346static void 347cmd(uint16 cmd, unsigned long off) 348{ 349 int i; 350 unlock_cmd_t *ul = NULL; 351 352 ASSERT(flashutl_cmd != NULL); 353 354 switch (flashutl_cmd->type) { 355 case AMD: 356 ul = &unlock_cmd_amd; 357 break; 358 case SST: 359 ul = &unlock_cmd_sst; 360 break; 361 default: 362 break; 363 } 364 365 if (flashutl_cmd->need_unlock) { 366 ASSERT(ul); 367 for (i = 0; i < UNLOCK_CMD_WORDS; i++) 368 flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]); 369 } 370 371 /* cmd |= cmd << 8; */ 372 373 if (off == CMD_ADDR) { 374 switch (flashutl_cmd->type) { 375 case AMD: 376 off = AMD_CMD; 377 break; 378 case SST: 379 off = SST_CMD; 380 break; 381 default: 382 off = 0; 383 break; 384 } 385 } 386 387#ifdef MIPSEB 388 off ^= 2; 389#endif /* MIPSEB */ 390 391 flash_writeword(FLASH_ADDR(off), cmd); 392} 393 394static void 395flash_reset() 396{ 397 ASSERT(flashutl_desc != NULL); 398 399 if (flashutl_cmd->clear_csr) 400 scmd(flashutl_cmd->clear_csr, 0); 401 if (flashutl_cmd->read_array) 402 scmd(flashutl_cmd->read_array, 0); 403} 404 405static int 406flash_poll(unsigned long off, uint16 data) 407{ 408 unsigned long addr; 409 int cnt = FLASH_TRIES; 410 uint16 st; 411 412 ASSERT(flashutl_desc != NULL); 413 414 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) { 415 /* AMD style poll checkes the address being written */ 416 addr = FLASH_ADDR(off); 417 while ((st = flash_readword(addr)) != data && cnt != 0) 418 cnt--; 419 if (cnt == 0) { 420 DPRINT(("flash_poll: timeout, off %lx, read 0x%x, expected 0x%x\n", 421 off, st, data)); 422 return -1; 423 } 424 } else { 425 /* INTEL style poll is at second word of the block being written */ 426 addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16)); 427 while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0) 428 cnt--; 429 if (cnt == 0) { 430 DPRINT(("flash_poll: timeout, error status = 0x%x\n", st)); 431 return -1; 432 } 433 } 434 435 return 0; 436} 437 438static unsigned long 439block(unsigned long addr, int which) 440{ 441 unsigned long b, l, sb; 442 uint* sblocks; 443 int i; 444 445 ASSERT(flashutl_desc != NULL); 446 447 ASSERT(addr < (unsigned long)flashutl_desc->size); 448 449 b = addr / flashutl_desc->bsize; 450 /* check for an address a full size block */ 451 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) { 452 if (which == BLOCK_LIM) b++; 453 return (b * flashutl_desc->bsize); 454 } 455 456 /* search for the sub-block */ 457 if (flashutl_desc->ff == 0) { 458 /* sub blocks are at the end of the flash */ 459 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1); 460 } else { 461 /* sub blocks are at the start of the flash */ 462 sb = 0; 463 } 464 465 sblocks = flashutl_desc->subblocks; 466 for (i = 0; i < flashutl_desc->nsub; i++) { 467 b = sb + sblocks[i]; 468 l = sb + sblocks[i+1]; 469 if (addr >= b && addr < l) { 470 if (which == BLOCK_BASE) 471 return b; 472 else 473 return l; 474 } 475 } 476 477 return 0; 478} 479 480void 481nvWrite(unsigned short *data, unsigned int len) 482{ 483 uint off = flashutl_desc->size - NVRAM_SPACE; 484 sysFlashWrite(off, (uchar*)data, len); 485} 486 487void 488nvWriteChars(unsigned char *data, unsigned int len) 489{ 490 uint off = flashutl_desc->size - NVRAM_SPACE; 491 int err; 492 493 if (flashutl_cmd->type == SFLASH) 494 err = sflash_commit(sbh, cc, off, len, data); 495 else /* PFLASH */ 496 err = flash_write(off, data, len); 497 498 if (err) 499 DPRINT(("nvWriteChars failed\n")); 500 else 501 DPRINT(("nvWriteChars succeeded\n")); 502} 503 504int 505sysFlashErase(uint off, unsigned int numbytes) 506{ 507 unsigned long end = off + numbytes; 508 int err = 0; 509 510 if (flashutl_cmd->type == SFLASH) { 511 err = sflash_commit(sbh, cc, off, numbytes, NULL); 512 } else { 513 while (off < end) { 514 err = flash_eraseblk(off); 515 if (err) 516 break; 517 off = block(off, BLOCK_LIM); 518 } 519 } 520 521 if (err) 522 DPRINT(("Block erase at 0x%x failed\n", off)); 523 else 524 DPRINT(("Done\n")); 525 526 return !err; 527} 528 529int 530sysFlashWrite(uint off, uchar *src, uint numbytes) 531{ 532 int err; 533 534 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off)); 535 536 if (flashutl_cmd->type == SFLASH) 537 err = sflash_commit(sbh, cc, off, numbytes, src); 538 else { 539 if (!sysFlashErase(off, numbytes)) 540 return 0; 541 err = flash_write(off, src, numbytes); 542 } 543 544 if (err) 545 DPRINT(("Flash write failed\n")); 546 else 547 DPRINT(("Flash write succeeded\n")); 548 549 return !err; 550} 551 552int 553sysFlashRead(uint off, uchar *buf, uint numbytes) 554{ 555 uint read, total_read = 0; 556 557 if (flashutl_cmd->type == SFLASH) { 558 while (numbytes) { 559 read = sflash_read(sbh, cc, off, numbytes, buf); 560 numbytes -= read; 561 buf += read; 562 off += read; 563 total_read += read; 564 } 565 } else { 566 ASSERT(!(off & (flashutl_wsz - 1))); 567 ASSERT(!(numbytes & (flashutl_wsz - 1))); 568 569 while (numbytes) { 570 flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off))); 571 numbytes -= flashutl_wsz; 572 buf += flashutl_wsz; 573 off += flashutl_wsz; 574 total_read += flashutl_wsz; 575 } 576 } 577 578 return (total_read); 579} 580