spi_flash_at45d.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. /*
  2. * Copyright (C) 2008-2011 by egnite GmbH
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. /*!
  35. * \file dev/spi_flash_at45d.c
  36. * \brief Low level SPI Flash routines for Adesto/Atmel AT45D chips.
  37. *
  38. * \verbatim
  39. * $Id$
  40. * \endverbatim
  41. */
  42. #include <cfg/memory.h>
  43. #include <sys/nutdebug.h>
  44. #include <dev/spi_node_at45d.h>
  45. #include <stdlib.h>
  46. #include <string.h>
  47. /*!
  48. * \addtogroup xgSpiFlashAt45d
  49. */
  50. /*@{*/
  51. #ifndef FLASH_BUFFERS_AT45D
  52. #define FLASH_BUFFERS_AT45D 2
  53. #endif
  54. #ifdef AT45D_CRC_PAGE
  55. #define AT45D_CRC_SIZE 2
  56. #else
  57. #define AT45D_CRC_SIZE 0
  58. #endif
  59. /*! \brief RAM buffer dirty flag. */
  60. #define FLASH_BUFFER_DIRTY 0x0001
  61. /*!
  62. * \brief Internal information structure.
  63. *
  64. * This structure is mainly used to keep track of the serial flash's page buffers.
  65. */
  66. typedef struct _AT45D_FLASH {
  67. /*! \brief Page number.
  68. *
  69. * Contains the number of the pages currently loaded.
  70. */
  71. sf_unit_t dxb_page[FLASH_BUFFERS_AT45D];
  72. /*! \brief Attribute flags.
  73. *
  74. * Contains FLASH_BUFFER_DIRTY, if the buffer has been modified and not
  75. * yet written back to flash memory.
  76. */
  77. uint_fast8_t flags[FLASH_BUFFERS_AT45D];
  78. /*! \brief Lock count.
  79. *
  80. * A buffer must not be written back to flash memory when this counter
  81. * is not equal to zero.
  82. */
  83. int dxb_locks[FLASH_BUFFERS_AT45D];
  84. /*! \brief Unlock event queue.
  85. *
  86. * Queued threads waiting for an unlocked buffer.
  87. */
  88. HANDLE dxb_lque;
  89. /*! \brief Page address shift value.
  90. *
  91. * Number of bytes to shift the page address for the Dataflash command
  92. * parameter.
  93. */
  94. uint_fast8_t dxb_pshft;
  95. /*! \brief The page buffers. */
  96. uint8_t *dxb_pbuf[FLASH_BUFFERS_AT45D];
  97. } AT45D_FLASH;
  98. #ifdef AT45D_CRC_PAGE
  99. /*
  100. * http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
  101. */
  102. static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
  103. {
  104. data ^= (uint8_t) (crc);
  105. data ^= data << 4;
  106. return ((((uint16_t) data << 8) | (crc >> 8)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
  107. }
  108. /*!
  109. * \brief Execute serial flash CRC.
  110. *
  111. * \param at Specifies the device.
  112. * \param b Number of the buffer, either 0 or 1.
  113. * \param crc16 Pointer to the variable that receives the checksum.
  114. * \param xlen Number of byte to use for the calculation.
  115. */
  116. static int CalculateChecksum(AT45D_FLASH * at, int_fast8_t b, uint16_t * crc16, int xlen)
  117. {
  118. int rc;
  119. NUTSPIBUS *bus;
  120. /* Sanity checks. */
  121. NUTASSERT(at != NULL);
  122. NUTASSERT(crc16 != NULL);
  123. *crc16 = 0xffff;
  124. if (xlen) {
  125. int i;
  126. uint8_t c;
  127. uint_fast8_t ne = 0;
  128. for (i = 0; i < xlen; i++) {
  129. c = at->dxb_pbuf[i];
  130. if (ne || c != 0xff) {
  131. ne = 1;
  132. *crc16 = crc_ccitt_update(*crc16, c);
  133. }
  134. }
  135. if (*crc16 == 0) {
  136. *crc16 = 0xffff;
  137. }
  138. }
  139. }
  140. #endif /* AT45D_CRC_PAGE */
  141. /*!
  142. * \brief Flash the given buffer.
  143. *
  144. * \param sfi Specifies the serial flash interface.
  145. * \param b Number of the buffer, either 0 or 1.
  146. *
  147. * \return 0 on success or -1 in case of an error.
  148. */
  149. static int At45dFlashSaveUnit(NUTSERIALFLASH * sfi, int_fast8_t b)
  150. {
  151. int rc;
  152. AT45D_FLASH *at;
  153. at = (AT45D_FLASH *) sfi->sf_info;
  154. #ifdef AT45D_CRC_PAGE
  155. /* Store a 16 bit CRC at the end of the page, if configured. */
  156. {
  157. uint16_t crc16;
  158. CalculateChecksum(at, b, &crc16, sfi->sf_unit_size);
  159. memcpy(at->dxb_pbuf[b] + sfi->sf_unit_size, &crc16, sizeof(crc16));
  160. }
  161. #endif
  162. rc = At45dNodeLock(sfi->sf_node);
  163. if (rc == 0) {
  164. rc = At45dNodeTransfer(sfi->sf_node, DFCMD_BUF2_WRITE, 0, 4, at->dxb_pbuf[b], NULL, sfi->sf_unit_size + AT45D_CRC_SIZE);
  165. if (rc == 0) {
  166. uint32_t pga;
  167. pga = at->dxb_page[b];
  168. pga <<= at->dxb_pshft;
  169. rc = At45dNodeCommand(sfi->sf_node, DFCMD_BUF2_FLASH_NE, pga, 4);
  170. if (rc == 0) {
  171. rc = At45dNodeWaitReady(sfi->sf_node, AT45_WRITE_POLLS, 0);
  172. }
  173. At45dNodeUnlock(sfi->sf_node);
  174. if (rc == 0) {
  175. at->flags[b] &= ~FLASH_BUFFER_DIRTY;
  176. }
  177. }
  178. }
  179. return rc;
  180. }
  181. /*!
  182. * \brief Release the specified buffer.
  183. *
  184. * \param sfi Specifies the serial flash interface.
  185. * \param b Number of the buffer.
  186. */
  187. static void At45dFlashRelease(NUTSERIALFLASH * sfi, int b)
  188. {
  189. AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
  190. at->dxb_locks[b]--;
  191. NutEventPost(&at->dxb_lque);
  192. }
  193. /*!
  194. * \brief Load a given page into an available buffer.
  195. *
  196. * If all buffers are locked, the current thread will be suspended until
  197. * a buffer becomes available again.
  198. *
  199. * If all buffers are marked dirty, the routine will write back one of
  200. * the buffers back to flash.
  201. *
  202. * \param sfi Specifies the serial flash interface.
  203. * \param pgn Number of the page to load.
  204. * \param lock If not 0, then the RAM buffer will be locked and marked
  205. * dirty. To unlock the buffer, call At45dFlashRelease().
  206. * To clean the buffer, call SpiAt45dFlashCommit().
  207. *
  208. * \return Number of the buffer.
  209. */
  210. static int_fast8_t At45dFlashLoadUnit(NUTSERIALFLASH * sfi, sf_unit_t pgn, int_fast8_t lock)
  211. {
  212. static int_fast8_t bnxt = 0;
  213. int_fast8_t b;
  214. AT45D_FLASH *at;
  215. at = (AT45D_FLASH *) sfi->sf_info;
  216. for (;;) {
  217. /*
  218. * Check, if the page is already loaded.
  219. */
  220. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  221. if (at->dxb_page[b] == pgn) {
  222. if (lock) {
  223. at->dxb_locks[b]++;
  224. at->flags[b] |= FLASH_BUFFER_DIRTY;
  225. }
  226. return b;
  227. }
  228. }
  229. /*
  230. * Search for a clean unlocked buffer and load the page.
  231. */
  232. b = bnxt;
  233. do {
  234. if (at->dxb_locks[b] == 0 && (at->flags[b] & FLASH_BUFFER_DIRTY) == 0) {
  235. break;
  236. }
  237. if (++b >= FLASH_BUFFERS_AT45D) {
  238. b = 0;
  239. }
  240. if (b == bnxt) {
  241. b = -1;
  242. }
  243. } while (b >= 0);
  244. if (b >= 0) {
  245. int rc;
  246. uint32_t pga = pgn;
  247. if (At45dNodeLock(sfi->sf_node) == 0) {
  248. pga <<= at->dxb_pshft;
  249. if (lock) {
  250. at->dxb_locks[b]++;
  251. }
  252. rc = At45dNodeTransfer(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, NULL, at->dxb_pbuf[b],
  253. sfi->sf_unit_size + AT45D_CRC_SIZE);
  254. At45dNodeUnlock(sfi->sf_node);
  255. if (rc == 0) {
  256. at->dxb_page[b] = pgn;
  257. if (lock) {
  258. at->flags[b] |= FLASH_BUFFER_DIRTY;
  259. }
  260. if (++bnxt >= FLASH_BUFFERS_AT45D) {
  261. bnxt = 0;
  262. }
  263. return b;
  264. }
  265. if (lock) {
  266. At45dFlashRelease(sfi, b);
  267. }
  268. }
  269. return -1;
  270. }
  271. /*
  272. * No clean buffer. If not locked, save one of them.
  273. */
  274. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  275. if (at->dxb_locks[b] == 0 && (at->flags[b] & FLASH_BUFFER_DIRTY) == FLASH_BUFFER_DIRTY) {
  276. if (At45dFlashSaveUnit(sfi, b)) {
  277. return -1;
  278. }
  279. break;
  280. }
  281. }
  282. /*
  283. * All buffers are locked. Wait for an unlock event.
  284. */
  285. if (b >= FLASH_BUFFERS_AT45D) {
  286. NutEventWait(&at->dxb_lque, 0);
  287. }
  288. }
  289. }
  290. /*!
  291. * \brief Initialize the serial flash interface.
  292. *
  293. * \param sfi Specifies the serial flash interface.
  294. *
  295. * \return 0 on success or -1 in case of an error.
  296. */
  297. static int SpiAt45dFlashInit(NUTSERIALFLASH * sfi)
  298. {
  299. int_fast8_t b;
  300. AT45D_INFO *df;
  301. AT45D_FLASH *at;
  302. /* Sanity checks. */
  303. NUTASSERT(sfi != NULL);
  304. NUTASSERT(sfi->sf_node != NULL);
  305. df = At45dNodeProbe(sfi->sf_node);
  306. if (df == NULL) {
  307. /* Unknown DataFlash type. */
  308. return -1;
  309. }
  310. /* Known DataFlash type. */
  311. at = calloc(1, sizeof(AT45D_FLASH));
  312. if (at == NULL) {
  313. return -1;
  314. }
  315. at->dxb_pshft = df->at45d_pshft;
  316. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  317. at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
  318. at->dxb_pbuf[b] = malloc(df->at45d_psize);
  319. }
  320. sfi->sf_info = (void *) at;
  321. sfi->sf_units = (sf_unit_t) df->at45d_pages;
  322. sfi->sf_unit_size = df->at45d_psize - AT45D_CRC_SIZE;
  323. return 0;
  324. }
  325. /*!
  326. * \brief Release the interface.
  327. *
  328. * \param sfi Specifies the serial flash interface.
  329. */
  330. static void SpiAt45dFlashExit(NUTSERIALFLASH * sfi)
  331. {
  332. int_fast8_t b;
  333. AT45D_FLASH *at;
  334. NUTASSERT(sfi != NULL);
  335. NUTASSERT(sfi->sf_info != NULL);
  336. at = (AT45D_FLASH *) sfi->sf_info;
  337. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  338. free(at->dxb_pbuf[b]);
  339. }
  340. free(sfi->sf_info);
  341. }
  342. /*!
  343. * \brief Verify CRC of consecutive pages.
  344. *
  345. * \param sfi Specifies the serial flash interface.
  346. * \param pgn First page to verify.
  347. * \param cnt Number of consecutive pages to verify.
  348. *
  349. * \return 0 on success or if CRC checking is disabled (compile option).
  350. * In case of an error -1 is returned.
  351. */
  352. static int SpiAt45dFlashCheck(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
  353. {
  354. int rc = 0;
  355. #ifdef AT45D_CRC_PAGE
  356. AT45D_FLASH *at;
  357. int_fast8_t b;
  358. uint16_t crc16 = 0;
  359. /* Sanity checks. */
  360. NUTASSERT(sfi != NULL);
  361. NUTASSERT(sfi->sf_info != NULL);
  362. NUTASSERT(pgn + cnt <= sfi->sf_units);
  363. NUTASSERT(cnt >= 0);
  364. at = (AT45D_FLASH *) sfi->sf_info;
  365. while (cnt--) {
  366. b = At45dFlashLoadUnit(sfi, pgn + cnt, 0);
  367. if (b >= 0) {
  368. CalculateChecksum(at, b, &crc16, sfi->sf_unit_size + AT45D_CRC_SIZE);
  369. }
  370. if (crc16 != 0xFFFF) {
  371. rc = -1;
  372. break;
  373. }
  374. }
  375. #endif
  376. return rc;
  377. }
  378. /*!
  379. * \brief Read data from a given page.
  380. *
  381. * \param sfi Specifies the serial flash interface.
  382. * \param pgn Page to read from.
  383. * \param off Read offset into the page. If negative, the offset counts
  384. * from the end of the page.
  385. * \param data Pointer to the buffer that receives the data.
  386. * \param len Number of data bytes to read.
  387. *
  388. * \return 0 on success or -1 in case of an error.
  389. */
  390. static int SpiAt45dFlashRead(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, void *data, int len)
  391. {
  392. int rc = 0;
  393. /* Sanity checks. */
  394. NUTASSERT(sfi != NULL);
  395. NUTASSERT(sfi->sf_info != NULL);
  396. NUTASSERT(pgn < sfi->sf_units);
  397. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  398. NUTASSERT(data != NULL);
  399. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  400. if (len) {
  401. int_fast8_t b;
  402. AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
  403. /* Normalize the offset. */
  404. if (off < 0) {
  405. off += sfi->sf_unit_size;
  406. }
  407. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  408. b = At45dFlashLoadUnit(sfi, pgn, 0);
  409. if (b < 0) {
  410. rc = -1;
  411. } else {
  412. memcpy(data, at->dxb_pbuf[b] + off, len);
  413. }
  414. }
  415. return rc;
  416. }
  417. /*!
  418. * \brief Compare data with the contents of a given page.
  419. *
  420. * \param sfi Specifies the serial flash interface.
  421. * \param pgn Page to compare.
  422. * \param off Offset into the page. If negative, the offset counts from
  423. * the end of the page.
  424. * \param data Pointer to the data to compare.
  425. * \param len Number of data bytes to compare.
  426. *
  427. * \return 0 on success or -1 if the contents differs or in case of an error.
  428. */
  429. static int SpiAt45dFlashCompare(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
  430. {
  431. int rc = 0;
  432. /* Sanity checks. */
  433. NUTASSERT(sfi != NULL);
  434. NUTASSERT(sfi->sf_info != NULL);
  435. NUTASSERT(pgn < sfi->sf_units);
  436. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  437. NUTASSERT(data != NULL);
  438. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  439. if (len) {
  440. int_fast8_t b;
  441. AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
  442. /* Normalize the offset. */
  443. if (off < 0) {
  444. off += sfi->sf_unit_size;
  445. }
  446. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  447. b = At45dFlashLoadUnit(sfi, pgn, 0);
  448. if (b < 0 || memcmp(at->dxb_pbuf[b] + off, data, len)) {
  449. rc = -1;
  450. }
  451. }
  452. return rc;
  453. }
  454. /*!
  455. * \brief Return the number of bytes used in a given page.
  456. *
  457. * Simply counts the number of trailing 0xFF bytes and may fail when used
  458. * with binary data.
  459. *
  460. * \param sfi Specifies the serial flash interface.
  461. * \param pgn Page to check.
  462. * \param skip Number of bytes to ingore. May be a positive or negative
  463. * value, to ingnore bytes at the beginning or at the end, resp.
  464. *
  465. * \return 0 on success or -1 if the contents differs or in case of an error.
  466. */
  467. static int SpiAt45dFlashUsed(NUTSERIALFLASH * sfi, sf_unit_t pgn, int skip)
  468. {
  469. int rc;
  470. int len;
  471. AT45D_FLASH *at;
  472. int_fast8_t b;
  473. /* Sanity checks. */
  474. NUTASSERT(sfi != NULL);
  475. NUTASSERT(pgn < sfi->sf_units);
  476. NUTASSERT(skip <= (int) sfi->sf_unit_size);
  477. at = (AT45D_FLASH *) sfi->sf_info;
  478. /* Determine length and offset. */
  479. len = (int) sfi->sf_unit_size;
  480. if (skip < 0) {
  481. len += skip;
  482. skip = 0;
  483. } else {
  484. len -= skip;
  485. }
  486. b = At45dFlashLoadUnit(sfi, pgn, 0);
  487. if (b < 0) {
  488. rc = -1;
  489. } else {
  490. rc = 0;
  491. if (len) {
  492. uint8_t *cp = at->dxb_pbuf[b] + skip;
  493. int i;
  494. for (i = 0; i < len; i++) {
  495. if (*cp++ != 0xff) {
  496. rc = i + 1;
  497. }
  498. }
  499. }
  500. }
  501. return rc;
  502. }
  503. /*!
  504. * \brief Write data to a given page.
  505. *
  506. * Loads the page into a buffer and replaces the contents with the given
  507. * data. The buffer will be marked dirty, but not written back. See
  508. * SpiAt45dFlashCommit().
  509. *
  510. * \param sfi Specifies the serial flash interface.
  511. * \param pgn Page to write to.
  512. * \param off Offset into the page, where the new data should be placed.
  513. * If negative, the offset counts from the end of the page.
  514. * \param data Pointer to the data to write.
  515. * \param len Number of data bytes to write.
  516. *
  517. * \return 0 on success or -1 in case of an error.
  518. */
  519. static int SpiAt45dFlashWrite(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
  520. {
  521. int rc = 0;
  522. /* Sanity checks. */
  523. NUTASSERT(sfi != NULL);
  524. NUTASSERT(pgn < sfi->sf_units);
  525. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  526. NUTASSERT(len == 0 || data != NULL);
  527. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  528. if (len) {
  529. int_fast8_t b;
  530. AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
  531. /* Normalize the offset. */
  532. if (off < 0) {
  533. off += sfi->sf_unit_size;
  534. }
  535. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  536. /* Load the page. */
  537. b = At45dFlashLoadUnit(sfi, pgn, 1);
  538. if (b < 0) {
  539. return -1;
  540. }
  541. /* Transfer the data and release the page. */
  542. memcpy(at->dxb_pbuf[b] + off, data, len);
  543. At45dFlashRelease(sfi, b);
  544. }
  545. return rc;
  546. }
  547. /*!
  548. * \brief Copy page.
  549. *
  550. * Loads the source page into a buffer und designates the buffer to the
  551. * destination page. The buffer will be marked dirty, but not written back.
  552. * See SpiAt45dFlashCommit().
  553. *
  554. * \param sfi Specifies the serial flash interface.
  555. * \param spg Source page to copy from.
  556. * \param dpg Destination page to copy to.
  557. *
  558. * \return 0 on success or -1 in case of an error.
  559. */
  560. static int SpiAt45dFlashCopy(NUTSERIALFLASH * sfi, sf_unit_t spg, sf_unit_t dpg)
  561. {
  562. int_fast8_t b;
  563. /* Sanity checks. */
  564. NUTASSERT(sfi != NULL);
  565. NUTASSERT(sfi->sf_info != NULL);
  566. NUTASSERT(spg < sfi->sf_units);
  567. NUTASSERT(dpg < sfi->sf_units);
  568. /* If source and destination page numbers are equal, just load it. */
  569. if (spg == dpg) {
  570. b = At45dFlashLoadUnit(sfi, spg, 1);
  571. } else {
  572. AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
  573. /* Invalidate any buffered destination. */
  574. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  575. if (at->dxb_page[b] == dpg) {
  576. /* But do not touch locked pages. */
  577. if (at->dxb_locks[b]) {
  578. return -1;
  579. }
  580. at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
  581. }
  582. }
  583. /* Try to load the source page and make it the destination page. */
  584. b = At45dFlashLoadUnit(sfi, spg, 1);
  585. if (b >= 0) {
  586. at->dxb_page[b] = dpg;
  587. }
  588. }
  589. if (b >= 0) {
  590. At45dFlashRelease(sfi, b);
  591. return 0;
  592. }
  593. return -1;
  594. }
  595. /*!
  596. * \brief Commit page.
  597. *
  598. * If the contents of the given page is in one of the internal RAM
  599. * buffers and if the contents had been changed since the last page
  600. * load, then the buffer is written back to the flash.
  601. *
  602. * \param sfi Specifies the serial flash interface.
  603. * \param pgn Page to commit.
  604. *
  605. * \return 0 on success or -1 in case of an error.
  606. */
  607. static int SpiAt45dFlashCommit(NUTSERIALFLASH * sfi, sf_unit_t pgn)
  608. {
  609. int rc = 0;
  610. int_fast8_t b;
  611. AT45D_FLASH *at;
  612. /* Sanity checks. */
  613. NUTASSERT(sfi != NULL);
  614. NUTASSERT(pgn < sfi->sf_units);
  615. at = (AT45D_FLASH *) sfi->sf_info;
  616. for (b = 0; b < FLASH_BUFFERS_AT45D && at->dxb_page[b] != pgn; b++);
  617. if (b < FLASH_BUFFERS_AT45D && (at->flags[b] & FLASH_BUFFER_DIRTY) == FLASH_BUFFER_DIRTY) {
  618. rc = At45dFlashSaveUnit(sfi, b);
  619. }
  620. return rc;
  621. }
  622. /*!
  623. * \brief Erase consecutive pages.
  624. *
  625. * \param sfi Specifies the serial flash interface.
  626. * \param pgn First page to erase.
  627. * \param cnt Number of consecutive pages to erase.
  628. *
  629. * \return 0 on success or -1 in case of an error.
  630. */
  631. static int SpiAt45dFlashErase(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
  632. {
  633. int rc = 0;
  634. int_fast8_t b;
  635. AT45D_FLASH *at;
  636. /* Sanity checks. */
  637. NUTASSERT(sfi != NULL);
  638. NUTASSERT(pgn + cnt <= sfi->sf_units);
  639. NUTASSERT(cnt >= 0);
  640. at = (AT45D_FLASH *) sfi->sf_info;
  641. /* Invalidate any buffered page. */
  642. for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
  643. if (at->dxb_page[b] >= pgn && at->dxb_page[b] < pgn + cnt) {
  644. at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
  645. at->flags[b] &= ~FLASH_BUFFER_DIRTY;
  646. }
  647. }
  648. while (rc == 0 && cnt--) {
  649. uint32_t pga = pgn + cnt;
  650. pga <<= at->dxb_pshft;
  651. rc = At45dNodeLock(sfi->sf_node);
  652. if (rc == 0) {
  653. rc = At45dNodeCommand(sfi->sf_node, DFCMD_PAGE_ERASE, pga, 4);
  654. if (rc == 0) {
  655. At45dNodeWaitReady(sfi->sf_node, AT45_WRITE_POLLS, 0);
  656. }
  657. At45dNodeUnlock(sfi->sf_node);
  658. }
  659. }
  660. return rc;
  661. }
  662. #ifndef FLASH_MOUNT_OFFSET_AT45D0
  663. #ifdef MOUNT_OFFSET_AT45D0
  664. #define FLASH_MOUNT_OFFSET_AT45D0 MOUNT_OFFSET_AT45D0
  665. #else
  666. #define FLASH_MOUNT_OFFSET_AT45D0 0
  667. #endif
  668. #endif
  669. #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D0
  670. #ifdef MOUNT_TOP_RESERVE_AT45D0
  671. #define FLASH_MOUNT_TOP_RESERVE_AT45D0 MOUNT_TOP_RESERVE_AT45D0
  672. #else
  673. #define FLASH_MOUNT_TOP_RESERVE_AT45D0 1
  674. #endif
  675. #endif
  676. /*!
  677. * \brief First AT45D DataFlash interface implementation structure.
  678. */
  679. NUTSERIALFLASH flashAt45d0 = {
  680. &nodeAt45d0, /*!< \brief Pointer to the SPI node structure, sf_node. */
  681. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  682. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  683. 0, /*!< \brief Total number of units, sf_units. */
  684. FLASH_MOUNT_OFFSET_AT45D0, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  685. FLASH_MOUNT_TOP_RESERVE_AT45D0, /*!< \brief Reserved units at the top, sf_rsvtop. */
  686. SpiAt45dFlashInit, /*!< \brief Flash device initialization function, sf_init. */
  687. SpiAt45dFlashExit, /*!< \brief Flash device release function, sf_exit. */
  688. SpiAt45dFlashCheck, /*!< \brief Unit validation function, sf_check. */
  689. SpiAt45dFlashRead, /*!< \brief Data read function, sf_read. */
  690. SpiAt45dFlashCompare, /*!< \brief Data compare function, sf_compare. */
  691. SpiAt45dFlashUsed, /*!< \brief Unit usage query function, sf_used. */
  692. SpiAt45dFlashWrite, /*!< \brief Data write function, sf_write. */
  693. SpiAt45dFlashCopy, /*!< \brief Unit copy function, sf_copy. */
  694. SpiAt45dFlashCommit, /*!< \brief Unit commit function, sf_commit. */
  695. SpiAt45dFlashErase /*!< \brief Unit erase function, sf_erase. */
  696. };
  697. #ifndef FLASH_MOUNT_OFFSET_AT45D1
  698. #ifdef MOUNT_OFFSET_AT45D1
  699. #define FLASH_MOUNT_OFFSET_AT45D1 MOUNT_OFFSET_AT45D1
  700. #else
  701. #define FLASH_MOUNT_OFFSET_AT45D1 0
  702. #endif
  703. #endif
  704. #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D1
  705. #ifdef MOUNT_TOP_RESERVE_AT45D1
  706. #define FLASH_MOUNT_TOP_RESERVE_AT45D1 MOUNT_TOP_RESERVE_AT45D1
  707. #else
  708. #define FLASH_MOUNT_TOP_RESERVE_AT45D1 1
  709. #endif
  710. #endif
  711. /*!
  712. * \brief Second AT45D DataFlash interface implementation structure.
  713. */
  714. NUTSERIALFLASH flashAt45d1 = {
  715. &nodeAt45d1, /*!< \brief Pointer to the SPI node structure, sf_node. */
  716. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  717. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  718. 0, /*!< \brief Total number of units, sf_units. */
  719. FLASH_MOUNT_OFFSET_AT45D1, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  720. FLASH_MOUNT_TOP_RESERVE_AT45D1, /*!< \brief Reserved units at the top, sf_rsvtop. */
  721. SpiAt45dFlashInit, /*!< \brief Flash device initialization function, sf_init. */
  722. SpiAt45dFlashExit, /*!< \brief Flash device release function, sf_exit. */
  723. SpiAt45dFlashCheck, /*!< \brief Unit validation function, sf_check. */
  724. SpiAt45dFlashRead, /*!< \brief Data read function, sf_read. */
  725. SpiAt45dFlashCompare, /*!< \brief Data compare function, sf_compare. */
  726. SpiAt45dFlashUsed, /*!< \brief Unit usage query function, sf_used. */
  727. SpiAt45dFlashWrite, /*!< \brief Data write function, sf_write. */
  728. SpiAt45dFlashCopy, /*!< \brief Unit copy function, sf_copy. */
  729. SpiAt45dFlashCommit, /*!< \brief Unit commit function, sf_commit. */
  730. SpiAt45dFlashErase /*!< \brief Unit erase function, sf_erase. */
  731. };
  732. #ifndef FLASH_MOUNT_OFFSET_AT45D2
  733. #ifdef MOUNT_OFFSET_AT45D2
  734. #define FLASH_MOUNT_OFFSET_AT45D2 MOUNT_OFFSET_AT45D2
  735. #else
  736. #define FLASH_MOUNT_OFFSET_AT45D2 0
  737. #endif
  738. #endif
  739. #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D2
  740. #ifdef MOUNT_TOP_RESERVE_AT45D2
  741. #define FLASH_MOUNT_TOP_RESERVE_AT45D2 MOUNT_TOP_RESERVE_AT45D2
  742. #else
  743. #define FLASH_MOUNT_TOP_RESERVE_AT45D2 1
  744. #endif
  745. #endif
  746. /*!
  747. * \brief Third AT45D DataFlash interface implementation structure.
  748. */
  749. NUTSERIALFLASH flashAt45d2 = {
  750. &nodeAt45d2, /*!< \brief Pointer to the SPI node structure, sf_node. */
  751. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  752. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  753. 0, /*!< \brief Total number of units, sf_units. */
  754. FLASH_MOUNT_OFFSET_AT45D2, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  755. FLASH_MOUNT_TOP_RESERVE_AT45D2, /*!< \brief Reserved units at the top, sf_rsvtop. */
  756. SpiAt45dFlashInit, /*!< \brief Flash device initialization function, sf_init. */
  757. SpiAt45dFlashExit, /*!< \brief Flash device release function, sf_exit. */
  758. SpiAt45dFlashCheck, /*!< \brief Unit validation function, sf_check. */
  759. SpiAt45dFlashRead, /*!< \brief Data read function, sf_read. */
  760. SpiAt45dFlashCompare, /*!< \brief Data compare function, sf_compare. */
  761. SpiAt45dFlashUsed, /*!< \brief Unit usage query function, sf_used. */
  762. SpiAt45dFlashWrite, /*!< \brief Data write function, sf_write. */
  763. SpiAt45dFlashCopy, /*!< \brief Unit copy function, sf_copy. */
  764. SpiAt45dFlashCommit, /*!< \brief Unit commit function, sf_commit. */
  765. SpiAt45dFlashErase /*!< \brief Unit erase function, sf_erase. */
  766. };
  767. #ifndef FLASH_MOUNT_OFFSET_AT45D3
  768. #ifdef MOUNT_OFFSET_AT45D3
  769. #define FLASH_MOUNT_OFFSET_AT45D3 MOUNT_OFFSET_AT45D3
  770. #else
  771. #define FLASH_MOUNT_OFFSET_AT45D3 0
  772. #endif
  773. #endif
  774. #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D3
  775. #ifdef MOUNT_TOP_RESERVE_AT45D3
  776. #define FLASH_MOUNT_TOP_RESERVE_AT45D3 MOUNT_TOP_RESERVE_AT45D3
  777. #else
  778. #define FLASH_MOUNT_TOP_RESERVE_AT45D3 1
  779. #endif
  780. #endif
  781. /*!
  782. * \brief Forth AT45D DataFlash interface implementation structure.
  783. */
  784. NUTSERIALFLASH flashAt45d3 = {
  785. &nodeAt45d3, /*!< \brief Pointer to the SPI node structure, sf_node. */
  786. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  787. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  788. 0, /*!< \brief Total number of units, sf_units. */
  789. FLASH_MOUNT_OFFSET_AT45D3, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  790. FLASH_MOUNT_TOP_RESERVE_AT45D3, /*!< \brief Reserved units at the top, sf_rsvtop. */
  791. SpiAt45dFlashInit, /*!< \brief Flash device initialization function, sf_init. */
  792. SpiAt45dFlashExit, /*!< \brief Flash device release function, sf_exit. */
  793. SpiAt45dFlashCheck, /*!< \brief Unit validation function, sf_check. */
  794. SpiAt45dFlashRead, /*!< \brief Data read function, sf_read. */
  795. SpiAt45dFlashCompare, /*!< \brief Data compare function, sf_compare. */
  796. SpiAt45dFlashUsed, /*!< \brief Unit usage query function, sf_used. */
  797. SpiAt45dFlashWrite, /*!< \brief Data write function, sf_write. */
  798. SpiAt45dFlashCopy, /*!< \brief Unit copy function, sf_copy. */
  799. SpiAt45dFlashCommit, /*!< \brief Unit commit function, sf_commit. */
  800. SpiAt45dFlashErase /*!< \brief Unit erase function, sf_erase. */
  801. };
  802. /*@}*/