1/* 2 * Broadcom SiliconBackplane chipcommon serial flash interface 3 * 4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved. 5 * 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. 9 * 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. 17 * 18 * $Id: sflash.c,v 1.54 2009/12/29 19:57:52 Exp $ 19 */ 20 21#include <typedefs.h> 22#include <osl.h> 23#include <bcmutils.h> 24#include <siutils.h> 25#include <hndsoc.h> 26#include <sbhndcpu.h> 27#include <sbchipc.h> 28#include <bcmdevs.h> 29#include <sflash.h> 30 31#define SFL_MSG(args) 32 33/* Private global state */ 34static struct sflash sflash; 35 36/* Issue a serial flash command */ 37static INLINE void 38sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode) 39{ 40 W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode); 41 while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY); 42} 43 44static bool firsttime = TRUE; 45 46/* Initialize serial flash access */ 47struct sflash * 48sflash_init(si_t *sih, chipcregs_t *cc) 49{ 50 uint32 id, id2; 51 char *name = ""; 52 osl_t *osh; 53 54 ASSERT(sih); 55 56 osh = si_osh(sih); 57 58 bzero(&sflash, sizeof(sflash)); 59 60 sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; 61 62 switch (sflash.type) { 63 case SFLASH_ST: 64 /* Probe for ST chips */ 65 name = "ST compatible"; 66 sflash_cmd(osh, cc, SFLASH_ST_DP); 67 W_REG(osh, &cc->flashaddress, 0); 68 sflash_cmd(osh, cc, SFLASH_ST_RES); 69 id = R_REG(osh, &cc->flashdata); 70 sflash.blocksize = 64 * 1024; 71 switch (id) { 72 case 0x11: 73 /* ST M25P20 2 Mbit Serial Flash */ 74 sflash.numblocks = 4; 75 break; 76 case 0x12: 77 /* ST M25P40 4 Mbit Serial Flash */ 78 sflash.numblocks = 8; 79 break; 80 case 0x13: 81 /* ST M25P80 8 Mbit Serial Flash */ 82 sflash.numblocks = 16; 83 break; 84 case 0x14: 85 /* ST M25P16 16 Mbit Serial Flash */ 86 sflash.numblocks = 32; 87 break; 88 case 0x15: 89 /* ST M25P32 32 Mbit Serial Flash */ 90 sflash.numblocks = 64; 91 break; 92 case 0x16: 93 /* ST M25P64 64 Mbit Serial Flash */ 94 sflash.numblocks = 128; 95 break; 96 case 0x17: 97 /* ST M25FL128 128 Mbit Serial Flash */ 98 sflash.numblocks = 256; 99 break; 100 case 0xbf: 101 /* All of the following flashes are SST with 102 * 4KB subsectors. Others should be added but 103 * We'll have to revamp the way we identify them 104 * since RES is not eough to disambiguate them. 105 */ 106 name = "SST"; 107 sflash.blocksize = 4 * 1024; 108 W_REG(osh, &cc->flashaddress, 1); 109 sflash_cmd(osh, cc, SFLASH_ST_RES); 110 id2 = R_REG(osh, &cc->flashdata); 111 switch (id2) { 112 case 1: 113 /* SST25WF512 512 Kbit Serial Flash */ 114 sflash.numblocks = 16; 115 break; 116 case 0x48: 117 /* SST25VF512 512 Kbit Serial Flash */ 118 sflash.numblocks = 16; 119 break; 120 case 2: 121 /* SST25WF010 1 Mbit Serial Flash */ 122 sflash.numblocks = 32; 123 break; 124 case 0x49: 125 /* SST25VF010 1 Mbit Serial Flash */ 126 sflash.numblocks = 32; 127 break; 128 case 3: 129 /* SST25WF020 2 Mbit Serial Flash */ 130 sflash.numblocks = 64; 131 break; 132 case 0x43: 133 /* SST25VF020 2 Mbit Serial Flash */ 134 sflash.numblocks = 64; 135 break; 136 case 4: 137 /* SST25WF040 4 Mbit Serial Flash */ 138 sflash.numblocks = 128; 139 break; 140 case 0x44: 141 /* SST25VF040 4 Mbit Serial Flash */ 142 sflash.numblocks = 128; 143 break; 144 case 0x8d: 145 /* SST25VF040B 4 Mbit Serial Flash */ 146 sflash.numblocks = 128; 147 break; 148 case 5: 149 /* SST25WF080 8 Mbit Serial Flash */ 150 sflash.numblocks = 256; 151 break; 152 case 0x8e: 153 /* SST25VF080B 8 Mbit Serial Flash */ 154 sflash.numblocks = 256; 155 break; 156 case 0x41: 157 /* SST25VF016 16 Mbit Serial Flash */ 158 sflash.numblocks = 512; 159 break; 160 case 0x4a: 161 /* SST25VF032 32 Mbit Serial Flash */ 162 sflash.numblocks = 1024; 163 break; 164 case 0x4b: 165 /* SST25VF064 64 Mbit Serial Flash */ 166 sflash.numblocks = 2048; 167 break; 168 } 169 break; 170 } 171 break; 172 173 case SFLASH_AT: 174 /* Probe for Atmel chips */ 175 name = "Atmel"; 176 sflash_cmd(osh, cc, SFLASH_AT_STATUS); 177 id = R_REG(osh, &cc->flashdata) & 0x3c; 178 switch (id) { 179 case 0xc: 180 /* Atmel AT45DB011 1Mbit Serial Flash */ 181 sflash.blocksize = 256; 182 sflash.numblocks = 512; 183 break; 184 case 0x14: 185 /* Atmel AT45DB021 2Mbit Serial Flash */ 186 sflash.blocksize = 256; 187 sflash.numblocks = 1024; 188 break; 189 case 0x1c: 190 /* Atmel AT45DB041 4Mbit Serial Flash */ 191 sflash.blocksize = 256; 192 sflash.numblocks = 2048; 193 break; 194 case 0x24: 195 /* Atmel AT45DB081 8Mbit Serial Flash */ 196 sflash.blocksize = 256; 197 sflash.numblocks = 4096; 198 break; 199 case 0x2c: 200 /* Atmel AT45DB161 16Mbit Serial Flash */ 201 sflash.blocksize = 512; 202 sflash.numblocks = 4096; 203 break; 204 case 0x34: 205 /* Atmel AT45DB321 32Mbit Serial Flash */ 206 sflash.blocksize = 512; 207 sflash.numblocks = 8192; 208 break; 209 case 0x3c: 210 /* Atmel AT45DB642 64Mbit Serial Flash */ 211 sflash.blocksize = 1024; 212 sflash.numblocks = 8192; 213 break; 214 } 215 break; 216 } 217 218 sflash.size = sflash.blocksize * sflash.numblocks; 219 220 if (firsttime) 221 printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n", 222 name, sflash.numblocks, sflash.blocksize / 1024, 223 sflash.size / (1024 * 1024)); 224 225 firsttime = FALSE; 226 return sflash.size ? &sflash : NULL; 227} 228 229/* Read len bytes starting at offset into buf. Returns number of bytes read. */ 230int 231sflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf) 232{ 233 uint8 *from, *to; 234 int cnt, i; 235 osl_t *osh; 236 237 ASSERT(sih); 238 239 if (!len) 240 return 0; 241 242 if ((offset + len) > sflash.size) 243 return -22; 244 245 if ((len >= 4) && (offset & 3)) 246 cnt = 4 - (offset & 3); 247 else if ((len >= 4) && ((uintptr)buf & 3)) 248 cnt = 4 - ((uintptr)buf & 3); 249 else 250 cnt = len; 251 252 osh = si_osh(sih); 253 254 if (sih->ccrev == 12) 255 from = (uint8 *)OSL_UNCACHED((void *)SI_FLASH2 + offset); 256 else 257 from = (uint8 *)OSL_CACHED((void *)SI_FLASH2 + offset); 258 to = (uint8 *)buf; 259 260 if (cnt < 4) { 261 for (i = 0; i < cnt; i ++) { 262 /* Cannot use R_REG because in bigendian that will 263 * xor the address and we don't want that here. 264 */ 265 *to = *from; 266 from ++; 267 to ++; 268 } 269 return cnt; 270 } 271 272 while (cnt >= 4) { 273 *(uint32 *)to = *(uint32 *)from; 274 from += 4; 275 to += 4; 276 cnt -= 4; 277 } 278 279 return (len - cnt); 280} 281 282/* Poll for command completion. Returns zero when complete. */ 283int 284sflash_poll(si_t *sih, chipcregs_t *cc, uint offset) 285{ 286 osl_t *osh; 287 288 ASSERT(sih); 289 290 osh = si_osh(sih); 291 292 if (offset >= sflash.size) 293 return -22; 294 295 switch (sflash.type) { 296 case SFLASH_ST: 297 /* Check for ST Write In Progress bit */ 298 sflash_cmd(osh, cc, SFLASH_ST_RDSR); 299 return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP; 300 case SFLASH_AT: 301 /* Check for Atmel Ready bit */ 302 sflash_cmd(osh, cc, SFLASH_AT_STATUS); 303 return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY); 304 } 305 306 return 0; 307} 308 309/* Write len bytes starting at offset into buf. Returns number of bytes 310 * written. Caller should poll for completion. 311 */ 312#define ST_RETRIES 3 313 314#ifdef IL_BIGENDIAN 315#ifdef BCMHND74K 316#define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 7)) 317#else /* !74K, bcm33xx */ 318#define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 3)) 319#endif /* BCMHND74K */ 320#else /* !IL_BIGENDIAN */ 321#define GET_BYTE(ptr) (*(ptr)) 322#endif /* IL_BIGENDIAN */ 323 324int 325sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) 326{ 327 struct sflash *sfl; 328 uint off = offset, len = length; 329 const uint8 *buf = buffer; 330 uint8 data; 331 int ret = 0, ntry = 0; 332 bool is4712b0; 333 uint32 page, byte, mask; 334 osl_t *osh; 335 336 ASSERT(sih); 337 338 osh = si_osh(sih); 339 340 if (!len) 341 return 0; 342 343 sfl = &sflash; 344 if ((off + len) > sfl->size) 345 return -22; 346 347 switch (sfl->type) { 348 case SFLASH_ST: 349 is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3); 350 /* Enable writes */ 351retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); 352 off = offset; 353 len = length; 354 buf = buffer; 355 ntry++; 356 if (is4712b0) { 357 mask = 1 << 14; 358 W_REG(osh, &cc->flashaddress, off); 359 data = GET_BYTE(buf); 360 buf++; 361 W_REG(osh, &cc->flashdata, data); 362 /* Set chip select */ 363 OR_REG(osh, &cc->gpioout, mask); 364 /* Issue a page program with the first byte */ 365 sflash_cmd(osh, cc, SFLASH_ST_PP); 366 ret = 1; 367 off++; 368 len--; 369 while (len > 0) { 370 if ((off & 255) == 0) { 371 /* Page boundary, drop cs and return */ 372 AND_REG(osh, &cc->gpioout, ~mask); 373 OSL_DELAY(1); 374 if (!sflash_poll(sih, cc, off)) { 375 /* Flash rejected command */ 376 if (ntry <= ST_RETRIES) 377 goto retry; 378 else 379 return -11; 380 } 381 return ret; 382 } else { 383 /* Write single byte */ 384 data = GET_BYTE(buf); 385 buf++; 386 sflash_cmd(osh, cc, data); 387 } 388 ret++; 389 off++; 390 len--; 391 } 392 /* All done, drop cs */ 393 AND_REG(osh, &cc->gpioout, ~mask); 394 OSL_DELAY(1); 395 if (!sflash_poll(sih, cc, off)) { 396 /* Flash rejected command */ 397 if (ntry <= ST_RETRIES) 398 goto retry; 399 else 400 return -12; 401 } 402 } else if (sih->ccrev >= 20) { 403 W_REG(osh, &cc->flashaddress, off); 404 data = GET_BYTE(buf); 405 buf++; 406 W_REG(osh, &cc->flashdata, data); 407 /* Issue a page program with CSA bit set */ 408 sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); 409 ret = 1; 410 off++; 411 len--; 412 while (len > 0) { 413 if ((off & 255) == 0) { 414 /* Page boundary, poll droping cs and return */ 415 W_REG(NULL, &cc->flashcontrol, 0); 416 OSL_DELAY(1); 417 if (sflash_poll(sih, cc, off) == 0) { 418 /* Flash rejected command */ 419 SFL_MSG(("sflash: pp rejected, ntry: %d," 420 " off: %d/%d, len: %d/%d, ret:" 421 "%d\n", ntry, off, offset, len, 422 length, ret)); 423 if (ntry <= ST_RETRIES) 424 goto retry; 425 else 426 return -11; 427 } 428 return ret; 429 } else { 430 /* Write single byte */ 431 data = GET_BYTE(buf); 432 buf++; 433 sflash_cmd(osh, cc, SFLASH_ST_CSA | data); 434 } 435 ret++; 436 off++; 437 len--; 438 } 439 /* All done, drop cs & poll */ 440 W_REG(NULL, &cc->flashcontrol, 0); 441 OSL_DELAY(1); 442 if (sflash_poll(sih, cc, off) == 0) { 443 /* Flash rejected command */ 444 SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d," 445 " len: %d/%d, ret: %d\n", 446 ntry, off, offset, len, length, ret)); 447 if (ntry <= ST_RETRIES) 448 goto retry; 449 else 450 return -12; 451 } 452 } else { 453 ret = 1; 454 W_REG(osh, &cc->flashaddress, off); 455 data = GET_BYTE(buf); 456 buf++; 457 W_REG(osh, &cc->flashdata, data); 458 /* Page program */ 459 sflash_cmd(osh, cc, SFLASH_ST_PP); 460 } 461 break; 462 case SFLASH_AT: 463 mask = sfl->blocksize - 1; 464 page = (off & ~mask) << 1; 465 byte = off & mask; 466 /* Read main memory page into buffer 1 */ 467 if (byte || (len < sfl->blocksize)) { 468 W_REG(osh, &cc->flashaddress, page); 469 sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); 470 /* 250 us for AT45DB321B */ 471 SPINWAIT(sflash_poll(sih, cc, off), 1000); 472 ASSERT(!sflash_poll(sih, cc, off)); 473 } 474 /* Write into buffer 1 */ 475 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { 476 W_REG(osh, &cc->flashaddress, byte++); 477 W_REG(osh, &cc->flashdata, *buf++); 478 sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); 479 } 480 /* Write buffer 1 into main memory page */ 481 W_REG(osh, &cc->flashaddress, page); 482 sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); 483 break; 484 } 485 486 return ret; 487} 488 489/* Erase a region. Returns number of bytes scheduled for erasure. 490 * Caller should poll for completion. 491 */ 492int 493sflash_erase(si_t *sih, chipcregs_t *cc, uint offset) 494{ 495 struct sflash *sfl; 496 osl_t *osh; 497 498 ASSERT(sih); 499 500 osh = si_osh(sih); 501 502 sfl = &sflash; 503 if (offset >= sfl->size) 504 return -22; 505 506 switch (sfl->type) { 507 case SFLASH_ST: 508 sflash_cmd(osh, cc, SFLASH_ST_WREN); 509 W_REG(osh, &cc->flashaddress, offset); 510 /* Newer flashes have "sub-sectors" which can be erased independently 511 * with a new command: ST_SSE. The ST_SE command erases 64KB just as 512 * before. 513 */ 514 sflash_cmd(osh, cc, (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : SFLASH_ST_SE); 515 return sfl->blocksize; 516 case SFLASH_AT: 517 W_REG(osh, &cc->flashaddress, offset << 1); 518 sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE); 519 return sfl->blocksize; 520 } 521 522 return 0; 523} 524 525/* 526 * writes the appropriate range of flash, a NULL buf simply erases 527 * the region of flash 528 */ 529int 530sflash_commit(si_t *sih, chipcregs_t *cc, uint offset, uint len, const uchar *buf) 531{ 532 struct sflash *sfl; 533 uchar *block = NULL, *cur_ptr, *blk_ptr; 534 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; 535 uint blk_offset, blk_len, copied; 536 int bytes, ret = 0; 537 osl_t *osh; 538 539 ASSERT(sih); 540 541 osh = si_osh(sih); 542 543 /* Check address range */ 544 if (len <= 0) 545 return 0; 546 547 sfl = &sflash; 548 if ((offset + len) > sfl->size) 549 return -1; 550 551 blocksize = sfl->blocksize; 552 mask = blocksize - 1; 553 554 /* Allocate a block of mem */ 555 if (!(block = MALLOC(osh, blocksize))) 556 return -1; 557 558 while (len) { 559 /* Align offset */ 560 cur_offset = offset & ~mask; 561 cur_length = blocksize; 562 cur_ptr = block; 563 564 remainder = blocksize - (offset & mask); 565 if (len < remainder) 566 cur_retlen = len; 567 else 568 cur_retlen = remainder; 569 570 /* buf == NULL means erase only */ 571 if (buf) { 572 /* Copy existing data into holding block if necessary */ 573 if ((offset & mask) || (len < blocksize)) { 574 blk_offset = cur_offset; 575 blk_len = cur_length; 576 blk_ptr = cur_ptr; 577 578 /* Copy entire block */ 579 while (blk_len) { 580 copied = sflash_read(sih, cc, blk_offset, blk_len, blk_ptr); 581 blk_offset += copied; 582 blk_len -= copied; 583 blk_ptr += copied; 584 } 585 } 586 587 /* Copy input data into holding block */ 588 memcpy(cur_ptr + (offset & mask), buf, cur_retlen); 589 } 590 591 /* Erase block */ 592 if ((ret = sflash_erase(sih, cc, (uint) cur_offset)) < 0) 593 goto done; 594 while (sflash_poll(sih, cc, (uint) cur_offset)); 595 596 /* buf == NULL means erase only */ 597 if (!buf) { 598 offset += cur_retlen; 599 len -= cur_retlen; 600 continue; 601 } 602 603 /* Write holding block */ 604 while (cur_length > 0) { 605 if ((bytes = sflash_write(sih, cc, 606 (uint) cur_offset, 607 (uint) cur_length, 608 (uchar *) cur_ptr)) < 0) { 609 ret = bytes; 610 goto done; 611 } 612 while (sflash_poll(sih, cc, (uint) cur_offset)); 613 cur_offset += bytes; 614 cur_length -= bytes; 615 cur_ptr += bytes; 616 } 617 618 offset += cur_retlen; 619 len -= cur_retlen; 620 buf += cur_retlen; 621 } 622 623 ret = len; 624done: 625 if (block) 626 MFREE(osh, block, blocksize); 627 return ret; 628} 629