| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /* ========================================================================
- * [PROJECT] SIR
- * [MODULE] Flash
- * [TITLE] Routines for Atmel AT45 serial dataflash memory chips.
- * [FILE] flash.c
- * [VSN] 1.0
- * [CREATED] 11042007
- * [LASTCHNGD] 11042007
- * [COPYRIGHT] Copyright (C) STREAMIT BV 2010
- * [PURPOSE] contains all interface- and low-level routines to
- * read/write/delete blocks in the serial DataFlash (AT45DBXX)
- * ======================================================================== */
- #define LOG_MODULE LOG_FLASH_MODULE
- //#include <stdio.h>
- #include <cfg/os.h>
- #include <cfg/memory.h>
- #include <sys/timer.h>
- #include <string.h>
- #include <stdlib.h>
- #include "typedefs.h"
- #include "flash.h"
- #include "portio.h"
- #include "log.h"
- #include "spidrv.h"
- /*-------------------------------------------------------------------------*/
- /* local defines */
- /*-------------------------------------------------------------------------*/
- #ifndef MAX_AT45_CMDLEN
- #define MAX_AT45_CMDLEN 8
- #endif
- #ifndef AT45_ERASE_WAIT
- #define AT45_ERASE_WAIT 3000
- #endif
- #ifndef AT45_CHIP_ERASE_WAIT
- #define AT45_CHIP_ERASE_WAIT 50000
- #endif
- #ifndef AT45_WRITE_POLLS
- #define AT45_WRITE_POLLS 1000
- #endif
- #define DFCMD_READ_PAGE 0xD2 /* Read main memory page. */
- #define DFCMD_READ_STATUS 0xD7 /* Read status register. */
- #define DFCMD_CONT_READ 0xE8 /* Continuos read. */
- #define DFCMD_PAGE_ERASE 0x81 /* Page erase. */
- #define DFCMD_BUF1_WRITE 0x84 /* Buffer 1 write. */
- #define DFCMD_BUF1_FLASH 0x83 /* Buffer 1 flash with page erase. */
- /*
- * \brief last page of flash (264 bytes) can be dedicated for parameter storage
- * Special routines are provided for that goal but can be disabled here to save
- * codespace (about 360 bytes of code for GCC)
- */
- //#define USE_FLASH_PARAM_PAGE
- /*-------------------------------------------------------------------------*/
- /* typedefs & structs */
- /*-------------------------------------------------------------------------*/
- /*!
- * \brief Known device type entry.
- */
- typedef struct _AT45_DEVTAB
- {
- u_long devt_pages;
- u_int devt_pagsiz;
- u_int devt_offs;
- u_char devt_srmsk;
- u_char devt_srval;
- } AT45_DEVTAB;
- /*!
- * \brief Active device entry.
- */
- typedef struct _AT45DB_DCB
- {
- AT45_DEVTAB *dcb_devt;
- u_char dcb_cmdbuf[MAX_AT45_CMDLEN];
- } AT45DB_DCB;
- /*!
- * \brief Table of known Dataflash types.
- */
- AT45_DEVTAB at45_devt[] = {
- {512, 264, 9, 0x3C, 0x0C}, // AT45DB011B - 128kB
- {1025, 264, 9, 0x3C, 0x14}, // AT45DB021B - 256kB
- {2048, 264, 9, 0x3C, 0x1C}, // AT45DB041B - 512kB
- {4096, 264, 9, 0x3C, 0x24}, // AT45DB081B - 1MB
- {4096, 528, 10, 0x3C, 0x2C}, // AT45DB0161B - 2MB
- {8192, 528, 10, 0x3C, 0x34}, // AT45DB0321B - 4MB
- {8192, 1056, 11, 0x38, 0x38}, // AT45DB0642 - 8MB
- {0, 0, 0, 0, 0} // End of table
- };
- /*-------------------------------------------------------------------------*/
- /* local variable definitions */
- /*-------------------------------------------------------------------------*/
- /*!
- * \brief Table of active devices.
- */
- static AT45DB_DCB dcbtab;
- /*-------------------------------------------------------------------------*/
- /* local routines (prototyping) */
- /*-------------------------------------------------------------------------*/
- static int At45dbTransfer(CONST void *txbuf, void *rxbuf, int xlen, CONST void *txnbuf, void *rxnbuf, int xnlen);
- /*!
- * \addtogroup SerialFlash
- */
- /*@{*/
- /*-------------------------------------------------------------------------*/
- /* start of code */
- /*-------------------------------------------------------------------------*/
- /*!
- * \brief mid-level SPI-interface routine
- *
- * This routine handles sending a command an reading back the reply in one routine
- * It will perform 'xlen' + 'xnlen' SPI-byte cycles. During the 'xlen' cycles, data in 'txbuf'
- * is sent using the SPI, and each resulting byte is stored in 'rxbuf'. Then it starts with
- * the 'xnlen' cycles whereby the contents of the 'txnbuf' are sent using the SPI.
- * Each resulting byte then is stored in 'rxnbuf'
- */
- // cb, cb, len, tdata, rdata, datalen
- static int At45dbTransfer(CONST void *txbuf, void *rxbuf, int xlen, CONST void *txnbuf, void *rxnbuf, int xnlen)
- { int i;
- u_char *ptTxbuf, *ptRxbuf;
- SPIselect(SPI_DEV_FLASH);
- ptTxbuf=(u_char*)txbuf;
- ptRxbuf=(u_char*)rxbuf;
- /*
- * send command, store the bytes that were read the same time
- */
- for (i=0; i<xlen; ++i)
- {
- *ptRxbuf++=SPItransferByte(*ptTxbuf++);
- }
- ptTxbuf=(u_char*)txnbuf;
- ptRxbuf=(u_char*)rxnbuf;
- /*
- * send dummy data, store the bytes that were read the same time
- */
- for (i=0; i<xnlen; ++i)
- {
- *ptRxbuf++=SPItransferByte(*ptTxbuf++);
- }
- SPIdeselect();
- return(0); // always...
- }
- /*!
- * \brief send a command to the AT45dbXX
- *
- */
- int At45dbSendCmd(u_char op, u_long parm, int len, CONST void *tdata, void *rdata, int datalen)
- {
- u_char *cb = dcbtab.dcb_cmdbuf;
- if (len > MAX_AT45_CMDLEN)
- {
- return (-1);
- }
- memset(cb, 0, len);
- cb[0] = op;
- if (parm)
- {
- cb[1] = (u_char) (parm >> 16);
- cb[2] = (u_char) (parm >> 8);
- cb[3] = (u_char) parm;
- }
- return (At45dbTransfer(cb, cb, len, tdata, rdata, datalen));
- }
- /*!
- * \brief read status
- *
- */
- u_char At45dbGetStatus()
- {
- u_char buf[2] = { DFCMD_READ_STATUS, 0xFF};
- if (At45dbTransfer(buf, buf, 2, NULL, NULL, 0))
- {
- return(u_char) - 1;
- }
- return (buf[1]);
- }
- /*!
- * \brief Wait until flash memory cycle finished.
- *
- * \return 0 on success or -1 in case of an error.
- */
- int At45dbWaitReady(u_long tmo, int poll)
- {
- u_char sr;
- while (((sr = At45dbGetStatus()) & 0x80) == 0)
- {
- if (!poll)
- {
- NutSleep(1);
- }
- if (tmo-- == 0)
- {
- return (-1);
- }
- }
- return (0);
- }
- /*!
- * \brief runtime detection of serial flash device
- */
- int At45dbInit()
- {
- u_char sr;
- u_char i;
- At45dbGetStatus();
- sr = At45dbGetStatus();
- for (i=0; at45_devt[i].devt_pages; i++)
- {
- if ((sr & at45_devt[i].devt_srmsk) == at45_devt[i].devt_srval)
- {
- dcbtab.dcb_devt = &at45_devt[i];
- break;
- }
- }
- return (i);
- }
- /*!
- * \brief Erase sector at the specified offset.
- */
- int At45dbPageErase(u_int pgn)
- {
- return (At45dbSendCmd(DFCMD_PAGE_ERASE, pgn, 4, NULL, NULL, 0));
- }
- /*!
- * \brief Erase entire flash memory chip.
- */
- int At45dbChipErase(void)
- {
- return (-1);
- }
- /*!
- * \brief Read data from flash memory.
- *
- * \param pgn Page number to read, starting at 0.
- * \param data Points to a buffer that receives the data.
- * \param len Number of bytes to read.
- *
- * \return 0 on success or -1 in case of an error.
- */
- int At45dbPageRead(u_long pgn, void *data, u_int len)
- {
- pgn <<= dcbtab.dcb_devt->devt_offs;
- return (At45dbSendCmd(DFCMD_CONT_READ, pgn, 8, data, data, len));
- }
- /*!
- * \brief Write data into flash memory.
- *
- * The related sector must have been erased before calling this function.
- *
- * \param pgn Start location within the chip, starting at 0.
- * \param data Points to a buffer that contains the bytes to be written.
- * \param len Number of bytes to write.
- *
- * \return 0 on success or -1 in case of an error.
- */
- int At45dbPageWrite(u_long pgn, CONST void *data, u_int len)
- {
- int rc = -1;
- void *rp;
- if ((rp = malloc(len)) != NULL)
- {
- /* Copy data to dataflash RAM buffer. */
- if (At45dbSendCmd(DFCMD_BUF1_WRITE, 0, 4, data, rp, len) == 0)
- {
- /* Flash RAM buffer. */
- pgn <<= dcbtab.dcb_devt->devt_offs;
- if (At45dbSendCmd(DFCMD_BUF1_FLASH, pgn, 4, NULL, NULL, 0) == 0)
- {
- rc = At45dbWaitReady(AT45_WRITE_POLLS, 1);
- }
- }
- free(rp);
- }
- return (rc);
- }
- #ifdef USE_FLASH_PARAM_PAGE
- u_long At45dbParamPage(void)
- {
- #ifdef AT45_CONF_PAGE
- return (AT45_CONF_PAGE);
- #else
- return (dcbtab.dcb_devt->devt_pages - 1);
- #endif
- }
- int At45dbParamSize(void)
- {
- int rc;
- #ifdef AT45_CONF_SIZE
- rc = AT45_CONF_SIZE;
- #else
- rc = dcbtab.dcb_devt->devt_pagsiz;
- #endif
- return (rc);
- }
- /*!
- * \brief Load configuration parameters from flash memory.
- *
- * \param pos Start location within configuration sector.
- * \param data Points to a buffer that receives the contents.
- * \param len Number of bytes to read.
- *
- * \return Always 0.
- */
- int At45dbParamRead(u_int pos, void *data, u_int len)
- {
- int rc = -1;
- u_char *buff;
- int csize = At45dbParamSize();
- u_long cpage = At45dbParamPage();
- /* Load the complete configuration area. */
- if (csize > len && (buff = malloc(csize)) != NULL)
- {
- rc = At45dbPageRead(cpage, buff, csize);
- /* Copy requested contents to caller's buffer. */
- memcpy(data, buff + pos, len);
- free(buff);
- }
- return (rc);
- }
- /*!
- * \brief Store configuration parameters in flash memory.
- *
- * \param pos Start location within configuration sector.
- * \param data Points to a buffer that contains the bytes to store.
- * \param len Number of bytes to store.
- *
- * \return 0 on success or -1 in case of an error.
- */
- int At45dbParamWrite(u_int pos, CONST void *data, u_int len)
- {
- int rc = -1;
- u_char *buff;
- int csize = At45dbParamSize();
- u_long cpage = At45dbParamPage();
- /* Load the complete configuration area. */
- if (csize > len && (buff = malloc(csize)) != NULL)
- {
- rc = At45dbPageRead(cpage, buff, csize);
- /* Compare old with new contents. */
- if (memcmp(buff + pos, data, len))
- {
- /* New contents differs. Copy it into the sector buffer. */
- memcpy(buff + pos, data, len);
- /* Erase sector and write new data. */
- rc = At45dbPageWrite(cpage, buff, csize);
- }
- free(buff);
- }
- return (rc);
- }
- #endif // USE_FLASH_PARAM_PAGE
|