PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

531 lines
15KB

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