/* ======================================================================== * [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 #include #include #include #include #include #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 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