X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=drv%2Fdataflash.c;h=d9392f9aadb6db27088b7c5206417eaa54982c3a;hb=2535cb94ec2183791128f8bbd109ca69a960cf78;hp=7dda5db06584bea0b74507fb3ebf32923e56baef;hpb=26bbacf8a1890f86a1cd3502ce335bfd0296fb75;p=bertos.git diff --git a/drv/dataflash.c b/drv/dataflash.c index 7dda5db0..d9392f9a 100644 --- a/drv/dataflash.c +++ b/drv/dataflash.c @@ -30,44 +30,86 @@ * * --> * - * \brief Function library for AT45DB081D Flash memory. + * \brief Function library for AT45DBXX Data Flash memory. * * - * \version $Id: dflash.c 15379 2007-03-28 15:46:09Z asterix $ + * \version $Id: dataflash.c 20677 2008-02-19 14:29:52Z batt $ * \author Daniele Basile + * \author Francesco Sacchi */ - +#include "dataflash.h" #include -#include #include #include #include #include -#include -#include +#include + +#if CONFIG_KERNEL +#include +#endif -#include "hw_spi.h" +#include /** - * Send a generic command to data flash memory. - * This function send only 4 byte, for opcode, page address and + * Array used to describe dataflash memory devices currently supported. + */ +static const DataflashInfo mem_info[] = +{ + { + /* AT45DB041B */ + .density_id = 0x07, + .page_size = 264, + .page_bits = 9, + .page_cnt = 2048, + .read_cmd = DFO_READ_FLASH_MEM_BYTE_B, + }, + { + /* AT45DB081D */ + .density_id = 0x09, + .page_size = 264, + .page_bits = 9, + .page_cnt = 4096, + .read_cmd = DFO_READ_FLASH_MEM_BYTE_D, + }, + { + /* AT45DB161D */ + .density_id = 0x0B, + .page_size = 528, + .page_bits = 10, + .page_cnt = 4096, + .read_cmd = DFO_READ_FLASH_MEM_BYTE_D, + }, + /* Add other memories here */ +}; + +STATIC_ASSERT(countof(mem_info) == DFT_CNT); + +/** + * Macro that toggle CS of dataflash. + * \note This is equivalent to fd->setCS(false) immediately followed by fd->setCS(true). + */ +INLINE void CS_TOGGLE(KFileDataflash *fd) +{ + fd->setCS(false); + fd->setCS(true); +} + +/** + * Send a generic command to dataflash memory. + * This function send only 4 byte: opcode, page address and * byte address. */ -static void send_cmd(dflashAddr_t page_addr, dflashAddr_t byte_addr, DFlashOpcode opcode) +static void send_cmd(KFileDataflash *fd, dataflash_page_t page_addr, dataflash_offset_t byte_addr, DataFlashOpcode opcode) { /* * Make sure to toggle CS signal in order, - * and reset dflash command decoder. - * - * Note: - * #define CS_TOGGLE() \ - * CS_DISABLE(); \ - * CS_ENABLE(); \ + * and reset dataflash command decoder. */ - CS_TOGGLE(); + CS_TOGGLE(fd); /* @@ -82,107 +124,61 @@ static void send_cmd(dflashAddr_t page_addr, dflashAddr_t byte_addr, DFlashOpcod * hight part of address byte in third byte togheter low par of page * address. * - * \{ */ /* * Send opcode. */ - spi_sendRecv(opcode); + kfile_putc(opcode, fd->channel); /* * Send page address. - * \{ */ - spi_sendRecv((uint8_t)(page_addr >> (16 - DFLASH_PAGE_ADDRESS_BIT))); - spi_sendRecv((uint8_t)((page_addr << (DFLASH_PAGE_ADDRESS_BIT - 8)) + (byte_addr >> 8))); - /*\}*/ + kfile_putc((uint8_t)(page_addr >> (16 - mem_info[fd->dev].page_bits)), fd->channel); + kfile_putc((uint8_t)((page_addr << (mem_info[fd->dev].page_bits - 8)) + (byte_addr >> 8)), fd->channel); /* * Send byte page address. */ - spi_sendRecv((uint8_t)byte_addr); - - /* \} */ - + kfile_putc((uint8_t)byte_addr, fd->channel); } /** * Reset dataflash memory function. * - * This function reset data flash memory + * If \a fd->setReset function is not NULL, + * this function resets data flash memory * with one pulse reset long about 10usec. * */ -static void dflash_reset(void) -{ - CS_ENABLE(); - RESET_ENABLE(); - timer_delayHp(us_to_hptime(RESET_PULSE_WIDTH)); - CS_DISABLE(); - RESET_DISABLE(); - timer_delayHp(us_to_hptime(RESET_PULSE_WIDTH)); -} - -/** - * dflash init function. - * This function initialize a micro pin and - * SPI driver, and test if data flash memory - * density is the same wich define in dflash.h. - */ -MOD_DEFINE(dflash); -static bool dflash_pin_init(void) +static void dataflash_reset(KFileDataflash *fd) { - uint8_t stat; - - MOD_CHECK(spi); - - RESET_DISABLE(); - WRITE_ENABLE(); //pilot wp pin. - - RESET_OUT(); - WP_OUT(); - - dflash_reset(); - - stat = dflash_stat(); - - MOD_INIT(dflash); - - /* - * 2,3,4,5 bit of 1 byte status register - * indicate a device density of dflash memory - * (see datasheet for more detail.) - */ - GET_ID_DESITY_DEVICE(stat); - - if(stat == DFLASH_ID_DEVICE_DENSITY) - return true; - else - return false; + fd->setCS(false); + if (fd->setReset) + { + fd->setReset(true); + timer_delayHp(us_to_hptime(RESET_PULSE_WIDTH)); + fd->setReset(false); + timer_delayHp(us_to_hptime(RESET_PULSE_WIDTH)); + } } /** * Read status register of dataflah memory. - * */ -static uint8_t dflash_stat(void) +static uint8_t dataflash_stat(KFileDataflash *fd) { - uint8_t stat; - /* - * Make sure to toggle CS signal in order, - * and reset dflash command decoder. - * \{ + * Make sure to toggle CS signal + * and reset dataflash command decoder. */ - CS_TOGGLE(); + CS_TOGGLE(fd); - stat = spi_sendRecv(DFO_READ_STATUS); - stat = spi_sendRecv(0x00); + kfile_putc(DFO_READ_STATUS, fd->channel); - return stat; + return kfile_getc(fd->channel); } @@ -191,150 +187,308 @@ static uint8_t dflash_stat(void) * return status register value. * */ -static uint8_t dflash_cmd(dflashAddr_t page_addr, dflashAddr_t byte_addr, DFlashOpcode opcode) +static uint8_t dataflash_cmd(KFileDataflash *fd, dataflash_page_t page_addr, dataflash_offset_t byte_addr, DataFlashOpcode opcode) { + uint8_t stat; - send_cmd(page_addr, byte_addr, opcode); + send_cmd(fd, page_addr, byte_addr, opcode); - CS_DISABLE(); - CS_ENABLE(); + CS_TOGGLE(fd); /* * We chech data flash memory state, and wait until busy-flag - * is hight. + * is high. */ - while(!(dflash_stat() & BUSY_BIT)); + while (!(dataflash_stat(fd) & BUSY_BIT)) + { + #if CONFIG_KERNEL + proc_switch(); + #endif + } - return (dflash_stat()); + stat = dataflash_stat(fd); + kfile_flush(fd->channel); // Flush channel + /* + * Data flash has completed a bus cycle, so disable CS. + */ + fd->setCS(false); + + return stat; } /** - * Read one byte from main data flash memory or buffer data - * flash memory. + * Read \a len bytes from main data flash memory or buffer data + * flash memory, and put it in \a *block. */ -static uint8_t dflash_read_byte(dflashAddr_t page_addr, dflashAddr_t byte_addr, DFlashOpcode opcode) +static void dataflash_readBlock(KFileDataflash *fd, dataflash_page_t page_addr, dataflash_offset_t byte_addr, DataFlashOpcode opcode, uint8_t *block, dataflash_size_t len) { - uint8_t data; + send_cmd(fd, page_addr, byte_addr, opcode); - send_cmd(page_addr, byte_addr, opcode); - -#if CONFIG_DATA_FLASH == AT45DB041B - if(opcode == DFO_READ_FLASH_MEM_BYTE) + if (opcode == DFO_READ_FLASH_MEM_BYTE_B) { /* - * Send 24 don't care bit. - * \{ + * Send 24 don't care bits. */ - spi_sendRecv(0x00); - spi_sendRecv(0x00); - spi_sendRecv(0x00); - /* \} */ - + uint8_t dummy[] = { 0, 0, 0 }; + kfile_write(fd->channel, dummy, sizeof(dummy)); } -#endif - - spi_sendRecv(0x00); //Send 8 don't care bit. - data = spi_sendRecv(0x00); //Read byte. - CS_DISABLE(); - return data; + kfile_putc(0, fd->channel); //Send 8 don't care bit. + kfile_read(fd->channel, block, len); //Read len bytes ad put in block buffer. + kfile_flush(fd->channel); // Flush channel + fd->setCS(false); } + /** - * Read \a len bytes from main data flash memory or buffer data - * flash memory, and put it in \a *block. + * Write \a len bytes in dataflash memory buffer. + * + * \note Is not possible to write directly in dataflash main memory. + * To perform a write in main memory you must first write in dataflash buffer + * memory and then send a command to write the page in main memory. */ -static void dflash_read_block(dflashAddr_t page_addr, dflashAddr_t byte_addr, DFlashOpcode opcode, uint8_t *block, dflashSize_t len) +static void dataflash_writeBlock(KFileDataflash *fd, dataflash_offset_t offset, DataFlashOpcode opcode, const uint8_t *block, dataflash_size_t len) { + ASSERT(offset + len <= mem_info[fd->dev].page_size); - send_cmd(page_addr, byte_addr, opcode); - - if(opcode == DFO_READ_FLASH_MEM_BYTE) - { - /* - * Send 24 don't care bit. - * \{ - */ - spi_sendRecv(0x00); - spi_sendRecv(0x00); - spi_sendRecv(0x00); - /* \} */ - } - - spi_sendRecv(0x00); //Send 8 don't care bit. - spi_read(block, len); //Read len bytes ad put in block buffer. + send_cmd(fd, 0x00, offset, opcode); + kfile_write(fd->channel, block, len); //Write len bytes. + kfile_flush(fd->channel); // Flush channel - CS_DISABLE(); - + fd->setCS(false); } /** - * Write \a len bytes in buffer buffer data flash memory. - * - * \note Isn't possible to write bytes directly in main memory data - * flash. To perform write in main memory you must before write in buffer - * data flash memory, an then send command to write page in main memory. + * Load selct page from dataflash memory to buffer. */ -static void dflash_write_block(dflashAddr_t byte_addr, DFlashOpcode opcode, uint8_t *block, dflashSize_t len) +static void dataflash_loadPage(KFileDataflash *fd, dataflash_page_t page_addr) { + dataflash_cmd(fd, page_addr, 0x00, DFO_MOV_MEM_TO_BUFF1); +} - send_cmd(0x00, byte_addr, opcode); - - spi_write(block, len); //Write len bytes. +/** + * Flush select page (stored in buffer) in data flash main memory page. + */ +static int dataflash_flush(KFile *_fd) +{ + KFileDataflash *fd = KFILEDATAFLASH(_fd); + if (fd->page_dirty) + { + dataflash_cmd(fd, fd->current_page, 0x00, DFO_WRITE_BUFF1_TO_MEM_E); - CS_DISABLE(); + fd->page_dirty = false; + kprintf("Flushing page <%ld>\n", fd->current_page); + } + return 0; } /* Kfile interface section */ /** - * Open data flash file \a fd - * \a name and \a mode are unused, cause flash memory is - * threated like one file. + * Close file \a fd. */ -static bool dflash_open(struct _KFile *fd, UNUSED_ARG(const char *, name), UNUSED_ARG(int, mode)) +static int dataflash_close(struct KFile *_fd) { + dataflash_flush(_fd); + TRACE; + return 0; } /** - * Close file \a fd + * Reopen dataflash file \a fd. */ -static bool dflash_close(UNUSED_ARG(struct _KFile *,fd)) +static KFile *dataflash_reopen(KFile *_fd) { + KFileDataflash *fd = KFILEDATAFLASH(_fd); + dataflash_close(_fd); + + fd->current_page = 0; + fd->fd.seek_pos = 0; + + /* Load selected page from dataflash memory */ + dataflash_loadPage(fd, fd->current_page); + + TRACE; + return &fd->fd; } + /** - * Move \a fd file seek position of \a offset bytes - * from current position. + * Read in \a buf \a size bytes from dataflash memmory. + * + * \note For reading data flash memory, we + * check flag page_dirty, if is true (that mean + * we have written a byte in buffer memory) we + * flush current page in main memory and + * then read from memory, else we read byte + * directly from data flash main memory. + * + * \return the number of bytes read. */ -static int32_t dflash_seek(struct _KFile *fd, int32_t offset, KSeekMode whence) +static size_t dataflash_read(struct KFile *_fd, void *buf, size_t size) { + KFileDataflash *fd = KFILEDATAFLASH(_fd); + + dataflash_offset_t byte_addr; + dataflash_page_t page_addr; + uint8_t *data = (uint8_t *)buf; + + + ASSERT(fd->fd.seek_pos + size <= fd->fd.size); + size = MIN((uint32_t)size, fd->fd.size - fd->fd.seek_pos); + + kprintf("Reading at pos[%lu]\n", fd->fd.seek_pos); + + /* + * We select page and offest from absolute address. + */ + page_addr = fd->fd.seek_pos / mem_info[fd->dev].page_size; + byte_addr = fd->fd.seek_pos % mem_info[fd->dev].page_size; + + kprintf("[page-<%ld>, byte-<%ld>]", page_addr, byte_addr); + + /* + * Flush current page in main memory if + * we had been written a byte in memory + */ + dataflash_flush(&fd->fd); + + /* + * Read byte in main page data flash memory. + */ + dataflash_readBlock(fd, page_addr, byte_addr, mem_info[fd->dev].read_cmd, data, size); + + fd->fd.seek_pos += size; + kprintf("Read %ld bytes\n", size); + + return size; } /** - * Read from file \a fd \a size bytes and put it in buffer \a buf - * \return the number of bytes read. + * Write \a _buf in dataflash memory + * + * \note For writing \a _buf in dataflash memory, we must + * first write in buffer data flash memory. At the end of write, + * we can put page in dataflash main memory. + * If we write in two contiguous pages, we put in main memory current + * page and then reload the page which we want to write. + * + * \return the number of bytes write. */ -static size_t dflash_read(struct _KFile *fd, void *buf, size_t size) +static size_t dataflash_write(struct KFile *_fd, const void *_buf, size_t size) { + KFileDataflash *fd = KFILEDATAFLASH(_fd); + + dataflash_offset_t offset; + dataflash_page_t new_page; + size_t total_write = 0; + + const uint8_t *data = (const uint8_t *) _buf; + + ASSERT(fd->fd.seek_pos + size <= fd->fd.size); + size = MIN((uint32_t)size, fd->fd.size - fd->fd.seek_pos); + + kprintf("Writing at pos[%lu]\n", fd->fd.seek_pos); + + while (size) + { + /* + * We select page and offest from absolute address. + */ + new_page = fd->fd.seek_pos / mem_info[fd->dev].page_size; + offset = fd->fd.seek_pos % mem_info[fd->dev].page_size; + + + size_t wr_len = MIN(size, mem_info[fd->dev].page_size - offset); + + kprintf(" [page-<%ld>, byte-<%ld>]",new_page, offset); + + if (new_page != fd->current_page) + { + /* Flush current page in main memory*/ + dataflash_flush(&fd->fd); + /* Load select page memory from data flash memory*/ + dataflash_loadPage(fd, new_page); + + fd->current_page = new_page; + kprintf(" >> Load page: <%ld> ", new_page); + } + /* + * Write byte in current page, and set true + * page_dirty flag. + */ + dataflash_writeBlock(fd, offset, DFO_WRITE_BUFF1, data, wr_len); + fd->page_dirty = true; + + data += wr_len; + fd->fd.seek_pos += wr_len; + size -= wr_len; + total_write += wr_len; + } + + kprintf("written %lu bytes\n", total_write); + return total_write; } +MOD_DEFINE(dataflash); + /** - * Init data flash memory interface. + * Dataflash init function. + * This function initialize \a fd with SPI channel \a ch and test if data flash memory + * density is the same specified by device \a dev. + * \a setCS is a callback used to set/reset CS line. + * \a setReset is a callback used to set/reset the dataflash (can be NULL if reset is unconnected) + * \return true if ok, false if memory density read from dataflash is not compliant with the + * configured one. */ -void dflash_init(struct _KFile *fd) +bool dataflash_init(KFileDataflash *fd, KFile *ch, DataflashType dev, dataflash_setCS_t *setCS, dataflash_setReset_t *setReset) { - // Set up data flash programming functions. - fd->open = dflash_open; - fd->close = dflash_close; - fd->read = dflash_read; - fd->write = dflash_write; - fd->seek = dflash_seek; - - // Init data flash memory and micro pin. - ASSERT(dflash_pin_init()); -} \ No newline at end of file + uint8_t stat; + + MOD_CHECK(dataflash_hw); + + ASSERT(fd); + ASSERT(ch); + ASSERT(setCS); + ASSERT(dev < DFT_CNT); + + memset(fd, 0, sizeof(*fd)); + DB(fd->fd._type = KFT_DATAFLASH); + fd->dev = dev; + fd->channel = ch; + fd->setReset = setReset; + fd->setCS = setCS; + + // Setup data flash programming functions. + fd->fd.reopen = dataflash_reopen; + fd->fd.close = dataflash_close; + fd->fd.read = dataflash_read; + fd->fd.write = dataflash_write; + fd->fd.seek = kfile_genericSeek; + fd->fd.flush = dataflash_flush; + + dataflash_reset(fd); + + stat = dataflash_stat(fd); + + /* + * 2,3,4,5 bits of 1 byte status register + * indicate a device density of dataflash memory + * (see datasheet for more detail.) + */ + if (GET_ID_DESITY_DEVICE(stat) != mem_info[fd->dev].density_id) + return false; + + fd->current_page = 0; + fd->fd.seek_pos = 0; + fd->fd.size = mem_info[fd->dev].page_size * mem_info[fd->dev].page_cnt; + + /* Load selected page from dataflash memory */ + dataflash_loadPage(fd, fd->current_page); + MOD_INIT(dataflash); + return true; +}