| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130 |
- /*
- * Copyright (C) 2008-2010 by egnite GmbH
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * \file dev/spi_at45dib.c
- * \brief Low memory routines for Adesto/Atmel AT45 serial DataFlash
- * memory chips.
- *
- * \verbatim
- * $Id$
- * \endverbatim
- */
- #include <cfg/memory.h>
- #include <sys/nutdebug.h>
- #include <dev/at45d.h>
- #ifndef DFCMD_BUF1_LOAD_PAGE
- #define DFCMD_BUF1_LOAD_PAGE 0x53
- #endif
- #ifndef DFCMD_BUF2_LOAD_PAGE
- #define DFCMD_BUF2_LOAD_PAGE 0x55
- #endif
- #include <stdlib.h>
- #include <dev/spi_at45dib.h>
- /*!
- * \addtogroup xgAt54dib
- */
- /*@{*/
- /*! \brief RAM buffer dirty flag. */
- #define AT45DIB_FDIRTY 0x0001
- /*!
- * \brief Internal information structure.
- *
- * This structure is mainly used to keep track of the serial flash's
- * internal RAM buffers.
- */
- typedef struct _AT45DIB {
- /*! \brief Page number.
- *
- * Contains the number of the pages currently loaded into the RAM buffers.
- */
- sf_unit_t dib_page[2];
- /*! \brief Attribute flags.
- *
- * Contains AT45DIB_FDIRTY, if the RAM buffer has been modified and not
- * yet written back to flash memory.
- */
- uint_fast8_t flags[2];
- /*! \brief Lock count.
- *
- * A buffer must not be written back to flash memory when this counter
- * is not equal to zero.
- */
- int dib_locks[2];
- /*! \brief Unlock event queue.
- *
- * Queued threads waiting for an unlocked buffer.
- */
- HANDLE dib_lque;
- /*! \brief Page address shift value.
- *
- * Number of bytes to shift the page address for the Dataflash command
- * parameter.
- */
- uint_fast8_t dib_pshft;
- } AT45DIB;
- /*!
- * \brief Transmit DataFlash command.
- *
- * \param node Specifies the SPI node.
- * \param op Command code.
- * \param parm Command parameter.
- * \param oplen Command length.
- *
- * \return 0 on success, -1 on errors.
- */
- static int At45dibTransmitCmd(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
- {
- uint8_t cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- NUTASSERT(oplen <= sizeof(cmd));
- cmd[0] = op;
- if (parm) {
- cmd[1] = (uint8_t) (parm >> 16);
- cmd[2] = (uint8_t) (parm >> 8);
- cmd[3] = (uint8_t) parm;
- }
- return (*((NUTSPIBUS *) node->node_bus)->bus_transfer) (node, cmd, NULL, oplen);
- }
- /*!
- * \brief Execute DataFlash command with data transfer.
- *
- * \param node Specifies the SPI node.
- * \param op Command code.
- * \param parm Command parameter.
- * \param oplen Command length.
- * \param txbuf Pointer to the transmit data buffer, may be set to NULL.
- * \param rxbuf Pointer to the receive data buffer, may be set to NULL.
- * \param xlen Number of byte to receive and/or transmit.
- *
- * \return 0 on success, -1 on errors.
- */
- static int At45dibTransfer(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen,
- const void *txbuf, void *rxbuf, int xlen)
- {
- int rc;
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- NUTASSERT(bus != NULL);
- NUTASSERT(bus->bus_alloc != NULL);
- NUTASSERT(bus->bus_transfer != NULL);
- NUTASSERT(bus->bus_release != NULL);
- rc = (*bus->bus_alloc) (node, 0);
- if (rc == 0) {
- rc = At45dibTransmitCmd(node, op, parm, oplen);
- if (rc == 0 && xlen) {
- rc = (*bus->bus_transfer) (node, txbuf, rxbuf, xlen);
- }
- (*bus->bus_release) (node);
- }
- return rc;
- }
- /*!
- * \brief Execute DataFlash command without data.
- *
- * \param node Specifies the SPI node.
- * \param op Command operation code.
- * \param parm Optional command parameter.
- * \param oplen Command length.
- *
- * \return 0 on success, -1 on errors.
- */
- static int At45dibCommand(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen)
- {
- return At45dibTransfer(node, op, parm, oplen, NULL, NULL, 0);
- }
- /*!
- * \brief Execute serial flash compare.
- *
- * \param node Specifies the SPI node.
- * \param op Command operation code.
- * \param parm Optional command parameter.
- * \param oplen Command length.
- * \param buf Pointer to the data to compare.
- * \param xlen Number of byte to compare.
- *
- * \return 0 on success, -1 on errors.
- */
- static int At45dibCompare(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, const void *buf, int xlen)
- {
- int rc;
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- NUTASSERT(bus != NULL);
- NUTASSERT(bus->bus_alloc != NULL);
- NUTASSERT(bus->bus_transfer != NULL);
- NUTASSERT(bus->bus_release != NULL);
- rc = (*bus->bus_alloc) (node, 0);
- if (rc == 0) {
- rc = At45dibTransmitCmd(node, op, parm, oplen);
- if (rc == 0 && xlen) {
- int i;
- uint8_t c;
- uint8_t *cp = (uint8_t *) buf;
- for (i = 0; i < xlen; i++) {
- rc = (*bus->bus_transfer) (node, NULL, &c, 1);
- if (rc) {
- break;
- }
- if (c != cp[i]) {
- rc = (int) c - (int) cp[i];
- break;
- }
- }
- }
- (*bus->bus_release) (node);
- }
- return rc;
- }
- /*!
- * \brief Determine number of used bytes in a unit.
- *
- * This function will scan a given page for bytes not equal 0xFF. The
- * last byte found marks the end of the block of used bytes.
- *
- * \param node Specifies the SPI node.
- * \param op Command operation code.
- * \param parm Optional command parameter.
- * \param oplen Command length.
- * \param xlen Number of byte to check.
- *
- * \return Number of bytes or -1 on errors.
- */
- static int At45dibUsed(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, int xlen)
- {
- int rc;
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- NUTASSERT(bus != NULL);
- NUTASSERT(bus->bus_alloc != NULL);
- NUTASSERT(bus->bus_transfer != NULL);
- NUTASSERT(bus->bus_release != NULL);
- rc = (*bus->bus_alloc) (node, 0);
- if (rc == 0) {
- rc = At45dibTransmitCmd(node, op, parm, oplen);
- if (rc == 0 && xlen) {
- int i;
- int n = -1;
- uint8_t c;
- for (i = 0; i < xlen; i++) {
- rc = (*bus->bus_transfer) (node, NULL, &c, 1);
- if (rc) {
- break;
- }
- if (c != 0xff) {
- n = i;
- }
- }
- if (rc == 0) {
- rc = n + 1;
- }
- }
- (*bus->bus_release) (node);
- }
- return rc;
- }
- #ifdef AT45D_CRC_PAGE
- /*
- * http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
- */
- static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
- {
- data ^= (uint8_t) (crc);
- data ^= data << 4;
- return ((((uint16_t) data << 8) | (crc >> 8)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
- }
- /*!
- * \brief Execute serial flash CRC.
- *
- * \param node Specifies the SPI node.
- * \param op Command operation code.
- * \param parm Optional command parameter.
- * \param oplen Command length.
- * \param crc16 Pointer to the variable that receives the checksum.
- * \param xlen Number of byte to use for the calculation.
- *
- * \return 0 on success, -1 on errors.
- */
- static int At45dibChecksum(NUTSPINODE * node, uint8_t op, uint32_t parm, uint_fast8_t oplen, uint16_t * crc16, int xlen)
- {
- int rc;
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- NUTASSERT(bus != NULL);
- NUTASSERT(bus->bus_alloc != NULL);
- NUTASSERT(bus->bus_transfer != NULL);
- NUTASSERT(bus->bus_release != NULL);
- *crc16 = 0xffff;
- rc = (*bus->bus_alloc) (node, 0);
- if (rc == 0) {
- rc = At45dibTransmitCmd(node, op, parm, oplen);
- if (rc == 0 && xlen) {
- int i;
- uint8_t c;
- uint_fast8_t ne = 0;
- for (i = 0; i < xlen; i++) {
- rc = (*bus->bus_transfer) (node, NULL, &c, 1);
- if (rc) {
- break;
- }
- if (ne || c != 0xff) {
- ne = 1;
- *crc16 = crc_ccitt_update(*crc16, c);
- }
- }
- if (*crc16 == 0) {
- *crc16 = 0xffff;
- }
- }
- (*bus->bus_release) (node);
- }
- return rc;
- }
- #endif /* AT45D_CRC_PAGE */
- /*!
- * \brief Query the status of the serial flash.
- *
- * \param node Specifies the SPI node.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static uint8_t At45dibStatus(NUTSPINODE * node)
- {
- int rc;
- uint8_t cmd[2] = { DFCMD_READ_STATUS, 0xFF };
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- NUTASSERT(node->node_bus != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- NUTASSERT(bus->bus_alloc != NULL);
- NUTASSERT(bus->bus_transfer != NULL);
- NUTASSERT(bus->bus_wait != NULL);
- NUTASSERT(bus->bus_release != NULL);
- rc = (*bus->bus_alloc) (node, 0);
- if (rc == 0) {
- rc = (*bus->bus_transfer) (node, cmd, cmd, 2);
- if (rc == 0) {
- (*bus->bus_wait) (node, NUT_WAIT_INFINITE);
- rc = cmd[1];
- }
- (*bus->bus_release) (node);
- }
- return (uint8_t) rc;
- }
- /*!
- * \brief Wait until serial flash memory cycle finished.
- *
- * \param node Specifies the SPI node.
- * \param tmo Number of loops to wait.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int At45dibWaitReady(NUTSPINODE * node, uint32_t tmo)
- {
- uint8_t sr;
- while (((sr = At45dibStatus(node)) & 0x80) == 0) {
- if (tmo-- == 0) {
- return -1;
- }
- }
- return 0;
- }
- /*!
- * \brief Flash the given buffer.
- *
- * \param sfi Specifies the serial flash interface.
- * \param b Number of the buffer, either 0 or 1.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int At45dibFlash(NUTSERIALFLASH * sfi, int_fast8_t b)
- {
- int rc;
- AT45DIB *at;
- uint32_t pga;
- #ifdef AT45D_CRC_PAGE
- /* Store a 16 bit CRC at the end of the page, if configured. */
- {
- uint16_t crc16;
- if (At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size)) {
- return -1;
- }
- if (At45dibTransfer
- (sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, sfi->sf_unit_size, 4, &crc16, NULL, sizeof(crc16))) {
- return -1;
- }
- }
- #endif
- at = (AT45DIB *) sfi->sf_info;
- pga = at->dib_page[b];
- pga <<= at->dib_pshft;
- rc = At45dibCommand(sfi->sf_node, b ? DFCMD_BUF2_FLASH_NE : DFCMD_BUF1_FLASH_NE, pga, 4);
- if (rc == 0) {
- rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
- at->flags[b] &= ~AT45DIB_FDIRTY;
- }
- return rc;
- }
- /*!
- * \brief Release the internal RAM buffer.
- *
- * \param sfi Specifies the serial flash interface.
- * \param b Number of the buffer, either 0 or 1.
- */
- static void At45dibRelease(NUTSERIALFLASH * sfi, int b)
- {
- AT45DIB *at = (AT45DIB *) sfi->sf_info;
- at->dib_locks[b]--;
- NutEventPost(&at->dib_lque);
- }
- /*!
- * \brief Load a given page into the internal RAM buffer.
- *
- * The RAM buffer will be locked and marked dirty. To unlock the buffer,
- * call At45dibRelease(). To clean the buffer, call SpiAt45dibCommit().
- *
- * \return Number of the buffer.
- */
- static int_fast8_t At45dibAllocate(NUTSERIALFLASH * sfi, sf_unit_t pgn)
- {
- int_fast8_t b;
- AT45DIB *at;
- at = (AT45DIB *) sfi->sf_info;
- for (;;) {
- /*
- ** Check, if the page is already loaded.
- */
- for (b = 0; b < 2; b++) {
- if (at->dib_page[b] == pgn) {
- at->dib_locks[b]++;
- at->flags[b] |= AT45DIB_FDIRTY;
- return b;
- }
- }
- /*
- ** Search for a clean unlocked buffer and load the page.
- */
- for (b = 0; b < 2; b++) {
- if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == 0) {
- int rc;
- uint32_t pga = pgn;
- pga <<= at->dib_pshft;
- at->dib_locks[b]++;
- if (b) {
- rc = At45dibCommand(sfi->sf_node, DFCMD_BUF2_LOAD_PAGE, pga, 4);
- } else {
- rc = At45dibCommand(sfi->sf_node, DFCMD_BUF1_LOAD_PAGE, pga, 4);
- }
- if (rc == 0) {
- rc = At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS);
- }
- if (rc == 0) {
- at->dib_page[b] = pgn;
- at->flags[b] |= AT45DIB_FDIRTY;
- return b;
- }
- At45dibRelease(sfi, b);
- return -1;
- }
- }
- /*
- ** No clean buffer. If not locked, save one of them.
- */
- for (b = 0; b < 2; b++) {
- if (at->dib_locks[b] == 0 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
- if (At45dibFlash(sfi, b)) {
- return -1;
- }
- break;
- }
- }
- /*
- ** All buffers are locked. Wait for an unlock event.
- */
- if (b >= 2) {
- NutEventWait(&at->dib_lque, 0);
- }
- }
- }
- /*!
- * \brief Initialize the interface.
- *
- * \param sfi Specifies the serial flash interface.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibInit(NUTSERIALFLASH * sfi)
- {
- int_fast8_t b;
- uint8_t sr;
- int_fast8_t i;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_node != NULL);
- sr = At45dibStatus(sfi->sf_node);
- sr &= AT45D_STATUS_DENSITY | AT45D_STATUS_PAGE_SIZE;
- for (i = at45d_known_types; --i >= 0;) {
- if (sr == at45d_info[i].at45d_srval) {
- /* Known DataFlash type. */
- AT45DIB *at = calloc(1, sizeof(AT45DIB));
- if (at == NULL) {
- return -1;
- }
- at->dib_pshft = at45d_info[i].at45d_pshft;
- for (b = 0; b < 2; b++) {
- at->dib_page[b] = SERIALFLASH_MAX_UNITS;
- }
- sfi->sf_info = (void *) at;
- sfi->sf_units = (sf_unit_t) at45d_info[i].at45d_pages;
- sfi->sf_unit_size = at45d_info[i].at45d_psize;
- #ifdef AT45D_CRC_PAGE
- sfi->sf_unit_size -= 2;
- #endif
- return 0;
- }
- }
- /* Unknown DataFlash type. */
- return -1;
- }
- /*!
- * \brief Release the interface.
- *
- * \param sfi Specifies the serial flash interface.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static void SpiAt45dibExit(NUTSERIALFLASH * sfi)
- {
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_info != NULL);
- free(sfi->sf_info);
- }
- /*!
- * \brief Verify CRC of consecutive pages.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn First page to verify.
- * \param cnt Number of consecutive pages to verify.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibCheck(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
- {
- int rc = 0;
- #ifdef AT45D_CRC_PAGE
- AT45DIB *at;
- int_fast8_t b;
- uint16_t crc16;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_info != NULL);
- NUTASSERT(pgn + cnt <= sfi->sf_units);
- NUTASSERT(cnt >= 0);
- at = (AT45DIB *) sfi->sf_info;
- while (cnt--) {
- /* If this page is buffered, then read from the buffer. */
- for (b = 0; b < 2 && at->dib_page[b] != pgn + cnt; b++);
- if (b < 2) {
- At45dibChecksum(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, 0, 5, &crc16, sfi->sf_unit_size + 2);
- }
- /* If not buffered, then directly read from the flash. */
- else {
- uint32_t pga = pgn + cnt;
- pga <<= at->dib_pshft;
- At45dibChecksum(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, &crc16, sfi->sf_unit_size + 2);
- }
- if (crc16 != 0xFFFF) {
- rc = -1;
- break;
- }
- }
- #endif
- return rc;
- }
- /*!
- * \brief Read data from a given page.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn Page to read from.
- * \param off Read offset into the page. If negative, the offset counts
- * from the end of the page.
- * \param data Pointer to the buffer that receives the data.
- * \param len Number of data bytes to read.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibRead(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, void *data, int len)
- {
- int rc = 0;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_info != NULL);
- NUTASSERT(pgn < sfi->sf_units);
- NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
- NUTASSERT(data != NULL);
- NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
- if (len) {
- int_fast8_t b;
- AT45DIB *at = (AT45DIB *) sfi->sf_info;
- /* Normalize the offset. */
- if (off < 0) {
- off += sfi->sf_unit_size;
- }
- NUTASSERT(off + len <= (int) sfi->sf_unit_size);
- /* If this page is buffered, then read from the buffer. */
- for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
- if (b < 2) {
- rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, NULL, data, len);
- }
- /* If not buffered, then directly read from the flash. */
- else {
- uint32_t pga = pgn;
- pga <<= at->dib_pshft;
- pga |= off;
- rc = At45dibTransfer(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, NULL, data, len);
- }
- }
- return rc;
- }
- /*!
- * \brief Compare data with the contents of a given page.
- *
- * This function allows to directly compare the contents of a page. It is
- * not required to load the page.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn Page to compare.
- * \param off Offset into the page. If negative, the offset counts from
- * the end of the page.
- * \param data Pointer to the data to compare.
- * \param len Number of data bytes to compare.
- *
- * \return 0 on success or -1 if the contents differs or in case of an error.
- */
- static int SpiAt45dibCompare(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
- {
- int rc = 0;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_info != NULL);
- NUTASSERT(pgn < sfi->sf_units);
- NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
- NUTASSERT(data != NULL);
- NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
- if (len) {
- int_fast8_t b;
- AT45DIB *at = (AT45DIB *) sfi->sf_info;
- /* Normalize the offset. */
- if (off < 0) {
- off += sfi->sf_unit_size;
- }
- NUTASSERT(off + len <= (int) sfi->sf_unit_size);
- /* If this page is buffered, then read from the buffer. */
- for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
- if (b < 2) {
- rc = At45dibCompare(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, off, 5, data, len);
- }
- /* If not buffered, then directly read from the flash. */
- else {
- uint32_t pga = pgn;
- pga <<= at->dib_pshft;
- pga |= off;
- rc = At45dibCompare(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, data, len);
- }
- }
- return rc;
- }
- /*!
- * \brief Return the number of bytes used in a given page.
- *
- * This function allows to directly compare the contents of a page. It is
- * not required to load the page.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn Page to check.
- * \param skip Number of bytes to ingore. May be a positive or negative
- * value, to ingnore bytes at the beginning or at the end, resp.
- *
- * \return 0 on success or -1 if the contents differs or in case of an error.
- */
- static int SpiAt45dibUsed(NUTSERIALFLASH * sfi, sf_unit_t pgn, int skip)
- {
- int rc;
- int len;
- AT45DIB *at;
- int_fast8_t b;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(pgn < sfi->sf_units);
- NUTASSERT(skip <= (int) sfi->sf_unit_size);
- at = (AT45DIB *) sfi->sf_info;
- /* Determine length and offset. */
- len = (int) sfi->sf_unit_size;
- if (skip < 0) {
- len += skip;
- skip = 0;
- } else {
- len -= skip;
- }
- /* If this page is buffered, then read from the buffer. */
- for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
- if (b < 2) {
- rc = At45dibUsed(sfi->sf_node, b ? DFCMD_BUF2_READ : DFCMD_BUF1_READ, skip, 5, len);
- }
- /* If not buffered, then directly read from the flash. */
- else {
- uint32_t pga = pgn;
- pga <<= at->dib_pshft;
- pga |= skip;
- rc = At45dibUsed(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, len);
- }
- return rc;
- }
- /*!
- * \brief Write data to a given page.
- *
- * Loads the page into an internal RAM buffer and replaces the contents
- * with the given data. The buffer will be marked dirty, but not written
- * back. See SpiAt45dibCommit().
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn Page to write to.
- * \param off Offset into the page, where the new data should be placed.
- * If negative, the offset counts from the end of the page.
- * \param data Pointer to the data to write.
- * \param len Number of data bytes to write.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibWrite(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, const void *data, int len)
- {
- int rc = 0;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(pgn < sfi->sf_units);
- NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
- NUTASSERT(len == 0 || data != NULL);
- NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
- if (len) {
- int_fast8_t b;
- /* Normalize the offset. */
- if (off < 0) {
- off += sfi->sf_unit_size;
- }
- NUTASSERT(off + len <= (int) sfi->sf_unit_size);
- /* Load the page. */
- b = At45dibAllocate(sfi, pgn);
- if (b < 0) {
- return -1;
- }
- /* Transfer the data and release the page. */
- rc = At45dibTransfer(sfi->sf_node, b ? DFCMD_BUF2_WRITE : DFCMD_BUF1_WRITE, off, 4, data, NULL, len);
- At45dibRelease(sfi, b);
- }
- return rc;
- }
- /*!
- * \brief Copy page.
- *
- * Loads the source page into an internal RAM buffer und designates the
- * buffer to the destination page. The buffer will be marked dirty, but
- * not written back. See SpiAt45dibCommit().
- *
- * \param sfi Specifies the serial flash interface.
- * \param spg Source page to copy from.
- * \param dpg Destination page to copy to.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibCopy(NUTSERIALFLASH * sfi, sf_unit_t spg, sf_unit_t dpg)
- {
- int_fast8_t b;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_info != NULL);
- NUTASSERT(spg < sfi->sf_units);
- NUTASSERT(dpg < sfi->sf_units);
- /* If source and destination page numbers are equal, just load it. */
- if (spg == dpg) {
- b = At45dibAllocate(sfi, spg);
- } else {
- AT45DIB *at = (AT45DIB *) sfi->sf_info;
- /* Invalidate any buffered destination. */
- for (b = 0; b < 2; b++) {
- if (at->dib_page[b] == dpg) {
- /* But do not touch locked pages. */
- if (at->dib_locks[b]) {
- return -1;
- }
- at->dib_page[b] = SERIALFLASH_MAX_UNITS;
- }
- }
- /* Try to load the source page and make it the destination page. */
- b = At45dibAllocate(sfi, spg);
- if (b >= 0) {
- at->dib_page[b] = dpg;
- }
- }
- if (b >= 0) {
- At45dibRelease(sfi, b);
- return 0;
- }
- return -1;
- }
- /*!
- * \brief Commit page.
- *
- * If the contents of the given page is in one of the internal RAM
- * buffers and if the contents had been changed since the last page
- * load, then the buffer is written back to the flash.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn Page to commit.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibCommit(NUTSERIALFLASH * sfi, sf_unit_t pgn)
- {
- int rc = 0;
- int_fast8_t b;
- AT45DIB *at;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(pgn < sfi->sf_units);
- at = (AT45DIB *) sfi->sf_info;
- for (b = 0; b < 2 && at->dib_page[b] != pgn; b++);
- if (b < 2 && (at->flags[b] & AT45DIB_FDIRTY) == AT45DIB_FDIRTY) {
- rc = At45dibFlash(sfi, b);
- }
- return rc;
- }
- /*!
- * \brief Erase consecutive pages.
- *
- * \param sfi Specifies the serial flash interface.
- * \param pgn First page to erase.
- * \param cnt Number of consecutive pages to erase.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int SpiAt45dibErase(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
- {
- int_fast8_t b;
- AT45DIB *at;
- /* Sanity checks. */
- NUTASSERT(sfi != NULL);
- NUTASSERT(pgn + cnt <= sfi->sf_units);
- NUTASSERT(cnt >= 0);
- at = (AT45DIB *) sfi->sf_info;
- /* Invalidate any buffered page. */
- for (b = 0; b < 2; b++) {
- if (at->dib_page[b] >= pgn && at->dib_page[b] < pgn + cnt) {
- at->dib_page[b] = SERIALFLASH_MAX_UNITS;
- at->flags[b] &= ~AT45DIB_FDIRTY;
- }
- }
- while (cnt--) {
- uint32_t pga = pgn + cnt;
- pga <<= at->dib_pshft;
- if (At45dibCommand(sfi->sf_node, DFCMD_PAGE_ERASE, pga, 4)) {
- return -1;
- }
- if (At45dibWaitReady(sfi->sf_node, AT45_WRITE_POLLS)) {
- return -1;
- }
- }
- return 0;
- }
- #ifndef SPI_RATE_AT45D0
- #define SPI_RATE_AT45D0 33000000
- #endif
- #ifndef SPI_MODE_AT45D0
- #ifdef SPI_CSHIGH_AT45D0
- #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
- #else
- #define SPI_MODE_AT45D0 SPI_MODE_3
- #endif
- #elif defined(SPI_CSHIGH_AT45D0)
- /* This is a tricky problem. Originally we didn't provide mode settings
- ** in the Configurator, but used mode 3 only. After experiencing problems
- ** with mode switching on the EIR, we need to set mode 0 for that board,
- ** which spoils our chip select polarity setting. */
- #if SPI_MODE_AT45D0 == SPI_MODE_0
- #undef SPI_MODE_AT45D0
- #define SPI_MODE_AT45D0 (SPI_MODE_0 | SPI_MODE_CSHIGH)
- #elif SPI_MODE_AT45D0 == SPI_MODE_3
- #undef SPI_MODE_AT45D0
- #define SPI_MODE_AT45D0 (SPI_MODE_3 | SPI_MODE_CSHIGH)
- #endif
- #endif /* SPI_MODE_AT45D0 */
- /*!
- * \brief First AT45D DataFlash SPI node implementation structure.
- */
- static NUTSPINODE spiNode0 = {
- NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */
- NULL, /*!< \brief Pointer to device driver specific settings, node_stat. */
- SPI_RATE_AT45D0, /*!< \brief Initial clock rate, node_rate. */
- SPI_MODE_AT45D0, /*!< \brief Initial mode, node_mode. */
- 8, /*!< \brief Initial data bits, node_bits. */
- 0 /*!< \brief Chip select, node_cs. */
- };
- #ifndef MOUNT_OFFSET_AT45DIB0
- #ifdef MOUNT_OFFSET_AT45D0
- #define MOUNT_OFFSET_AT45DIB0 MOUNT_OFFSET_AT45D0
- #else
- #define MOUNT_OFFSET_AT45DIB0 0
- #endif
- #endif
- #ifndef MOUNT_TOP_RESERVE_AT45DIB0
- #ifdef MOUNT_TOP_RESERVE_AT45D0
- #define MOUNT_TOP_RESERVE_AT45DIB0 MOUNT_TOP_RESERVE_AT45D0
- #else
- #define MOUNT_TOP_RESERVE_AT45DIB0 1
- #endif
- #endif
- /*!
- * \brief First AT45D DataFlash interface implementation structure.
- */
- NUTSERIALFLASH flashAt45dib0 = {
- &spiNode0, /*!< \brief Pointer to the SPI node structure, sf_node. */
- NULL, /*!< \brief Pointer to a local information structure, sf_info. */
- 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
- 0, /*!< \brief Total number of units, sf_units. */
- MOUNT_OFFSET_AT45DIB0, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
- MOUNT_TOP_RESERVE_AT45DIB0, /*!< \brief Reserved units at the top, sf_rsvtop. */
- SpiAt45dibInit, /*!< \brief Flash device initialization function, sf_init. */
- SpiAt45dibExit, /*!< \brief Flash device release function, sf_exit. */
- SpiAt45dibCheck, /*!< \brief Unit validation function, sf_check. */
- SpiAt45dibRead, /*!< \brief Data read function, sf_read. */
- SpiAt45dibCompare, /*!< \brief Data compare function, sf_compare. */
- SpiAt45dibUsed, /*!< \brief Unit usage query function, sf_used. */
- SpiAt45dibWrite, /*!< \brief Data write function, sf_write. */
- SpiAt45dibCopy, /*!< \brief Unit copy function, sf_copy. */
- SpiAt45dibCommit, /*!< \brief Unit commit function, sf_commit. */
- SpiAt45dibErase /*!< \brief Unit erase function, sf_erase. */
- };
- #ifndef SPI_RATE_AT45D1
- #define SPI_RATE_AT45D1 33000000
- #endif
- #ifndef SPI_MODE_AT45D1
- #ifdef SPI_CSHIGH_AT45D1
- #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
- #else
- #define SPI_MODE_AT45D1 SPI_MODE_3
- #endif
- #elif defined(SPI_CSHIGH_AT45D1)
- /* Same problem problem as above. */
- #if SPI_MODE_AT45D1 == SPI_MODE_0
- #undef SPI_MODE_AT45D1
- #define SPI_MODE_AT45D1 (SPI_MODE_0 | SPI_MODE_CSHIGH)
- #elif SPI_MODE_AT45D1 == SPI_MODE_3
- #undef SPI_MODE_AT45D1
- #define SPI_MODE_AT45D1 (SPI_MODE_3 | SPI_MODE_CSHIGH)
- #endif
- #endif /* SPI_MODE_AT45D0 */
- /*!
- * \brief Second AT45D DataFlash SPI node implementation structure.
- */
- static NUTSPINODE spiNode1 = {
- NULL, /*!< \brief Pointer to the bus controller driver, node_bus. */
- NULL, /*!< \brief Pointer to device driver specific settings, node_stat. */
- SPI_RATE_AT45D1, /*!< \brief Initial clock rate, node_rate. */
- SPI_MODE_AT45D1, /*!< \brief Initial mode, node_mode. */
- 8, /*!< \brief Initial data bits, node_bits. */
- 1 /*!< \brief Chip select, node_cs. */
- };
- #ifndef MOUNT_OFFSET_AT45DIB1
- #ifdef MOUNT_OFFSET_AT45D1
- #define MOUNT_OFFSET_AT45DIB1 MOUNT_OFFSET_AT45D1
- #else
- #define MOUNT_OFFSET_AT45DIB1 0
- #endif
- #endif
- #ifndef MOUNT_TOP_RESERVE_AT45DIB1
- #ifdef MOUNT_TOP_RESERVE_AT45D1
- #define MOUNT_TOP_RESERVE_AT45DIB1 MOUNT_TOP_RESERVE_AT45D1
- #else
- #define MOUNT_TOP_RESERVE_AT45DIB1 1
- #endif
- #endif
- /*!
- * \brief Second AT45D DataFlash interface implementation structure.
- */
- NUTSERIALFLASH flashAt45dib1 = {
- &spiNode1, /*!< \brief Pointer to the SPI node structure, sf_node. */
- NULL, /*!< \brief Pointer to a local information structure, sf_info. */
- 0, /*!< \brief Size of an erase/write unit, sf_unit_size. */
- 0, /*!< \brief Total number of units, sf_units. */
- MOUNT_OFFSET_AT45DIB1, /*!< \brief Reserved units at the bottom, sf_rsvbot. */
- MOUNT_TOP_RESERVE_AT45DIB1, /*!< \brief Reserved units at the top, sf_rsvtop. */
- SpiAt45dibInit, /*!< \brief Flash device initialization function, sf_init. */
- SpiAt45dibExit, /*!< \brief Flash device release function, sf_exit. */
- SpiAt45dibCheck, /*!< \brief Unit validation function, sf_check. */
- SpiAt45dibRead, /*!< \brief Data read function, sf_read. */
- SpiAt45dibCompare, /*!< \brief Data compare function, sf_compare. */
- SpiAt45dibUsed, /*!< \brief Unit usage query function, sf_used. */
- SpiAt45dibWrite, /*!< \brief Data write function, sf_write. */
- SpiAt45dibCopy, /*!< \brief Unit copy function, sf_copy. */
- SpiAt45dibCommit, /*!< \brief Unit commit function, sf_commit. */
- SpiAt45dibErase /*!< \brief Unit erase function, sf_erase. */
- };
- /*@}*/
|