sflash.c 11 KB

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