spi_at45dib.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. /*
  2. * Copyright (C) 2008-2010 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_at45dib.c
  36. * \brief Low memory routines for Adesto/Atmel AT45 serial DataFlash
  37. * memory chips.
  38. *
  39. * \verbatim
  40. * $Id$
  41. * \endverbatim
  42. */
  43. #include <cfg/memory.h>
  44. #include <sys/nutdebug.h>
  45. #include <dev/at45d.h>
  46. #ifndef DFCMD_BUF1_LOAD_PAGE
  47. #define DFCMD_BUF1_LOAD_PAGE 0x53
  48. #endif
  49. #ifndef DFCMD_BUF2_LOAD_PAGE
  50. #define DFCMD_BUF2_LOAD_PAGE 0x55
  51. #endif
  52. #include <stdlib.h>
  53. #include <dev/spi_at45dib.h>
  54. /*!
  55. * \addtogroup xgAt54dib
  56. */
  57. /*@{*/
  58. /*! \brief RAM buffer dirty flag. */
  59. #define AT45DIB_FDIRTY 0x0001
  60. /*!
  61. * \brief Internal information structure.
  62. *
  63. * This structure is mainly used to keep track of the serial flash's
  64. * internal RAM buffers.
  65. */
  66. typedef struct _AT45DIB {
  67. /*! \brief Page number.
  68. *
  69. * Contains the number of the pages currently loaded into the RAM buffers.
  70. */
  71. sf_unit_t dib_page[2];
  72. /*! \brief Attribute flags.
  73. *
  74. * Contains AT45DIB_FDIRTY, if the RAM buffer has been modified and not
  75. * yet written back to flash memory.
  76. */
  77. uint_fast8_t flags[2];
  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 dib_locks[2];
  84. /*! \brief Unlock event queue.
  85. *
  86. * Queued threads waiting for an unlocked buffer.
  87. */
  88. HANDLE dib_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 dib_pshft;
  95. } AT45DIB;
  96. /*!
  97. * \brief Transmit DataFlash command.
  98. *
  99. * \param node Specifies the SPI node.
  100. * \param op Command code.
  101. * \param parm Command parameter.
  102. * \param oplen Command length.
  103. *
  104. * \return 0 on success, -1 on errors.
  105. */
  106. static int At45dibTransmitCmd(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
  107. {
  108. uint8_t cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  109. NUTASSERT(oplen <= sizeof(cmd));
  110. cmd[0] = op;
  111. if (parm) {
  112. cmd[1] = (uint8_t) (parm >> 16);
  113. cmd[2] = (uint8_t) (parm >> 8);
  114. cmd[3] = (uint8_t) parm;
  115. }
  116. return (*((NUTSPIBUS *) node->node_bus)->bus_transfer) (node, cmd, NULL, oplen);
  117. }
  118. /*!
  119. * \brief Execute DataFlash command with data transfer.
  120. *
  121. * \param node Specifies the SPI node.
  122. * \param op Command code.
  123. * \param parm Command parameter.
  124. * \param oplen Command length.
  125. * \param txbuf Pointer to the transmit data buffer, may be set to NULL.
  126. * \param rxbuf Pointer to the receive data buffer, may be set to NULL.
  127. * \param xlen Number of byte to receive and/or transmit.
  128. *
  129. * \return 0 on success, -1 on errors.
  130. */
  131. static int At45dibTransfer(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen,
  132. const void *txbuf, void *rxbuf, int xlen)
  133. {
  134. int rc;
  135. NUTSPIBUS *bus;
  136. /* Sanity checks. */
  137. NUTASSERT(node != NULL);
  138. bus = (NUTSPIBUS *) node->node_bus;
  139. NUTASSERT(bus != NULL);
  140. NUTASSERT(bus->bus_alloc != NULL);
  141. NUTASSERT(bus->bus_transfer != NULL);
  142. NUTASSERT(bus->bus_release != NULL);
  143. rc = (*bus->bus_alloc) (node, 0);
  144. if (rc == 0) {
  145. rc = At45dibTransmitCmd(node, op, parm, oplen);
  146. if (rc == 0 && xlen) {
  147. rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen);
  148. }
  149. (*bus->bus_release) (node);
  150. }
  151. return rc;
  152. }
  153. /*!
  154. * \brief Execute DataFlash command without data.
  155. *
  156. * \param node Specifies the SPI node.
  157. * \param op Command operation code.
  158. * \param parm Optional command parameter.
  159. * \param oplen Command length.
  160. *
  161. * \return 0 on success, -1 on errors.
  162. */
  163. static int At45dibCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
  164. {
  165. return At45dibTransfer(node, op, parm, oplen, NULL, NULL, 0);
  166. }
  167. /*!
  168. * \brief Execute serial flash compare.
  169. *
  170. * \param node Specifies the SPI node.
  171. * \param op Command operation code.
  172. * \param parm Optional command parameter.
  173. * \param oplen Command length.
  174. * \param buf Pointer to the data to compare.
  175. * \param xlen Number of byte to compare.
  176. *
  177. * \return 0 on success, -1 on errors.
  178. */
  179. static int At45dibCompare(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, const void *buf, int xlen)
  180. {
  181. int rc;
  182. NUTSPIBUS *bus;
  183. /* Sanity checks. */
  184. NUTASSERT(node != NULL);
  185. bus = (NUTSPIBUS *) node->node_bus;
  186. NUTASSERT(bus != NULL);
  187. NUTASSERT(bus->bus_alloc != NULL);
  188. NUTASSERT(bus->bus_transfer != NULL);
  189. NUTASSERT(bus->bus_release != NULL);
  190. rc = (*bus->bus_alloc) (node, 0);
  191. if (rc == 0) {
  192. rc = At45dibTransmitCmd(node, op, parm, oplen);
  193. if (rc == 0 && xlen) {
  194. int i;
  195. uint8_t c;
  196. uint8_t *cp = (uint8_t *) buf;
  197. for (i = 0; i < xlen; i++) {
  198. rc = (*bus->bus_transfer) (node, NULL, &c, 1);
  199. if (rc) {
  200. break;
  201. }
  202. if (c != cp[i]) {
  203. rc = (int) c - (int) cp[i];
  204. break;
  205. }
  206. }
  207. }
  208. (*bus->bus_release) (node);
  209. }
  210. return rc;
  211. }
  212. /*!
  213. * \brief Determine number of used bytes in a unit.
  214. *
  215. * This function will scan a given page for bytes not equal 0xFF. The
  216. * last byte found marks the end of the block of used bytes.
  217. *
  218. * \param node Specifies the SPI node.
  219. * \param op Command operation code.
  220. * \param parm Optional command parameter.
  221. * \param oplen Command length.
  222. * \param xlen Number of byte to check.
  223. *
  224. * \return Number of bytes or -1 on errors.
  225. */
  226. static int At45dibUsed(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, int xlen)
  227. {
  228. int rc;
  229. NUTSPIBUS *bus;
  230. /* Sanity checks. */
  231. NUTASSERT(node != NULL);
  232. bus = (NUTSPIBUS *) node->node_bus;
  233. NUTASSERT(bus != NULL);
  234. NUTASSERT(bus->bus_alloc != NULL);
  235. NUTASSERT(bus->bus_transfer != NULL);
  236. NUTASSERT(bus->bus_release != NULL);
  237. rc = (*bus->bus_alloc) (node, 0);
  238. if (rc == 0) {
  239. rc = At45dibTransmitCmd(node, op, parm, oplen);
  240. if (rc == 0 && xlen) {
  241. int i;
  242. int n = -1;
  243. uint8_t c;
  244. for (i = 0; i < xlen; i++) {
  245. rc = (*bus->bus_transfer) (node, NULL, &c, 1);
  246. if (rc) {
  247. break;
  248. }
  249. if (c != 0xff) {
  250. n = i;
  251. }
  252. }
  253. if (rc == 0) {
  254. rc = n + 1;
  255. }
  256. }
  257. (*bus->bus_release) (node);
  258. }
  259. return rc;
  260. }
  261. #ifdef AT45D_CRC_PAGE
  262. /*
  263. * http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
  264. */
  265. static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
  266. {
  267. data ^= (uint8_t) (crc);
  268. data ^= data << 4;
  269. return ((((uint16_t) data << 8) | (crc >> 8)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
  270. }
  271. /*!
  272. * \brief Execute serial flash CRC.
  273. *
  274. * \param node Specifies the SPI node.
  275. * \param op Command operation code.
  276. * \param parm Optional command parameter.
  277. * \param oplen Command length.
  278. * \param crc16 Pointer to the variable that receives the checksum.
  279. * \param xlen Number of byte to use for the calculation.
  280. *
  281. * \return 0 on success, -1 on errors.
  282. */
  283. static int At45dibChecksum(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, uint16_t * crc16, int xlen)
  284. {
  285. int rc;
  286. NUTSPIBUS *bus;
  287. /* Sanity checks. */
  288. NUTASSERT(node != NULL);
  289. bus = (NUTSPIBUS *) node->node_bus;
  290. NUTASSERT(bus != NULL);
  291. NUTASSERT(bus->bus_alloc != NULL);
  292. NUTASSERT(bus->bus_transfer != NULL);
  293. NUTASSERT(bus->bus_release != NULL);
  294. *crc16 = 0xffff;
  295. rc = (*bus->bus_alloc) (node, 0);
  296. if (rc == 0) {
  297. rc = At45dibTransmitCmd(node, op, parm, oplen);
  298. if (rc == 0 && xlen) {
  299. int i;
  300. uint8_t c;
  301. uint_fast8_t ne = 0;
  302. for (i = 0; i < xlen; i++) {
  303. rc = (*bus->bus_transfer) (node, NULL, &c, 1);
  304. if (rc) {
  305. break;
  306. }
  307. if (ne || c != 0xff) {
  308. ne = 1;
  309. *crc16 = crc_ccitt_update(*crc16, c);
  310. }
  311. }
  312. if (*crc16 == 0) {
  313. *crc16 = 0xffff;
  314. }
  315. }
  316. (*bus->bus_release) (node);
  317. }
  318. return rc;
  319. }
  320. #endif /* AT45D_CRC_PAGE */
  321. /*!
  322. * \brief Query the status of the serial flash.
  323. *
  324. * \param node Specifies the SPI node.
  325. *
  326. * \return 0 on success or -1 in case of an error.
  327. */
  328. static uint8_t At45dibStatus(NUTSPINODE * node)
  329. {
  330. int rc;
  331. uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF };
  332. NUTSPIBUS *bus;
  333. /* Sanity checks. */
  334. NUTASSERT(node != NULL);
  335. NUTASSERT(node->node_bus != NULL);
  336. bus = (NUTSPIBUS *) node->node_bus;
  337. NUTASSERT(bus->bus_alloc != NULL);
  338. NUTASSERT(bus->bus_transfer != NULL);
  339. NUTASSERT(bus->bus_wait != NULL);
  340. NUTASSERT(bus->bus_release != NULL);
  341. rc = (*bus->bus_alloc) (node, 0);
  342. if (rc == 0) {
  343. rc = (*bus->bus_transfer) (node, cmd, cmd, 2);
  344. if (rc == 0) {
  345. (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
  346. rc = cmd[1];
  347. }
  348. (*bus->bus_release) (node);
  349. }
  350. return (uint8_t) rc;
  351. }
  352. /*!
  353. * \brief Wait until serial flash memory cycle finished.
  354. *
  355. * \param node Specifies the SPI node.
  356. * \param tmo Number of loops to wait.
  357. *
  358. * \return 0 on success or -1 in case of an error.
  359. */
  360. static int At45dibWaitReady(NUTSPINODE * node, uint32_t tmo)
  361. {
  362. uint8_t sr;
  363. while (((sr = At45dibStatus(node)) & 0x80) == 0) {
  364. if (tmo-- == 0) {
  365. return -1;
  366. }
  367. }
  368. return 0;
  369. }
  370. /*!
  371. * \brief Flash the given buffer.
  372. *
  373. * \param sfi Specifies the serial flash interface.
  374. * \param b Number of the buffer, either 0 or 1.
  375. *
  376. * \return 0 on success or -1 in case of an error.
  377. */
  378. static int At45dibFlash(NUTSERIALFLASH * sfi, int_fast8_t b)
  379. {
  380. int rc;
  381. AT45DIB *at;
  382. uint32_t pga;
  383. #ifdef AT45D_CRC_PAGE
  384. /* Store a 16 bit CRC at the end of the page, if configured. */
  385. {
  386. uint16_t crc16;
  387. if (At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size)) {
  388. return -1;
  389. }
  390. if (At45dibTransfer
  391. (sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, sfi->sf_unit_size, 4, &crc16, NULL, sizeof(crc16))) {
  392. return -1;
  393. }
  394. }
  395. #endif
  396. at = (AT45DIB *) sfi->sf_info;
  397. pga = at->dib_page[b];
  398. pga <<= at->dib_pshft;
  399. rc = At45dibCommand(sfi->sf_node, b ? DFCMD_BUF2_FLASH_NE : DFCMD_BUF1_FLASH_NE, pga, 4);
  400. if (rc == 0) {
  401. rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
  402. at->flags[b] &= ~AT45DIB_FDIRTY;
  403. }
  404. return rc;
  405. }
  406. /*!
  407. * \brief Release the internal RAM buffer.
  408. *
  409. * \param sfi Specifies the serial flash interface.
  410. * \param b Number of the buffer, either 0 or 1.
  411. */
  412. static void At45dibRelease(NUTSERIALFLASH * sfi, int b)
  413. {
  414. AT45DIB *at = (AT45DIB *) sfi->sf_info;
  415. at->dib_locks[b]--;
  416. NutEventPost(&at->dib_lque);
  417. }
  418. /*!
  419. * \brief Load a given page into the internal RAM buffer.
  420. *
  421. * The RAM buffer will be locked and marked dirty. To unlock the buffer,
  422. * call At45dibRelease(). To clean the buffer, call SpiAt45dibCommit().
  423. *
  424. * \return Number of the buffer.
  425. */
  426. static int_fast8_t At45dibAllocate(NUTSERIALFLASH * sfi, sf_unit_t pgn)
  427. {
  428. int_fast8_t b;
  429. AT45DIB *at;
  430. at = (AT45DIB *) sfi->sf_info;
  431. for (;;) {
  432. /*
  433. ** Check, if the page is already loaded.
  434. */
  435. for (b = 0; b < 2; b++) {
  436. if (at->dib_page[b] == pgn) {
  437. at->dib_locks[b]++;
  438. at->flags[b] |= AT45DIB_FDIRTY;
  439. return b;
  440. }
  441. }
  442. /*
  443. ** Search for a clean unlocked buffer and load the page.
  444. */
  445. for (b = 0; b < 2; b++) {
  446. if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == 0) {
  447. int rc;
  448. uint32_t pga = pgn;
  449. pga <<= at->dib_pshft;
  450. at->dib_locks[b]++;
  451. if (b) {
  452. rc = At45dibCommand(sfi->sf_node, DFCMD_BUF2_LOAD_PAGE, pga, 4);
  453. } else {
  454. rc = At45dibCommand(sfi->sf_node, DFCMD_BUF1_LOAD_PAGE, pga, 4);
  455. }
  456. if (rc == 0) {
  457. rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
  458. }
  459. if (rc == 0) {
  460. at->dib_page[b] = pgn;
  461. at->flags[b] |= AT45DIB_FDIRTY;
  462. return b;
  463. }
  464. At45dibRelease(sfi, b);
  465. return -1;
  466. }
  467. }
  468. /*
  469. ** No clean buffer. If not locked, save one of them.
  470. */
  471. for (b = 0; b < 2; b++) {
  472. if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
  473. if (At45dibFlash(sfi, b)) {
  474. return -1;
  475. }
  476. break;
  477. }
  478. }
  479. /*
  480. ** All buffers are locked. Wait for an unlock event.
  481. */
  482. if (b >= 2) {
  483. NutEventWait(&at->dib_lque, 0);
  484. }
  485. }
  486. }
  487. /*!
  488. * \brief Initialize the interface.
  489. *
  490. * \param sfi Specifies the serial flash interface.
  491. *
  492. * \return 0 on success or -1 in case of an error.
  493. */
  494. static int SpiAt45dibInit(NUTSERIALFLASH * sfi)
  495. {
  496. int_fast8_t b;
  497. uint8_t sr;
  498. int_fast8_t i;
  499. /* Sanity checks. */
  500. NUTASSERT(sfi != NULL);
  501. NUTASSERT(sfi->sf_node != NULL);
  502. sr = At45dibStatus(sfi->sf_node);
  503. sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE;
  504. for (i = at45d_known_types; --i >= 0;) {
  505. if (sr == at45d_info[i].at45d_srval) {
  506. /* Known DataFlash type. */
  507. AT45DIB *at = calloc(1, sizeof(AT45DIB));
  508. if (at == NULL) {
  509. return -1;
  510. }
  511. at->dib_pshft = at45d_info[i].at45d_pshft;
  512. for (b = 0; b < 2; b++) {
  513. at->dib_page[b] = SERIALFLASH_MAX_UNITS;
  514. }
  515. sfi->sf_info = (void *) at;
  516. sfi->sf_units = (sf_unit_t) at45d_info[i].at45d_pages;
  517. sfi->sf_unit_size = at45d_info[i].at45d_psize;
  518. #ifdef AT45D_CRC_PAGE
  519. sfi->sf_unit_size -= 2;
  520. #endif
  521. return 0;
  522. }
  523. }
  524. /* Unknown DataFlash type. */
  525. return -1;
  526. }
  527. /*!
  528. * \brief Release the interface.
  529. *
  530. * \param sfi Specifies the serial flash interface.
  531. *
  532. * \return 0 on success or -1 in case of an error.
  533. */
  534. static void SpiAt45dibExit(NUTSERIALFLASH * sfi)
  535. {
  536. NUTASSERT(sfi != NULL);
  537. NUTASSERT(sfi->sf_info != NULL);
  538. free(sfi->sf_info);
  539. }
  540. /*!
  541. * \brief Verify CRC of consecutive pages.
  542. *
  543. * \param sfi Specifies the serial flash interface.
  544. * \param pgn First page to verify.
  545. * \param cnt Number of consecutive pages to verify.
  546. *
  547. * \return 0 on success or -1 in case of an error.
  548. */
  549. static int SpiAt45dibCheck(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
  550. {
  551. int rc = 0;
  552. #ifdef AT45D_CRC_PAGE
  553. AT45DIB *at;
  554. int_fast8_t b;
  555. uint16_t crc16;
  556. /* Sanity checks. */
  557. NUTASSERT(sfi != NULL);
  558. NUTASSERT(sfi->sf_info != NULL);
  559. NUTASSERT(pgn + cnt <= sfi->sf_units);
  560. NUTASSERT(cnt >= 0);
  561. at = (AT45DIB *) sfi->sf_info;
  562. while (cnt--) {
  563. /* If this page is buffered, then read from the buffer. */
  564. for (b = 0; b < 2 && at->dib_page[b] != pgn + cnt; b++);
  565. if (b < 2) {
  566. At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size + 2);
  567. }
  568. /* If not buffered, then directly read from the flash. */
  569. else {
  570. uint32_t pga = pgn + cnt;
  571. pga <<= at->dib_pshft;
  572. At45dibChecksum(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, &crc16, sfi->sf_unit_size + 2);
  573. }
  574. if (crc16 != 0xFFFF) {
  575. rc = -1;
  576. break;
  577. }
  578. }
  579. #endif
  580. return rc;
  581. }
  582. /*!
  583. * \brief Read data from a given page.
  584. *
  585. * \param sfi Specifies the serial flash interface.
  586. * \param pgn Page to read from.
  587. * \param off Read offset into the page. If negative, the offset counts
  588. * from the end of the page.
  589. * \param data Pointer to the buffer that receives the data.
  590. * \param len Number of data bytes to read.
  591. *
  592. * \return 0 on success or -1 in case of an error.
  593. */
  594. static int SpiAt45dibRead(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, void *data, int len)
  595. {
  596. int rc = 0;
  597. /* Sanity checks. */
  598. NUTASSERT(sfi != NULL);
  599. NUTASSERT(sfi->sf_info != NULL);
  600. NUTASSERT(pgn < sfi->sf_units);
  601. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  602. NUTASSERT(data != NULL);
  603. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  604. if (len) {
  605. int_fast8_t b;
  606. AT45DIB *at = (AT45DIB *) sfi->sf_info;
  607. /* Normalize the offset. */
  608. if (off < 0) {
  609. off += sfi->sf_unit_size;
  610. }
  611. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  612. /* If this page is buffered, then read from the buffer. */
  613. for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
  614. if (b < 2) {
  615. rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, NULL, data, len);
  616. }
  617. /* If not buffered, then directly read from the flash. */
  618. else {
  619. uint32_t pga = pgn;
  620. pga <<= at->dib_pshft;
  621. pga |= off;
  622. rc = At45dibTransfer(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, NULL, data, len);
  623. }
  624. }
  625. return rc;
  626. }
  627. /*!
  628. * \brief Compare data with the contents of a given page.
  629. *
  630. * This function allows to directly compare the contents of a page. It is
  631. * not required to load the page.
  632. *
  633. * \param sfi Specifies the serial flash interface.
  634. * \param pgn Page to compare.
  635. * \param off Offset into the page. If negative, the offset counts from
  636. * the end of the page.
  637. * \param data Pointer to the data to compare.
  638. * \param len Number of data bytes to compare.
  639. *
  640. * \return 0 on success or -1 if the contents differs or in case of an error.
  641. */
  642. static int SpiAt45dibCompare(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
  643. {
  644. int rc = 0;
  645. /* Sanity checks. */
  646. NUTASSERT(sfi != NULL);
  647. NUTASSERT(sfi->sf_info != NULL);
  648. NUTASSERT(pgn < sfi->sf_units);
  649. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  650. NUTASSERT(data != NULL);
  651. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  652. if (len) {
  653. int_fast8_t b;
  654. AT45DIB *at = (AT45DIB *) sfi->sf_info;
  655. /* Normalize the offset. */
  656. if (off < 0) {
  657. off += sfi->sf_unit_size;
  658. }
  659. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  660. /* If this page is buffered, then read from the buffer. */
  661. for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
  662. if (b < 2) {
  663. rc = At45dibCompare(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, data, len);
  664. }
  665. /* If not buffered, then directly read from the flash. */
  666. else {
  667. uint32_t pga = pgn;
  668. pga <<= at->dib_pshft;
  669. pga |= off;
  670. rc = At45dibCompare(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, data, len);
  671. }
  672. }
  673. return rc;
  674. }
  675. /*!
  676. * \brief Return the number of bytes used in a given page.
  677. *
  678. * This function allows to directly compare the contents of a page. It is
  679. * not required to load the page.
  680. *
  681. * \param sfi Specifies the serial flash interface.
  682. * \param pgn Page to check.
  683. * \param skip Number of bytes to ingore. May be a positive or negative
  684. * value, to ingnore bytes at the beginning or at the end, resp.
  685. *
  686. * \return 0 on success or -1 if the contents differs or in case of an error.
  687. */
  688. static int SpiAt45dibUsed(NUTSERIALFLASH * sfi, sf_unit_t pgn, int skip)
  689. {
  690. int rc;
  691. int len;
  692. AT45DIB *at;
  693. int_fast8_t b;
  694. /* Sanity checks. */
  695. NUTASSERT(sfi != NULL);
  696. NUTASSERT(pgn < sfi->sf_units);
  697. NUTASSERT(skip <= (int) sfi->sf_unit_size);
  698. at = (AT45DIB *) sfi->sf_info;
  699. /* Determine length and offset. */
  700. len = (int) sfi->sf_unit_size;
  701. if (skip < 0) {
  702. len += skip;
  703. skip = 0;
  704. } else {
  705. len -= skip;
  706. }
  707. /* If this page is buffered, then read from the buffer. */
  708. for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
  709. if (b < 2) {
  710. rc = At45dibUsed(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, skip, 5, len);
  711. }
  712. /* If not buffered, then directly read from the flash. */
  713. else {
  714. uint32_t pga = pgn;
  715. pga <<= at->dib_pshft;
  716. pga |= skip;
  717. rc = At45dibUsed(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, len);
  718. }
  719. return rc;
  720. }
  721. /*!
  722. * \brief Write data to a given page.
  723. *
  724. * Loads the page into an internal RAM buffer and replaces the contents
  725. * with the given data. The buffer will be marked dirty, but not written
  726. * back. See SpiAt45dibCommit().
  727. *
  728. * \param sfi Specifies the serial flash interface.
  729. * \param pgn Page to write to.
  730. * \param off Offset into the page, where the new data should be placed.
  731. * If negative, the offset counts from the end of the page.
  732. * \param data Pointer to the data to write.
  733. * \param len Number of data bytes to write.
  734. *
  735. * \return 0 on success or -1 in case of an error.
  736. */
  737. static int SpiAt45dibWrite(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
  738. {
  739. int rc = 0;
  740. /* Sanity checks. */
  741. NUTASSERT(sfi != NULL);
  742. NUTASSERT(pgn < sfi->sf_units);
  743. NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
  744. NUTASSERT(len == 0 || data != NULL);
  745. NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
  746. if (len) {
  747. int_fast8_t b;
  748. /* Normalize the offset. */
  749. if (off < 0) {
  750. off += sfi->sf_unit_size;
  751. }
  752. NUTASSERT(off + len <= (int) sfi->sf_unit_size);
  753. /* Load the page. */
  754. b = At45dibAllocate(sfi, pgn);
  755. if (b < 0) {
  756. return -1;
  757. }
  758. /* Transfer the data and release the page. */
  759. rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, off, 4, data, NULL, len);
  760. At45dibRelease(sfi, b);
  761. }
  762. return rc;
  763. }
  764. /*!
  765. * \brief Copy page.
  766. *
  767. * Loads the source page into an internal RAM buffer und designates the
  768. * buffer to the destination page. The buffer will be marked dirty, but
  769. * not written back. See SpiAt45dibCommit().
  770. *
  771. * \param sfi Specifies the serial flash interface.
  772. * \param spg Source page to copy from.
  773. * \param dpg Destination page to copy to.
  774. *
  775. * \return 0 on success or -1 in case of an error.
  776. */
  777. static int SpiAt45dibCopy(NUTSERIALFLASH * sfi, sf_unit_t spg, sf_unit_t dpg)
  778. {
  779. int_fast8_t b;
  780. /* Sanity checks. */
  781. NUTASSERT(sfi != NULL);
  782. NUTASSERT(sfi->sf_info != NULL);
  783. NUTASSERT(spg < sfi->sf_units);
  784. NUTASSERT(dpg < sfi->sf_units);
  785. /* If source and destination page numbers are equal, just load it. */
  786. if (spg == dpg) {
  787. b = At45dibAllocate(sfi, spg);
  788. } else {
  789. AT45DIB *at = (AT45DIB *) sfi->sf_info;
  790. /* Invalidate any buffered destination. */
  791. for (b = 0; b < 2; b++) {
  792. if (at->dib_page[b] == dpg) {
  793. /* But do not touch locked pages. */
  794. if (at->dib_locks[b]) {
  795. return -1;
  796. }
  797. at->dib_page[b] = SERIALFLASH_MAX_UNITS;
  798. }
  799. }
  800. /* Try to load the source page and make it the destination page. */
  801. b = At45dibAllocate(sfi, spg);
  802. if (b >= 0) {
  803. at->dib_page[b] = dpg;
  804. }
  805. }
  806. if (b >= 0) {
  807. At45dibRelease(sfi, b);
  808. return 0;
  809. }
  810. return -1;
  811. }
  812. /*!
  813. * \brief Commit page.
  814. *
  815. * If the contents of the given page is in one of the internal RAM
  816. * buffers and if the contents had been changed since the last page
  817. * load, then the buffer is written back to the flash.
  818. *
  819. * \param sfi Specifies the serial flash interface.
  820. * \param pgn Page to commit.
  821. *
  822. * \return 0 on success or -1 in case of an error.
  823. */
  824. static int SpiAt45dibCommit(NUTSERIALFLASH * sfi, sf_unit_t pgn)
  825. {
  826. int rc = 0;
  827. int_fast8_t b;
  828. AT45DIB *at;
  829. /* Sanity checks. */
  830. NUTASSERT(sfi != NULL);
  831. NUTASSERT(pgn < sfi->sf_units);
  832. at = (AT45DIB *) sfi->sf_info;
  833. for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
  834. if (b < 2 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
  835. rc = At45dibFlash(sfi, b);
  836. }
  837. return rc;
  838. }
  839. /*!
  840. * \brief Erase consecutive pages.
  841. *
  842. * \param sfi Specifies the serial flash interface.
  843. * \param pgn First page to erase.
  844. * \param cnt Number of consecutive pages to erase.
  845. *
  846. * \return 0 on success or -1 in case of an error.
  847. */
  848. static int SpiAt45dibErase(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
  849. {
  850. int_fast8_t b;
  851. AT45DIB *at;
  852. /* Sanity checks. */
  853. NUTASSERT(sfi != NULL);
  854. NUTASSERT(pgn + cnt <= sfi->sf_units);
  855. NUTASSERT(cnt >= 0);
  856. at = (AT45DIB *) sfi->sf_info;
  857. /* Invalidate any buffered page. */
  858. for (b = 0; b < 2; b++) {
  859. if (at->dib_page[b] >= pgn && at->dib_page[b] < pgn + cnt) {
  860. at->dib_page[b] = SERIALFLASH_MAX_UNITS;
  861. at->flags[b] &= ~AT45DIB_FDIRTY;
  862. }
  863. }
  864. while (cnt--) {
  865. uint32_t pga = pgn + cnt;
  866. pga <<= at->dib_pshft;
  867. if (At45dibCommand(sfi->sf_node, DFCMD_PAGE_ERASE, pga, 4)) {
  868. return -1;
  869. }
  870. if (At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS)) {
  871. return -1;
  872. }
  873. }
  874. return 0;
  875. }
  876. #ifndef SPI_RATE_AT45D0
  877. #define SPI_RATE_AT45D0 33000000
  878. #endif
  879. #ifndef SPI_MODE_AT45D0
  880. #ifdef SPI_CSHIGH_AT45D0
  881. #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
  882. #else
  883. #define SPI_MODE_AT45D0 SPI_MODE_3
  884. #endif
  885. #elif defined(SPI_CSHIGH_AT45D0)
  886. /* This is a tricky problem. Originally we didn't provide mode settings
  887. ** in the Configurator, but used mode 3 only. After experiencing problems
  888. ** with mode switching on the EIR, we need to set mode 0 for that board,
  889. ** which spoils our chip select polarity setting. */
  890. #if SPI_MODE_AT45D0 == SPI_MODE_0
  891. #undef SPI_MODE_AT45D0
  892. #define SPI_MODE_AT45D0 (SPI_MODE_0 | SPI_MODE_CSHIGH)
  893. #elif SPI_MODE_AT45D0 == SPI_MODE_3
  894. #undef SPI_MODE_AT45D0
  895. #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
  896. #endif
  897. #endif /* SPI_MODE_AT45D0 */
  898. /*!
  899. * \brief First AT45D DataFlash SPI node implementation structure.
  900. */
  901. static NUTSPINODE spiNode0 = {
  902. NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */
  903. NULL, /*!< \brief Pointer to device driver specific settings, node_stat. */
  904. SPI_RATE_AT45D0, /*!< \brief Initial clock rate, node_rate. */
  905. SPI_MODE_AT45D0, /*!< \brief Initial mode, node_mode. */
  906. 8, /*!< \brief Initial data bits, node_bits. */
  907. 0 /*!< \brief Chip select, node_cs. */
  908. };
  909. #ifndef MOUNT_OFFSET_AT45DIB0
  910. #ifdef MOUNT_OFFSET_AT45D0
  911. #define MOUNT_OFFSET_AT45DIB0 MOUNT_OFFSET_AT45D0
  912. #else
  913. #define MOUNT_OFFSET_AT45DIB0 0
  914. #endif
  915. #endif
  916. #ifndef MOUNT_TOP_RESERVE_AT45DIB0
  917. #ifdef MOUNT_TOP_RESERVE_AT45D0
  918. #define MOUNT_TOP_RESERVE_AT45DIB0 MOUNT_TOP_RESERVE_AT45D0
  919. #else
  920. #define MOUNT_TOP_RESERVE_AT45DIB0 1
  921. #endif
  922. #endif
  923. /*!
  924. * \brief First AT45D DataFlash interface implementation structure.
  925. */
  926. NUTSERIALFLASH flashAt45dib0 = {
  927. &spiNode0, /*!< \brief Pointer to the SPI node structure, sf_node. */
  928. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  929. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  930. 0, /*!< \brief Total number of units, sf_units. */
  931. MOUNT_OFFSET_AT45DIB0, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  932. MOUNT_TOP_RESERVE_AT45DIB0, /*!< \brief Reserved units at the top, sf_rsvtop. */
  933. SpiAt45dibInit, /*!< \brief Flash device initialization function, sf_init. */
  934. SpiAt45dibExit, /*!< \brief Flash device release function, sf_exit. */
  935. SpiAt45dibCheck, /*!< \brief Unit validation function, sf_check. */
  936. SpiAt45dibRead, /*!< \brief Data read function, sf_read. */
  937. SpiAt45dibCompare, /*!< \brief Data compare function, sf_compare. */
  938. SpiAt45dibUsed, /*!< \brief Unit usage query function, sf_used. */
  939. SpiAt45dibWrite, /*!< \brief Data write function, sf_write. */
  940. SpiAt45dibCopy, /*!< \brief Unit copy function, sf_copy. */
  941. SpiAt45dibCommit, /*!< \brief Unit commit function, sf_commit. */
  942. SpiAt45dibErase /*!< \brief Unit erase function, sf_erase. */
  943. };
  944. #ifndef SPI_RATE_AT45D1
  945. #define SPI_RATE_AT45D1 33000000
  946. #endif
  947. #ifndef SPI_MODE_AT45D1
  948. #ifdef SPI_CSHIGH_AT45D1
  949. #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
  950. #else
  951. #define SPI_MODE_AT45D1 SPI_MODE_3
  952. #endif
  953. #elif defined(SPI_CSHIGH_AT45D1)
  954. /* Same problem problem as above. */
  955. #if SPI_MODE_AT45D1 == SPI_MODE_0
  956. #undef SPI_MODE_AT45D1
  957. #define SPI_MODE_AT45D1 (SPI_MODE_0 | SPI_MODE_CSHIGH)
  958. #elif SPI_MODE_AT45D1 == SPI_MODE_3
  959. #undef SPI_MODE_AT45D1
  960. #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
  961. #endif
  962. #endif /* SPI_MODE_AT45D0 */
  963. /*!
  964. * \brief Second AT45D DataFlash SPI node implementation structure.
  965. */
  966. static NUTSPINODE spiNode1 = {
  967. NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */
  968. NULL, /*!< \brief Pointer to device driver specific settings, node_stat. */
  969. SPI_RATE_AT45D1, /*!< \brief Initial clock rate, node_rate. */
  970. SPI_MODE_AT45D1, /*!< \brief Initial mode, node_mode. */
  971. 8, /*!< \brief Initial data bits, node_bits. */
  972. 1 /*!< \brief Chip select, node_cs. */
  973. };
  974. #ifndef MOUNT_OFFSET_AT45DIB1
  975. #ifdef MOUNT_OFFSET_AT45D1
  976. #define MOUNT_OFFSET_AT45DIB1 MOUNT_OFFSET_AT45D1
  977. #else
  978. #define MOUNT_OFFSET_AT45DIB1 0
  979. #endif
  980. #endif
  981. #ifndef MOUNT_TOP_RESERVE_AT45DIB1
  982. #ifdef MOUNT_TOP_RESERVE_AT45D1
  983. #define MOUNT_TOP_RESERVE_AT45DIB1 MOUNT_TOP_RESERVE_AT45D1
  984. #else
  985. #define MOUNT_TOP_RESERVE_AT45DIB1 1
  986. #endif
  987. #endif
  988. /*!
  989. * \brief Second AT45D DataFlash interface implementation structure.
  990. */
  991. NUTSERIALFLASH flashAt45dib1 = {
  992. &spiNode1, /*!< \brief Pointer to the SPI node structure, sf_node. */
  993. NULL, /*!< \brief Pointer to a local information structure, sf_info. */
  994. 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
  995. 0, /*!< \brief Total number of units, sf_units. */
  996. MOUNT_OFFSET_AT45DIB1, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
  997. MOUNT_TOP_RESERVE_AT45DIB1, /*!< \brief Reserved units at the top, sf_rsvtop. */
  998. SpiAt45dibInit, /*!< \brief Flash device initialization function, sf_init. */
  999. SpiAt45dibExit, /*!< \brief Flash device release function, sf_exit. */
  1000. SpiAt45dibCheck, /*!< \brief Unit validation function, sf_check. */
  1001. SpiAt45dibRead, /*!< \brief Data read function, sf_read. */
  1002. SpiAt45dibCompare, /*!< \brief Data compare function, sf_compare. */
  1003. SpiAt45dibUsed, /*!< \brief Unit usage query function, sf_used. */
  1004. SpiAt45dibWrite, /*!< \brief Data write function, sf_write. */
  1005. SpiAt45dibCopy, /*!< \brief Unit copy function, sf_copy. */
  1006. SpiAt45dibCommit, /*!< \brief Unit commit function, sf_commit. */
  1007. SpiAt45dibErase /*!< \brief Unit erase function, sf_erase. */
  1008. };
  1009. /*@}*/