1/* 2 * Broadcom SiliconBackplane chipcommon serial flash interface 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: sflash.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#include <bcmutils.h> 18#include <sbutils.h> 19#include <sbconfig.h> 20#include <sbchipc.h> 21#include <bcmdevs.h> 22#include <sflash.h> 23 24#define SFL_MSG(args) 25 26/* Private global state */ 27static struct sflash sflash; 28 29/* Issue a serial flash command */ 30static INLINE void 31sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode) 32{ 33 W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode); 34 while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY); 35} 36 37/* Initialize serial flash access */ 38struct sflash * 39sflash_init(sb_t *sbh, chipcregs_t *cc) 40{ 41 uint32 id, id2; 42 osl_t *osh; 43 44 ASSERT(sbh); 45 46 osh = sb_osh(sbh); 47 48 bzero(&sflash, sizeof(sflash)); 49 50 sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK; 51 52 switch (sflash.type) { 53 case SFLASH_ST: 54 /* Probe for ST chips */ 55 sflash_cmd(osh, cc, SFLASH_ST_DP); 56 sflash_cmd(osh, cc, SFLASH_ST_RES); 57 id = R_REG(osh, &cc->flashdata); 58 switch (id) { 59 case 0x11: 60 /* ST M25P20 2 Mbit Serial Flash */ 61 sflash.blocksize = 64 * 1024; 62 sflash.numblocks = 4; 63 break; 64 case 0x12: 65 /* ST M25P40 4 Mbit Serial Flash */ 66 sflash.blocksize = 64 * 1024; 67 sflash.numblocks = 8; 68 break; 69 case 0x13: 70 /* ST M25P80 8 Mbit Serial Flash */ 71 sflash.blocksize = 64 * 1024; 72 sflash.numblocks = 16; 73 break; 74 case 0x14: 75 /* ST M25P16 16 Mbit Serial Flash */ 76 sflash.blocksize = 64 * 1024; 77 sflash.numblocks = 32; 78 break; 79 case 0x15: 80 /* ST M25P32 32 Mbit Serial Flash */ 81 sflash.blocksize = 64 * 1024; 82 sflash.numblocks = 64; 83 break; 84 case 0x16: 85 /* ST M25P64 64 Mbit Serial Flash */ 86 sflash.blocksize = 64 * 1024; 87 sflash.numblocks = 128; 88 break; 89 case 0xbf: 90 W_REG(osh, &cc->flashaddress, 1); 91 sflash_cmd(osh, cc, SFLASH_ST_RES); 92 id2 = R_REG(osh, &cc->flashdata); 93 if (id2 == 0x44) { 94 /* SST M25VF80 4 Mbit Serial Flash */ 95 sflash.blocksize = 64 * 1024; 96 sflash.numblocks = 8; 97 } 98 break; 99 } 100 break; 101 102 case SFLASH_AT: 103 /* Probe for Atmel chips */ 104 sflash_cmd(osh, cc, SFLASH_AT_STATUS); 105 id = R_REG(osh, &cc->flashdata) & 0x3c; 106 switch (id) { 107 case 0xc: 108 /* Atmel AT45DB011 1Mbit Serial Flash */ 109 sflash.blocksize = 256; 110 sflash.numblocks = 512; 111 break; 112 case 0x14: 113 /* Atmel AT45DB021 2Mbit Serial Flash */ 114 sflash.blocksize = 256; 115 sflash.numblocks = 1024; 116 break; 117 case 0x1c: 118 /* Atmel AT45DB041 4Mbit Serial Flash */ 119 sflash.blocksize = 256; 120 sflash.numblocks = 2048; 121 break; 122 case 0x24: 123 /* Atmel AT45DB081 8Mbit Serial Flash */ 124 sflash.blocksize = 256; 125 sflash.numblocks = 4096; 126 break; 127 case 0x2c: 128 /* Atmel AT45DB161 16Mbit Serial Flash */ 129 sflash.blocksize = 512; 130 sflash.numblocks = 4096; 131 break; 132 case 0x34: 133 /* Atmel AT45DB321 32Mbit Serial Flash */ 134 sflash.blocksize = 512; 135 sflash.numblocks = 8192; 136 break; 137 case 0x3c: 138 /* Atmel AT45DB642 64Mbit Serial Flash */ 139 sflash.blocksize = 1024; 140 sflash.numblocks = 8192; 141 break; 142 } 143 break; 144 } 145 146 sflash.size = sflash.blocksize * sflash.numblocks; 147 return sflash.size ? &sflash : NULL; 148} 149 150/* Read len bytes starting at offset into buf. Returns number of bytes read. */ 151int 152sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf) 153{ 154 uint8 *from, *to; 155 int cnt, i; 156 osl_t *osh; 157 158 ASSERT(sbh); 159 160 if (!len) 161 return 0; 162 163 if ((offset + len) > sflash.size) 164 return -22; 165 166 if ((len >= 4) && (offset & 3)) 167 cnt = 4 - (offset & 3); 168 else if ((len >= 4) && ((uintptr)buf & 3)) 169 cnt = 4 - ((uintptr)buf & 3); 170 else 171 cnt = len; 172 173 osh = sb_osh(sbh); 174 175 from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset); 176 to = (uint8 *)buf; 177 178 if (cnt < 4) { 179 for (i = 0; i < cnt; i ++) { 180 *to = R_REG(osh, from); 181 from ++; 182 to ++; 183 } 184 return cnt; 185 } 186 187 while (cnt >= 4) { 188 *(uint32 *)to = R_REG(osh, (uint32 *)from); 189 from += 4; 190 to += 4; 191 cnt -= 4; 192 } 193 194 return (len - cnt); 195} 196 197/* Poll for command completion. Returns zero when complete. */ 198int 199sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset) 200{ 201 osl_t *osh; 202 203 ASSERT(sbh); 204 205 osh = sb_osh(sbh); 206 207 if (offset >= sflash.size) 208 return -22; 209 210 switch (sflash.type) { 211 case SFLASH_ST: 212 /* Check for ST Write In Progress bit */ 213 sflash_cmd(osh, cc, SFLASH_ST_RDSR); 214 return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP; 215 case SFLASH_AT: 216 /* Check for Atmel Ready bit */ 217 sflash_cmd(osh, cc, SFLASH_AT_STATUS); 218 return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY); 219 } 220 221 return 0; 222} 223 224/* Write len bytes starting at offset into buf. Returns number of bytes 225 * written. Caller should poll for completion. 226 */ 227#define ST_RETRIES 3 228 229int 230sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) 231{ 232 struct sflash *sfl; 233 uint off = offset, len = length; 234 const uchar *buf = buffer; 235 int ret = 0, try = 0; 236 bool is4712b0; 237 uint32 page, byte, mask; 238 osl_t *osh; 239 240 ASSERT(sbh); 241 242 osh = sb_osh(sbh); 243 244 if (!len) 245 return 0; 246 247 sfl = &sflash; 248 if ((off + len) > sfl->size) 249 return -22; 250 251 switch (sfl->type) { 252 case SFLASH_ST: 253 is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3); 254 /* Enable writes */ 255retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); 256 off = offset; 257 len = length; 258 buf = buffer; 259 try++; 260 if (is4712b0) { 261 mask = 1 << 14; 262 W_REG(osh, &cc->flashaddress, off); 263 W_REG(osh, &cc->flashdata, *buf++); 264 /* Set chip select */ 265 OR_REG(osh, &cc->gpioout, mask); 266 /* Issue a page program with the first byte */ 267 sflash_cmd(osh, cc, SFLASH_ST_PP); 268 ret = 1; 269 off++; 270 len--; 271 while (len > 0) { 272 if ((off & 255) == 0) { 273 /* Page boundary, drop cs and return */ 274 AND_REG(osh, &cc->gpioout, ~mask); 275 OSL_DELAY(1); 276 if (!sflash_poll(sbh, cc, off)) { 277 /* Flash rejected command */ 278 if (try <= ST_RETRIES) 279 goto retry; 280 else 281 return -11; 282 } 283 return ret; 284 } else { 285 /* Write single byte */ 286 sflash_cmd(osh, cc, *buf++); 287 } 288 ret++; 289 off++; 290 len--; 291 } 292 /* All done, drop cs */ 293 AND_REG(osh, &cc->gpioout, ~mask); 294 OSL_DELAY(1); 295 if (!sflash_poll(sbh, cc, off)) { 296 /* Flash rejected command */ 297 if (try <= ST_RETRIES) 298 goto retry; 299 else 300 return -12; 301 } 302 } else if (sbh->ccrev >= 20) { 303 W_REG(NULL, &cc->flashaddress, off); 304 W_REG(NULL, &cc->flashdata, *buf++); 305 /* Issue a page program with CSA bit set */ 306 sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); 307 ret = 1; 308 off++; 309 len--; 310 while (len > 0) { 311 if ((off & 255) == 0) { 312 /* Page boundary, poll droping cs and return */ 313 W_REG(NULL, &cc->flashcontrol, 0); 314 OSL_DELAY(1); 315 if (sflash_poll(sbh, cc, off) == 0) { 316 /* Flash rejected command */ 317 SFL_MSG(("sflash: pp rejected, try: %d," 318 " off: %d/%d, len: %d/%d, ret:" 319 "%d\n", try, off, offset, len, 320 length, ret)); 321 if (try <= ST_RETRIES) 322 goto retry; 323 else 324 return -11; 325 } 326 return ret; 327 } else { 328 /* Write single byte */ 329 sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++); 330 } 331 ret++; 332 off++; 333 len--; 334 } 335 /* All done, drop cs & poll */ 336 W_REG(NULL, &cc->flashcontrol, 0); 337 OSL_DELAY(1); 338 if (sflash_poll(sbh, cc, off) == 0) { 339 /* Flash rejected command */ 340 SFL_MSG(("sflash: pp rejected, try: %d, off: %d/%d," 341 " len: %d/%d, ret: %d\n", 342 try, off, offset, len, length, ret)); 343 if (try <= ST_RETRIES) 344 goto retry; 345 else 346 return -12; 347 } 348 } else { 349 ret = 1; 350 W_REG(osh, &cc->flashaddress, off); 351 W_REG(osh, &cc->flashdata, *buf); 352 /* Page program */ 353 sflash_cmd(osh, cc, SFLASH_ST_PP); 354 } 355 break; 356 case SFLASH_AT: 357 mask = sfl->blocksize - 1; 358 page = (off & ~mask) << 1; 359 byte = off & mask; 360 /* Read main memory page into buffer 1 */ 361 if (byte || (len < sfl->blocksize)) { 362 W_REG(osh, &cc->flashaddress, page); 363 sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); 364 /* 250 us for AT45DB321B */ 365 SPINWAIT(sflash_poll(sbh, cc, off), 1000); 366 ASSERT(!sflash_poll(sbh, cc, off)); 367 } 368 /* Write into buffer 1 */ 369 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { 370 W_REG(osh, &cc->flashaddress, byte++); 371 W_REG(osh, &cc->flashdata, *buf++); 372 sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); 373 } 374 /* Write buffer 1 into main memory page */ 375 W_REG(osh, &cc->flashaddress, page); 376 sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); 377 break; 378 } 379 380 return ret; 381} 382 383/* Erase a region. Returns number of bytes scheduled for erasure. 384 * Caller should poll for completion. 385 */ 386int 387sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset) 388{ 389 struct sflash *sfl; 390 osl_t *osh; 391 392 ASSERT(sbh); 393 394 osh = sb_osh(sbh); 395 396 sfl = &sflash; 397 if (offset >= sfl->size) 398 return -22; 399 400 switch (sfl->type) { 401 case SFLASH_ST: 402 sflash_cmd(osh, cc, SFLASH_ST_WREN); 403 W_REG(osh, &cc->flashaddress, offset); 404 sflash_cmd(osh, cc, SFLASH_ST_SE); 405 return sfl->blocksize; 406 case SFLASH_AT: 407 W_REG(osh, &cc->flashaddress, offset << 1); 408 sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE); 409 return sfl->blocksize; 410 } 411 412 return 0; 413} 414 415/* 416 * writes the appropriate range of flash, a NULL buf simply erases 417 * the region of flash 418 */ 419int 420sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf) 421{ 422 struct sflash *sfl; 423 uchar *block = NULL, *cur_ptr, *blk_ptr; 424 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; 425 uint blk_offset, blk_len, copied; 426 int bytes, ret = 0; 427 osl_t *osh; 428 429 ASSERT(sbh); 430 431 osh = sb_osh(sbh); 432 433 /* Check address range */ 434 if (len <= 0) 435 return 0; 436 437 sfl = &sflash; 438 if ((offset + len) > sfl->size) 439 return -1; 440 441 blocksize = sfl->blocksize; 442 mask = blocksize - 1; 443 444 /* Allocate a block of mem */ 445 if (!(block = MALLOC(osh, blocksize))) 446 return -1; 447 448 while (len) { 449 /* Align offset */ 450 cur_offset = offset & ~mask; 451 cur_length = blocksize; 452 cur_ptr = block; 453 454 remainder = blocksize - (offset & mask); 455 if (len < remainder) 456 cur_retlen = len; 457 else 458 cur_retlen = remainder; 459 460 /* buf == NULL means erase only */ 461 if (buf) { 462 /* Copy existing data into holding block if necessary */ 463 if ((offset & mask) || (len < blocksize)) { 464 blk_offset = cur_offset; 465 blk_len = cur_length; 466 blk_ptr = cur_ptr; 467 468 /* Copy entire block */ 469 while (blk_len) { 470 copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr); 471 blk_offset += copied; 472 blk_len -= copied; 473 blk_ptr += copied; 474 } 475 } 476 477 /* Copy input data into holding block */ 478 memcpy(cur_ptr + (offset & mask), buf, cur_retlen); 479 } 480 481 /* Erase block */ 482 if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0) 483 goto done; 484 while (sflash_poll(sbh, cc, (uint) cur_offset)); 485 486 /* buf == NULL means erase only */ 487 if (!buf) { 488 offset += cur_retlen; 489 len -= cur_retlen; 490 continue; 491 } 492 493 /* Write holding block */ 494 while (cur_length > 0) { 495 if ((bytes = sflash_write(sbh, cc, 496 (uint) cur_offset, 497 (uint) cur_length, 498 (uchar *) cur_ptr)) < 0) { 499 ret = bytes; 500 goto done; 501 } 502 while (sflash_poll(sbh, cc, (uint) cur_offset)); 503 cur_offset += bytes; 504 cur_length -= bytes; 505 cur_ptr += bytes; 506 } 507 508 offset += cur_retlen; 509 len -= cur_retlen; 510 buf += cur_retlen; 511 } 512 513 ret = len; 514done: 515 if (block) 516 MFREE(osh, block, blocksize); 517 return ret; 518} 519