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