diff options
| author | bernard <[email protected]> | 2014-08-01 12:03:24 +0800 |
|---|---|---|
| committer | bernard <[email protected]> | 2014-08-01 12:03:24 +0800 |
| commit | 582aff86ed91dad23e8ace36f8466ca20c80bebf (patch) | |
| tree | 77b4338a8afe48a1c45be9f4bab9b40de1543c5b /components/drivers/spi/spi_flash_w25qxx.c | |
| parent | 516711dc1d08ed332704ec53fd377edbdd0b9834 (diff) | |
| parent | 2fdef817b1a0ce82ef61bde11f41e651c1cc8d79 (diff) | |
Merge branch 'master' of https://github.com/RT-Thread/rt-threadv2.0.0_beta
Diffstat (limited to 'components/drivers/spi/spi_flash_w25qxx.c')
| -rw-r--r-- | components/drivers/spi/spi_flash_w25qxx.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/components/drivers/spi/spi_flash_w25qxx.c b/components/drivers/spi/spi_flash_w25qxx.c new file mode 100644 index 0000000000..bdcdbd83c1 --- /dev/null +++ b/components/drivers/spi/spi_flash_w25qxx.c @@ -0,0 +1,371 @@ +/* + * File : spi_flash_w25qxx.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2011, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2011-12-16 aozima the first version + * 2012-05-06 aozima can page write. + * 2012-08-23 aozima add flash lock. + * 2012-08-24 aozima fixed write status register BUG. + */ + +#include <stdint.h> +#include "spi_flash_w25qxx.h" + +#define FLASH_DEBUG + +#ifdef FLASH_DEBUG +#define FLASH_TRACE rt_kprintf +#else +#define FLASH_TRACE(...) +#endif /* #ifdef FLASH_DEBUG */ + +#define PAGE_SIZE 4096 + +/* JEDEC Manufacturer��s ID */ +#define MF_ID (0xEF) +/* JEDEC Device ID: Memory type and Capacity */ +#define MTC_W25Q16_BV_CL_CV (0x4015) /* W25Q16BV W25Q16CL W25Q16CV */ +#define MTC_W25Q16_DW (0x6015) /* W25Q16DW */ +#define MTC_W25Q32_BV (0x4016) /* W25Q32BV */ +#define MTC_W25Q32_DW (0x6016) /* W25Q32DW */ +#define MTC_W25Q64_BV_CV (0x4017) /* W25Q64BV W25Q64CV */ +#define MTC_W25Q64_DW (0x4017) /* W25Q64DW */ +#define MTC_W25Q128_BV (0x4018) /* W25Q128BV */ +#define MTC_W25Q256_FV (TBD) /* W25Q256FV */ + +/* command list */ +#define CMD_WRSR (0x01) /* Write Status Register */ +#define CMD_PP (0x02) /* Page Program */ +#define CMD_READ (0x03) /* Read Data */ +#define CMD_WRDI (0x04) /* Write Disable */ +#define CMD_RDSR1 (0x05) /* Read Status Register-1 */ +#define CMD_WREN (0x06) /* Write Enable */ +#define CMD_FAST_READ (0x0B) /* Fast Read */ +#define CMD_ERASE_4K (0x20) /* Sector Erase:4K */ +#define CMD_RDSR2 (0x35) /* Read Status Register-2 */ +#define CMD_ERASE_32K (0x52) /* 32KB Block Erase */ +#define CMD_JEDEC_ID (0x9F) /* Read JEDEC ID */ +#define CMD_ERASE_full (0xC7) /* Chip Erase */ +#define CMD_ERASE_64K (0xD8) /* 64KB Block Erase */ + +#define DUMMY (0xFF) + +static struct spi_flash_device spi_flash_device; + +static void flash_lock(struct spi_flash_device * flash_device) +{ + rt_mutex_take(&flash_device->lock, RT_WAITING_FOREVER); +} + +static void flash_unlock(struct spi_flash_device * flash_device) +{ + rt_mutex_release(&flash_device->lock); +} + +static uint8_t w25qxx_read_status(void) +{ + return rt_spi_sendrecv8(spi_flash_device.rt_spi_device, CMD_RDSR1); +} + +static void w25qxx_wait_busy(void) +{ + while( w25qxx_read_status() & (0x01)); +} + +/** \brief read [size] byte from [offset] to [buffer] + * + * \param offset uint32_t unit : byte + * \param buffer uint8_t* + * \param size uint32_t unit : byte + * \return uint32_t byte for read + * + */ +static uint32_t w25qxx_read(uint32_t offset, uint8_t * buffer, uint32_t size) +{ + uint8_t send_buffer[4]; + + send_buffer[0] = CMD_WRDI; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 1); + + send_buffer[0] = CMD_READ; + send_buffer[1] = (uint8_t)(offset>>16); + send_buffer[2] = (uint8_t)(offset>>8); + send_buffer[3] = (uint8_t)(offset); + + rt_spi_send_then_recv(spi_flash_device.rt_spi_device, + send_buffer, 4, + buffer, size); + + return size; +} + +/** \brief write N page on [page] + * + * \param page_addr uint32_t unit : byte (4096 * N,1 page = 4096byte) + * \param buffer const uint8_t* + * \return uint32_t + * + */ +uint32_t w25qxx_page_write(uint32_t page_addr, const uint8_t* buffer) +{ + uint32_t index; + uint8_t send_buffer[4]; + + RT_ASSERT((page_addr&0xFF) == 0); /* page addr must align to 256byte. */ + + send_buffer[0] = CMD_WREN; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 1); + + send_buffer[0] = CMD_ERASE_4K; + send_buffer[1] = (page_addr >> 16); + send_buffer[2] = (page_addr >> 8); + send_buffer[3] = (page_addr); + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 4); + + w25qxx_wait_busy(); // wait erase done. + + for(index=0; index < (PAGE_SIZE / 256); index++) + { + send_buffer[0] = CMD_WREN; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 1); + + send_buffer[0] = CMD_PP; + send_buffer[1] = (uint8_t)(page_addr >> 16); + send_buffer[2] = (uint8_t)(page_addr >> 8); + send_buffer[3] = (uint8_t)(page_addr); + + rt_spi_send_then_send(spi_flash_device.rt_spi_device, + send_buffer, + 4, + buffer, + 256); + + buffer += 256; + page_addr += 256; + w25qxx_wait_busy(); + } + + send_buffer[0] = CMD_WRDI; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 1); + + return PAGE_SIZE; +} + +/* RT-Thread device interface */ +static rt_err_t w25qxx_flash_init(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_err_t w25qxx_flash_open(rt_device_t dev, rt_uint16_t oflag) +{ + uint8_t send_buffer[3]; + + flash_lock((struct spi_flash_device *)dev); + + send_buffer[0] = CMD_WREN; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 1); + + send_buffer[0] = CMD_WRSR; + send_buffer[1] = 0; + send_buffer[2] = 0; + rt_spi_send(spi_flash_device.rt_spi_device, send_buffer, 3); + + w25qxx_wait_busy(); + + flash_unlock((struct spi_flash_device *)dev); + + return RT_EOK; +} + +static rt_err_t w25qxx_flash_close(rt_device_t dev) +{ + return RT_EOK; +} + +static rt_err_t w25qxx_flash_control(rt_device_t dev, rt_uint8_t cmd, void *args) +{ + RT_ASSERT(dev != RT_NULL); + + if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME) + { + struct rt_device_blk_geometry *geometry; + + geometry = (struct rt_device_blk_geometry *)args; + if (geometry == RT_NULL) return -RT_ERROR; + + geometry->bytes_per_sector = spi_flash_device.geometry.bytes_per_sector; + geometry->sector_count = spi_flash_device.geometry.sector_count; + geometry->block_size = spi_flash_device.geometry.block_size; + } + + return RT_EOK; +} + +static rt_size_t w25qxx_flash_read(rt_device_t dev, + rt_off_t pos, + void* buffer, + rt_size_t size) +{ + flash_lock((struct spi_flash_device *)dev); + + w25qxx_read(pos*spi_flash_device.geometry.bytes_per_sector, + buffer, + size*spi_flash_device.geometry.bytes_per_sector); + + flash_unlock((struct spi_flash_device *)dev); + + return size; +} + +static rt_size_t w25qxx_flash_write(rt_device_t dev, + rt_off_t pos, + const void* buffer, + rt_size_t size) +{ + rt_size_t i = 0; + rt_size_t block = size; + const uint8_t * ptr = buffer; + + flash_lock((struct spi_flash_device *)dev); + + while(block--) + { + w25qxx_page_write((pos + i)*spi_flash_device.geometry.bytes_per_sector, + ptr); + ptr += PAGE_SIZE; + i++; + } + + flash_unlock((struct spi_flash_device *)dev); + + return size; +} + +rt_err_t w25qxx_init(const char * flash_device_name, const char * spi_device_name) +{ + struct rt_spi_device * rt_spi_device; + + /* initialize mutex */ + if (rt_mutex_init(&spi_flash_device.lock, spi_device_name, RT_IPC_FLAG_FIFO) != RT_EOK) + { + rt_kprintf("init sd lock mutex failed\n"); + return -RT_ENOSYS; + } + + rt_spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name); + if(rt_spi_device == RT_NULL) + { + FLASH_TRACE("spi device %s not found!\r\n", spi_device_name); + return -RT_ENOSYS; + } + spi_flash_device.rt_spi_device = rt_spi_device; + + /* config spi */ + { + struct rt_spi_configuration cfg; + cfg.data_width = 8; + cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */ + cfg.max_hz = 50 * 1000 * 1000; /* 50M */ + rt_spi_configure(spi_flash_device.rt_spi_device, &cfg); + } + + /* init flash */ + { + rt_uint8_t cmd; + rt_uint8_t id_recv[3]; + uint16_t memory_type_capacity; + + flash_lock(&spi_flash_device); + + cmd = 0xFF; /* reset SPI FLASH, cancel all cmd in processing. */ + rt_spi_send(spi_flash_device.rt_spi_device, &cmd, 1); + + cmd = CMD_WRDI; + rt_spi_send(spi_flash_device.rt_spi_device, &cmd, 1); + + /* read flash id */ + cmd = CMD_JEDEC_ID; + rt_spi_send_then_recv(spi_flash_device.rt_spi_device, &cmd, 1, id_recv, 3); + + flash_unlock(&spi_flash_device); + + if(id_recv[0] != MF_ID) + { + FLASH_TRACE("Manufacturers ID error!\r\n"); + FLASH_TRACE("JEDEC Read-ID Data : %02X %02X %02X\r\n", id_recv[0], id_recv[1], id_recv[2]); + return -RT_ENOSYS; + } + + spi_flash_device.geometry.bytes_per_sector = 4096; + spi_flash_device.geometry.block_size = 4096; /* block erase: 4k */ + + /* get memory type and capacity */ + memory_type_capacity = id_recv[1]; + memory_type_capacity = (memory_type_capacity << 8) | id_recv[2]; + + if(memory_type_capacity == MTC_W25Q128_BV) + { + FLASH_TRACE("W25Q128BV detection\r\n"); + spi_flash_device.geometry.sector_count = 4096; + } + else if(memory_type_capacity == MTC_W25Q64_BV_CV) + { + FLASH_TRACE("W25Q64BV or W25Q64CV detection\r\n"); + spi_flash_device.geometry.sector_count = 2048; + } + else if(memory_type_capacity == MTC_W25Q64_DW) + { + FLASH_TRACE("W25Q64DW detection\r\n"); + spi_flash_device.geometry.sector_count = 2048; + } + else if(memory_type_capacity == MTC_W25Q32_BV) + { + FLASH_TRACE("W25Q32BV detection\r\n"); + spi_flash_device.geometry.sector_count = 1024; + } + else if(memory_type_capacity == MTC_W25Q32_DW) + { + FLASH_TRACE("W25Q32DW detection\r\n"); + spi_flash_device.geometry.sector_count = 1024; + } + else if(memory_type_capacity == MTC_W25Q16_BV_CL_CV) + { + FLASH_TRACE("W25Q16BV or W25Q16CL or W25Q16CV detection\r\n"); + spi_flash_device.geometry.sector_count = 512; + } + else if(memory_type_capacity == MTC_W25Q16_DW) + { + FLASH_TRACE("W25Q16DW detection\r\n"); + spi_flash_device.geometry.sector_count = 512; + } + else + { + FLASH_TRACE("Memory Capacity error!\r\n"); + return -RT_ENOSYS; + } + } + + /* register device */ + spi_flash_device.flash_device.type = RT_Device_Class_Block; + spi_flash_device.flash_device.init = w25qxx_flash_init; + spi_flash_device.flash_device.open = w25qxx_flash_open; + spi_flash_device.flash_device.close = w25qxx_flash_close; + spi_flash_device.flash_device.read = w25qxx_flash_read; + spi_flash_device.flash_device.write = w25qxx_flash_write; + spi_flash_device.flash_device.control = w25qxx_flash_control; + /* no private */ + spi_flash_device.flash_device.user_data = RT_NULL; + + rt_device_register(&spi_flash_device.flash_device, flash_device_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); + + return RT_EOK; +} |
