Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

SerialFlashChip.cpp 14KB

pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
pirms 9 gadiem
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory
  2. * https://github.com/PaulStoffregen/SerialFlash
  3. * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com
  4. *
  5. * Development of this library was funded by PJRC.COM, LLC by sales of Teensy.
  6. * Please support PJRC's efforts to develop open source software by purchasing
  7. * Teensy or other genuine PJRC products.
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice, development funding notice, and this permission
  17. * notice shall be included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. */
  27. #include "SerialFlash.h"
  28. #include "util/SerialFlash_directwrite.h"
  29. #define CSASSERT() DIRECT_WRITE_LOW(cspin_basereg, cspin_bitmask)
  30. #define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask)
  31. #define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0)
  32. #if defined(__arc__)
  33. // Use SPI1 on Arduino 101 (accesses chip already on the board)
  34. #define SPIPORT SPI1
  35. #elif 0
  36. // Add cases here, if you wish to use other SPI ports...
  37. #else
  38. // Otherwise, use the normal SPI port.
  39. #define SPIPORT SPI
  40. #endif
  41. uint16_t SerialFlashChip::dirindex = 0;
  42. uint8_t SerialFlashChip::flags = 0;
  43. uint8_t SerialFlashChip::busy = 0;
  44. static volatile IO_REG_TYPE *cspin_basereg;
  45. static IO_REG_TYPE cspin_bitmask;
  46. #define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address
  47. #define FLAG_STATUS_CMD70 0x02 // requires special busy flag check
  48. #define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands
  49. #define FLAG_MULTI_DIE 0x08 // multiple die, don't read cross 32M barrier
  50. #define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks
  51. #define FLAG_DIE_MASK 0xC0 // top 2 bits count during multi-die erase
  52. void SerialFlashChip::wait(void)
  53. {
  54. uint32_t status;
  55. //Serial.print("wait-");
  56. while (1) {
  57. SPIPORT.beginTransaction(SPICONFIG);
  58. CSASSERT();
  59. if (flags & FLAG_STATUS_CMD70) {
  60. // some Micron chips require this different
  61. // command to detect program and erase completion
  62. SPIPORT.transfer(0x70);
  63. status = SPIPORT.transfer(0);
  64. CSRELEASE();
  65. SPIPORT.endTransaction();
  66. //Serial.printf("b=%02x.", status & 0xFF);
  67. if ((status & 0x80)) break;
  68. } else {
  69. // all others work by simply reading the status reg
  70. SPIPORT.transfer(0x05);
  71. status = SPIPORT.transfer(0);
  72. CSRELEASE();
  73. SPIPORT.endTransaction();
  74. //Serial.printf("b=%02x.", status & 0xFF);
  75. if (!(status & 1)) break;
  76. }
  77. }
  78. busy = 0;
  79. //Serial.println();
  80. }
  81. void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len)
  82. {
  83. uint8_t *p = (uint8_t *)buf;
  84. uint8_t b, f, status, cmd;
  85. memset(p, 0, len);
  86. f = flags;
  87. SPIPORT.beginTransaction(SPICONFIG);
  88. b = busy;
  89. if (b) {
  90. // read status register ... chip may no longer be busy
  91. CSASSERT();
  92. if (flags & FLAG_STATUS_CMD70) {
  93. SPIPORT.transfer(0x70);
  94. status = SPIPORT.transfer(0);
  95. if ((status & 0x80)) b = 0;
  96. } else {
  97. SPIPORT.transfer(0x05);
  98. status = SPIPORT.transfer(0);
  99. if (!(status & 1)) b = 0;
  100. }
  101. CSRELEASE();
  102. if (b == 0) {
  103. // chip is no longer busy :-)
  104. busy = 0;
  105. } else if (b < 3) {
  106. // TODO: this may not work on Spansion chips
  107. // which apparently have 2 different suspend
  108. // commands, for program vs erase
  109. CSASSERT();
  110. SPIPORT.transfer(0x06); // write enable (Micron req'd)
  111. CSRELEASE();
  112. delayMicroseconds(1);
  113. cmd = 0x75; //Suspend program/erase for almost all chips
  114. // but Spansion just has to be different for program suspend!
  115. if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85;
  116. CSASSERT();
  117. SPIPORT.transfer(cmd); // Suspend command
  118. CSRELEASE();
  119. if (f & FLAG_STATUS_CMD70) {
  120. // Micron chips don't actually suspend until flags read
  121. CSASSERT();
  122. SPIPORT.transfer(0x70);
  123. do {
  124. status = SPIPORT.transfer(0);
  125. } while (!(status & 0x80));
  126. CSRELEASE();
  127. } else {
  128. CSASSERT();
  129. SPIPORT.transfer(0x05);
  130. do {
  131. status = SPIPORT.transfer(0);
  132. } while ((status & 0x01));
  133. CSRELEASE();
  134. }
  135. } else {
  136. // chip is busy with an operation that can not suspend
  137. SPIPORT.endTransaction(); // is this a good idea?
  138. wait(); // should we wait without ending
  139. b = 0; // the transaction??
  140. SPIPORT.beginTransaction(SPICONFIG);
  141. }
  142. }
  143. do {
  144. uint32_t rdlen = len;
  145. if (f & FLAG_MULTI_DIE) {
  146. if ((addr & 0xFE000000) != ((addr + len - 1) & 0xFE000000)) {
  147. rdlen = 0x2000000 - (addr & 0x1FFFFFF);
  148. }
  149. }
  150. CSASSERT();
  151. // TODO: FIFO optimize....
  152. if (f & FLAG_32BIT_ADDR) {
  153. SPIPORT.transfer(0x03);
  154. SPIPORT.transfer16(addr >> 16);
  155. SPIPORT.transfer16(addr);
  156. } else {
  157. SPIPORT.transfer16(0x0300 | ((addr >> 16) & 255));
  158. SPIPORT.transfer16(addr);
  159. }
  160. SPIPORT.transfer(p, rdlen);
  161. CSRELEASE();
  162. p += rdlen;
  163. addr += rdlen;
  164. len -= rdlen;
  165. } while (len > 0);
  166. if (b) {
  167. CSASSERT();
  168. SPIPORT.transfer(0x06); // write enable (Micron req'd)
  169. CSRELEASE();
  170. delayMicroseconds(1);
  171. cmd = 0x7A;
  172. if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A;
  173. CSASSERT();
  174. SPIPORT.transfer(cmd); // Resume program/erase
  175. CSRELEASE();
  176. }
  177. SPIPORT.endTransaction();
  178. }
  179. void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len)
  180. {
  181. const uint8_t *p = (const uint8_t *)buf;
  182. uint32_t max, pagelen;
  183. //Serial.printf("WR: addr %08X, len %d\n", addr, len);
  184. do {
  185. if (busy) wait();
  186. SPIPORT.beginTransaction(SPICONFIG);
  187. CSASSERT();
  188. // write enable command
  189. SPIPORT.transfer(0x06);
  190. CSRELEASE();
  191. max = 256 - (addr & 0xFF);
  192. pagelen = (len <= max) ? len : max;
  193. //Serial.printf("WR: addr %08X, pagelen %d\n", addr, pagelen);
  194. delayMicroseconds(1); // TODO: reduce this, but prefer safety first
  195. CSASSERT();
  196. if (flags & FLAG_32BIT_ADDR) {
  197. SPIPORT.transfer(0x02); // program page command
  198. SPIPORT.transfer16(addr >> 16);
  199. SPIPORT.transfer16(addr);
  200. } else {
  201. SPIPORT.transfer16(0x0200 | ((addr >> 16) & 255));
  202. SPIPORT.transfer16(addr);
  203. }
  204. addr += pagelen;
  205. len -= pagelen;
  206. do {
  207. SPIPORT.transfer(*p++);
  208. } while (--pagelen > 0);
  209. CSRELEASE();
  210. busy = 4;
  211. SPIPORT.endTransaction();
  212. } while (len > 0);
  213. }
  214. void SerialFlashChip::eraseAll()
  215. {
  216. if (busy) wait();
  217. uint8_t id[5];
  218. readID(id);
  219. //Serial.printf("ID: %02X %02X %02X\n", id[0], id[1], id[2]);
  220. if (id[0] == 0x20 && id[2] >= 0x20 && id[2] <= 0x22) {
  221. // Micron's multi-die chips require special die erase commands
  222. // N25Q512A 20 BA 20 2 dies 32 Mbyte/die 65 nm transitors
  223. // N25Q00AA 20 BA 21 4 dies 32 Mbyte/die 65 nm transitors
  224. // MT25QL02GC 20 BA 22 2 dies 128 Mbyte/die 45 nm transitors
  225. uint8_t die_count = 2;
  226. if (id[2] == 0x21) die_count = 4;
  227. uint8_t die_index = flags >> 6;
  228. //Serial.printf("Micron die erase %d\n", die_index);
  229. flags &= 0x3F;
  230. if (die_index >= die_count) return; // all dies erased :-)
  231. uint8_t die_size = 2; // in 16 Mbyte units
  232. if (id[2] == 0x22) die_size = 8;
  233. SPIPORT.beginTransaction(SPICONFIG);
  234. CSASSERT();
  235. SPIPORT.transfer(0x06); // write enable command
  236. CSRELEASE();
  237. delayMicroseconds(1);
  238. CSASSERT();
  239. // die erase command
  240. SPIPORT.transfer(0xC4);
  241. SPIPORT.transfer16((die_index * die_size) << 8);
  242. SPIPORT.transfer16(0x0000);
  243. CSRELEASE();
  244. //Serial.printf("Micron erase begin\n");
  245. flags |= (die_index + 1) << 6;
  246. } else {
  247. // All other chips support the bulk erase command
  248. SPIPORT.beginTransaction(SPICONFIG);
  249. CSASSERT();
  250. // write enable command
  251. SPIPORT.transfer(0x06);
  252. CSRELEASE();
  253. delayMicroseconds(1);
  254. CSASSERT();
  255. // bulk erase command
  256. SPIPORT.transfer(0xC7);
  257. CSRELEASE();
  258. SPIPORT.endTransaction();
  259. }
  260. busy = 3;
  261. }
  262. void SerialFlashChip::eraseBlock(uint32_t addr)
  263. {
  264. uint8_t f = flags;
  265. if (busy) wait();
  266. SPIPORT.beginTransaction(SPICONFIG);
  267. CSASSERT();
  268. SPIPORT.transfer(0x06); // write enable command
  269. CSRELEASE();
  270. delayMicroseconds(1);
  271. CSASSERT();
  272. if (f & FLAG_32BIT_ADDR) {
  273. SPIPORT.transfer(0xD8);
  274. SPIPORT.transfer16(addr >> 16);
  275. SPIPORT.transfer16(addr);
  276. } else {
  277. SPIPORT.transfer16(0xD800 | ((addr >> 16) & 255));
  278. SPIPORT.transfer16(addr);
  279. }
  280. CSRELEASE();
  281. SPIPORT.endTransaction();
  282. busy = 2;
  283. }
  284. bool SerialFlashChip::ready()
  285. {
  286. uint32_t status;
  287. if (!busy) return true;
  288. SPIPORT.beginTransaction(SPICONFIG);
  289. CSASSERT();
  290. if (flags & FLAG_STATUS_CMD70) {
  291. // some Micron chips require this different
  292. // command to detect program and erase completion
  293. SPIPORT.transfer(0x70);
  294. status = SPIPORT.transfer(0);
  295. CSRELEASE();
  296. SPIPORT.endTransaction();
  297. //Serial.printf("ready=%02x\n", status & 0xFF);
  298. if ((status & 0x80) == 0) return false;
  299. } else {
  300. // all others work by simply reading the status reg
  301. SPIPORT.transfer(0x05);
  302. status = SPIPORT.transfer(0);
  303. CSRELEASE();
  304. SPIPORT.endTransaction();
  305. //Serial.printf("ready=%02x\n", status & 0xFF);
  306. if ((status & 1)) return false;
  307. }
  308. busy = 0;
  309. if (flags & 0xC0) {
  310. // continue a multi-die erase
  311. eraseAll();
  312. return false;
  313. }
  314. return true;
  315. }
  316. #define ID0_WINBOND 0xEF
  317. #define ID0_SPANSION 0x01
  318. #define ID0_MICRON 0x20
  319. #define ID0_MACRONIX 0xC2
  320. #define ID0_SST 0xBF
  321. //#define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address
  322. //#define FLAG_STATUS_CMD70 0x02 // requires special busy flag check
  323. //#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands
  324. //#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks
  325. bool SerialFlashChip::begin(uint8_t pin)
  326. {
  327. uint8_t id[5];
  328. uint8_t f;
  329. uint32_t size;
  330. cspin_basereg = PIN_TO_BASEREG(pin);
  331. cspin_bitmask = PIN_TO_BITMASK(pin);
  332. SPIPORT.begin();
  333. pinMode(pin, OUTPUT);
  334. CSRELEASE();
  335. readID(id);
  336. f = 0;
  337. size = capacity(id);
  338. if (size > 16777216) {
  339. // more than 16 Mbyte requires 32 bit addresses
  340. f |= FLAG_32BIT_ADDR;
  341. SPIPORT.beginTransaction(SPICONFIG);
  342. if (id[0] == ID0_SPANSION) {
  343. // spansion uses MSB of bank register
  344. CSASSERT();
  345. SPIPORT.transfer16(0x1780); // bank register write
  346. CSRELEASE();
  347. } else {
  348. // micron & winbond & macronix use command
  349. CSASSERT();
  350. SPIPORT.transfer(0x06); // write enable
  351. CSRELEASE();
  352. delayMicroseconds(1);
  353. CSASSERT();
  354. SPIPORT.transfer(0xB7); // enter 4 byte addr mode
  355. CSRELEASE();
  356. }
  357. SPIPORT.endTransaction();
  358. if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE;
  359. }
  360. if (id[0] == ID0_SPANSION) {
  361. // Spansion has separate suspend commands
  362. f |= FLAG_DIFF_SUSPEND;
  363. if (!id[4]) {
  364. // Spansion chips with id[4] == 0 use 256K sectors
  365. f |= FLAG_256K_BLOCKS;
  366. }
  367. }
  368. if (id[0] == ID0_MICRON) {
  369. // Micron requires busy checks with a different command
  370. f |= FLAG_STATUS_CMD70; // TODO: all or just multi-die chips?
  371. }
  372. flags = f;
  373. readID(id);
  374. return true;
  375. }
  376. // chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992
  377. //
  378. void SerialFlashChip::sleep()
  379. {
  380. if (busy) wait();
  381. SPIPORT.beginTransaction(SPICONFIG);
  382. CSASSERT();
  383. SPIPORT.transfer(0xB9); // Deep power down command
  384. CSRELEASE();
  385. }
  386. void SerialFlashChip::wakeup()
  387. {
  388. SPIPORT.beginTransaction(SPICONFIG);
  389. CSASSERT();
  390. SPIPORT.transfer(0xAB); // Wake up from deep power down command
  391. CSRELEASE();
  392. }
  393. void SerialFlashChip::readID(uint8_t *buf)
  394. {
  395. if (busy) wait();
  396. SPIPORT.beginTransaction(SPICONFIG);
  397. CSASSERT();
  398. SPIPORT.transfer(0x9F);
  399. buf[0] = SPIPORT.transfer(0); // manufacturer ID
  400. buf[1] = SPIPORT.transfer(0); // memory type
  401. buf[2] = SPIPORT.transfer(0); // capacity
  402. if (buf[0] == ID0_SPANSION) {
  403. buf[3] = SPIPORT.transfer(0); // ID-CFI
  404. buf[4] = SPIPORT.transfer(0); // sector size
  405. }
  406. CSRELEASE();
  407. SPIPORT.endTransaction();
  408. //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]);
  409. }
  410. void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes
  411. {
  412. if (busy) wait();
  413. SPIPORT.beginTransaction(SPICONFIG);
  414. CSASSERT();
  415. SPIPORT.transfer(0x4B);
  416. SPIPORT.transfer16(0);
  417. SPIPORT.transfer16(0);
  418. for (int i=0; i<8; i++) {
  419. buf[i] = SPIPORT.transfer(0);
  420. }
  421. CSRELEASE();
  422. SPIPORT.endTransaction();
  423. // Serial.printf("Serial Number: %02X %02X %02X %02X %02X %02X %02X %02X\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
  424. }
  425. uint32_t SerialFlashChip::capacity(const uint8_t *id)
  426. {
  427. uint32_t n = 1048576; // unknown chips, default to 1 MByte
  428. if (id[2] >= 16 && id[2] <= 31) {
  429. n = 1ul << id[2];
  430. } else
  431. if (id[2] >= 32 && id[2] <= 37) {
  432. n = 1ul << (id[2] - 6);
  433. } else
  434. if ((id[0]==0 && id[1]==0 && id[2]==0) ||
  435. (id[0]==255 && id[1]==255 && id[2]==255)) {
  436. n = 0;
  437. }
  438. //Serial.printf("capacity %lu\n", n);
  439. return n;
  440. }
  441. uint32_t SerialFlashChip::blockSize()
  442. {
  443. // Spansion chips >= 512 mbit use 256K sectors
  444. if (flags & FLAG_256K_BLOCKS) return 262144;
  445. // everything else seems to have 64K sectors
  446. return 65536;
  447. }
  448. /*
  449. Chip Uniform Sector Erase
  450. 20/21 52 D8/DC
  451. ----- -- -----
  452. W25Q64CV 4 32 64
  453. W25Q128FV 4 32 64
  454. S25FL127S 64
  455. N25Q512A 4 64
  456. N25Q00AA 4 64
  457. S25FL512S 256
  458. SST26VF032 4
  459. */
  460. // size sector busy pgm/erase chip
  461. // Part Mbyte kbyte ID bytes cmd suspend erase
  462. // ---- ---- ----- -------- --- ------- -----
  463. // Winbond W25Q64CV 8 64 EF 40 17
  464. // Winbond W25Q128FV 16 64 EF 40 18 05 single 60 & C7
  465. // Winbond W25Q256FV 32 64 EF 40 19
  466. // Spansion S25FL064A 8 ? 01 02 16
  467. // Spansion S25FL127S 16 64 01 20 18 05
  468. // Spansion S25FL128P 16 64 01 20 18
  469. // Spansion S25FL256S 32 64 01 02 19 05 60 & C7
  470. // Spansion S25FL512S 64 256 01 02 20
  471. // Macronix MX25L12805D 16 ? C2 20 18
  472. // Macronix MX66L51235F 64 C2 20 1A
  473. // Numonyx M25P128 16 ? 20 20 18
  474. // Micron M25P80 1 ? 20 20 14
  475. // Micron N25Q128A 16 64 20 BA 18
  476. // Micron N25Q512A 64 ? 20 BA 20 70 single C4 x2
  477. // Micron N25Q00AA 128 64 20 BA 21 single C4 x4
  478. // Micron MT25QL02GC 256 64 20 BA 22 70 C4 x2
  479. // SST SST25WF010 1/8 ? BF 25 02
  480. // SST SST25WF020 1/4 ? BF 25 03
  481. // SST SST25WF040 1/2 ? BF 25 04
  482. // SST SST25VF016B 1 ? BF 25 41
  483. // SST26VF016 ? BF 26 01
  484. // SST26VF032 ? BF 26 02
  485. // SST25VF032 4 64 BF 25 4A
  486. // SST26VF064 8 ? BF 26 43
  487. // LE25U40CMC 1/2 64 62 06 13
  488. SerialFlashChip SerialFlash;