From 5a52620172f13f89cb7bc95eca00d8acf71d296f Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Thu, 9 Aug 2018 18:50:55 +0300 Subject: [PATCH] Initial changes save --- SPIFBlockDevice.cpp | 476 +++++++++++++++++++++++++++++++++++--------- SPIFBlockDevice.h | 119 ++++++++++- 2 files changed, 490 insertions(+), 105 deletions(-) diff --git a/SPIFBlockDevice.cpp b/SPIFBlockDevice.cpp index 329e0a5806..057deb120d 100644 --- a/SPIFBlockDevice.cpp +++ b/SPIFBlockDevice.cpp @@ -53,10 +53,108 @@ SPIFBlockDevice::SPIFBlockDevice( { _cs = 1; _spi.frequency(freq); + _address_size = QSPI_CFG_ADDR_SIZE_24; + // Initial SFDP read tables are read with 8 dummy cycles + _read_dummy_and_mode_cycles = 8; + _dummy_and_mode_cycles = 8; + } int SPIFBlockDevice::init() { + + uint8_t vendor_device_ids[4]; + size_t data_length = 3; + int status = SPIF_BD_ERROR_OK; + uint32_t basic_table_addr = NULL; + size_t basic_table_size = 0; + uint32_t sector_map_table_addr = NULL; + size_t sector_map_table_size = 0; + int qspi_status = QSPI_STATUS_OK; + + _mutex.lock(); + if (_is_initialized == true) { + goto exit_point; + } + + // Soft Reset + /* + if ( -1 == _reset_flash_mem()) { + tr_error("ERROR: init - Unable to initialize flash memory, tests failed\n"); + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } else { + tr_info("INFO: Initialize flash memory OK\n"); + } + */ + + /* Read Manufacturer ID (1byte), and Device ID (2bytes)*/ + qspi_status = _qspi_send_read_command(QSPIF_RDID, (char *)vendor_device_ids, 0x0 /*address*/, data_length); + if (qspi_status != QSPI_STATUS_OK) { + tr_error("ERROR: init - Read Vendor ID Failed"); + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + switch (vendor_device_ids[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue write disable instruction to clear + _set_write_enable(); + _qspi_send_general_command(QSPIF_WRDI, -1, NULL, 0, NULL, 0); + break; + } + + //Synchronize Device + if ( false == _is_mem_ready()) { + tr_error("ERROR: init - _is_mem_ready Failed"); + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + /**************************** Parse SFDP Header ***********************************/ + if ( 0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) { + tr_error("ERROR: init - Parse SFDP Headers Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + + /**************************** Parse Basic Parameters Table ***********************************/ + if ( 0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size) ) { + tr_error("ERROR: init - Parse Basic Param Table Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + /**************************** Parse Sector Map Table ***********************************/ + _region_size_bytes[0] = + _device_size_bytes; // If there's no region map, we have a single region sized the entire device size + _region_high_boundary[0] = _device_size_bytes - 1; + + if ( (sector_map_table_addr != NULL) && (0 != sector_map_table_size) ) { + tr_info("INFO: init - Parsing Sector Map Table - addr: 0x%xh, Size: %d", sector_map_table_addr, + sector_map_table_size); + if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size) ) { + tr_error("ERROR: init - Parse Sector Map Table Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + } + + // Configure BUS Mode to 1_1_1 for all commands other than Read + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + + _is_initialized = true; + +exit_point: + _mutex.unlock(); + + return status; + + +/**************************************************************************************************************/ if (!_is_initialized) { _init_ref_count = 0; } @@ -67,6 +165,9 @@ int SPIFBlockDevice::init() return BD_ERROR_OK; } + // Initial SFDP read tables are read with 8 dummy cycles + _read_dummy_and_mode_cycles = 8; + // Check for vendor specific hacks, these should move into more general // handling when possible. RDID is not used to verify a device is attached. uint8_t id[3]; @@ -160,76 +261,64 @@ int SPIFBlockDevice::deinit() return 0; } -void SPIFBlockDevice::_cmdread( - uint8_t op, uint32_t addrc, uint32_t retc, - uint32_t addr, uint8_t *rets) + + +void SPIFBlockDevice::_cmdread(uint8_t inst, uint32_t addr, uint32_t data_size, uint8_t *data) { _cs = 0; - _spi.write(op); + uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; + uint8_t dummy_byte = 0x00; + uint8_t *addr_byte_ptr = &addr; + addr_byte_ptr += (_address_size-1) - for (uint32_t i = 0; i < addrc; i++) { - _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + // Write 1 byte Instruction + _spi.write(inst); + + // Write Address (can be either 3 or 4 bytes long) + for (uint32_t i = 0; i < _address_size; i++) { + _spi.write(*addr_byte_ptr); + addr_byte_ptr--; } - for (uint32_t i = 0; i < retc; i++) { - rets[i] = _spi.write(0); + // Write Dummy Cycles Bytes + for (uint32_t i = 0; i < dummy_bytes; i++) { + _spi.write(dummy_byte); + } + + // Read Data + for (uint32_t i = 0; i < data_size; i++) { + data[i] = _spi.write(0); } _cs = 1; - - if (SPIF_DEBUG) { - printf("spif <- %02x", op); - for (uint32_t i = 0; i < addrc; i++) { - if (i < addrc) { - printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); - } else { - printf(" "); - } - } - printf(" "); - for (uint32_t i = 0; i < 16 && i < retc; i++) { - printf("%02x", rets[i]); - } - if (retc > 16) { - printf("..."); - } - printf("\n"); - } } -void SPIFBlockDevice::_cmdwrite( - uint8_t op, uint32_t addrc, uint32_t argc, - uint32_t addr, const uint8_t *args) +void SPIFBlockDevice::_cmdwrite(uint8_t inst, uint32_t addr, uint32_t data_size, const uint8_t *data) { _cs = 0; - _spi.write(op); + uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; + uint8_t dummy_byte = 0x00; + uint8_t *addr_byte_ptr = &addr; + addr_byte_ptr += (_address_size-1) - for (uint32_t i = 0; i < addrc; i++) { - _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + // Write 1 byte Instruction + _spi.write(inst); + + // Write Address (can be either 3 or 4 bytes long) + for (uint32_t i = 0; i < _address_size; i++) { + _spi.write(*addr_byte_ptr); + addr_byte_ptr--; } - for (uint32_t i = 0; i < argc; i++) { - _spi.write(args[i]); + // Write Dummy Cycles Bytes + for (uint32_t i = 0; i < dummy_bytes; i++) { + _spi.write(dummy_byte); + } + + // Write Data + for (uint32_t i = 0; i < data_size; i++) { + _spi.write(data[i]); } _cs = 1; - - if (SPIF_DEBUG) { - printf("spif -> %02x", op); - for (uint32_t i = 0; i < addrc; i++) { - if (i < addrc) { - printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); - } else { - printf(" "); - } - } - printf(" "); - for (uint32_t i = 0; i < 16 && i < argc; i++) { - printf("%02x", args[i]); - } - if (argc > 16) { - printf("..."); - } - printf("\n"); - } } int SPIFBlockDevice::_sync() @@ -270,52 +359,179 @@ int SPIFBlockDevice::_wren() return BD_ERROR_DEVICE_ERROR; } + +void SPIFBlockDevice::_spi_send_read_command(uint8_t read_inst, void *buffer, bd_addr_t addr, bd_size_t size) +{ + uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; + uint8_t dummy_byte = 0x00; + uint8_t *addr_byte_ptr = addr; + uint8_t *data = (uint8_t *)buffer; + addr_byte_ptr += (_address_size-1); + + // Write 1 byte Instruction + _spi.write(read_inst); + + // Write Address (can be either 3 or 4 bytes long) + for (uint32_t i = 0; i < _address_size; i++) { + _spi.write(*addr_byte_ptr); + addr_byte_ptr--; + } + + // Write Dummy Cycles Bytes + for (uint32_t i = 0; i < dummy_bytes; i++) { + _spi.write(dummy_byte); + } + + // Read Data + for (bd_size_t i = 0; i < size; i++) { + data[i] = _spi.write(0); + } + + return; +} + +void SPIFBlockDevice::_spi_send_program_command(unsigned int prog_inst, const void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Send Program (write) command to device driver + uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; + uint8_t dummy_byte = 0x00; + uint8_t *addr_byte_ptr = addr; + uint8_t *data = (uint8_t *)buffer; + addr_byte_ptr += (_address_size-1) + + // Write 1 byte Instruction + _spi.write(prog_inst); + + // Write Address (can be either 3 or 4 bytes long) + for (uint32_t i = 0; i < _address_size; i++) { + _spi.write(*addr_byte_ptr); + addr_byte_ptr--; + } + + // Write Dummy Cycles Bytes + for (uint32_t i = 0; i < dummy_bytes; i++) { + _spi.write(dummy_byte); + } + + // Write Data + for (bd_size_t i = 0; i < size; i++) { + _spi.write(data[i]); + } + + return; +} + +qspi_status_t SPIFBlockDevice::_spi_send_erase_command(unsigned int erase_inst, bd_addr_t addr, bd_size_t size) +{ + tr_info("INFO: Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); + _spi_send_general_command(erase_inst, addr) + return; +} + +qspi_status_t SPIFBlockDevice::_spi_send_general_command(unsigned int instruction, bd_addr_t addr) +{ + // Send a general command Instruction to driver + uint32_t dummy_bytes = _dummy_and_mode_cycles / 8; + uint8_t dummy_byte = 0x00; + uint8_t *addr_byte_ptr = (((int)addr) & 0x00FFF000); + uint8_t *data = (uint8_t *)buffer; + addr_byte_ptr += (_address_size-1) + + // Write 1 byte Instruction + _spi.write(instruction); + + // Write Address (can be either 3 or 4 bytes long) + for (uint32_t i = 0; i < _address_size; i++) { + _spi.write(*addr_byte_ptr); + addr_byte_ptr--; + } + + // Write Dummy Cycles Bytes + for (uint32_t i = 0; i < dummy_bytes; i++) { + _spi.write(dummy_byte); + } + + return; +} + + + int SPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { if (!_is_initialized) { return BD_ERROR_DEVICE_ERROR; } - // Check the address and size fit onto the chip. - MBED_ASSERT(is_valid_read(addr, size)); + int status = SPIF_BD_ERROR_OK; + + tr_info("INFO Inst: 0x%xh", _read_instruction); + + _mutex.lock(); + + // Set Dummy Cycles for Specific Read Command Mode + _dummy_and_mode_cycles = _read_dummy_and_mode_cycles; + + _spi_send_read_command(_read_instruction, buffer, addr, size); + + // Set Dummy Cycles for all other command modes + _dummy_and_mode_cycles = 0; + + _mutex.unlock(); + return status; - _cmdread(SPIF_READ, 3, size, addr, static_cast(buffer)); - return 0; } int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) { - // Check the address and size fit onto the chip. - MBED_ASSERT(is_valid_program(addr, size)); - if (!_is_initialized) { return BD_ERROR_DEVICE_ERROR; } - while (size > 0) { - int err = _wren(); - if (err) { - return err; - } + qspi_status_t result = QSPI_STATUS_OK; + bool program_failed = false; + int status = SPIF_BD_ERROR_OK; + uint32_t offset = 0; + uint32_t chunk = 0; - // Write up to 256 bytes a page - // TODO handle unaligned programs - uint32_t off = addr % 256; - uint32_t chunk = (off + size < 256) ? size : (256-off); - _cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast(buffer)); - buffer = static_cast(buffer) + chunk; - addr += chunk; - size -= chunk; + tr_debug("DEBUG: program - Buff: 0x%x, addr: %llu, size: %llu", buffer, addr, size); - wait_ms(1); + while (size > 0) { - err = _sync(); - if (err) { - return err; - } + // Write on _page_size_bytes boundaries (Default 256 bytes a page) + offset = addr % _page_size_bytes; + chunk = (offset + size < _page_size_bytes) ? size : (_page_size_bytes - offset); + + _mutex.lock(); + + //Send WREN + if (_set_write_enable() != 0) { + tr_error("ERROR: Write Enabe failed\n"); + program_failed = true; + status = QSPIF_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + _spi_send_program_command(_prog_instruction, buffer, addr, chunk); + + buffer = static_cast(buffer) + chunk; + addr += chunk; + size -= chunk; + + if ( false == _is_mem_ready()) { + tr_error("ERROR: Device not ready after write, failed\n"); + program_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + _mutex.unlock(); + } + +exit_point: + if (program_failed) { + _mutex.unlock(); } - return 0; + return status; } int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size) @@ -327,26 +543,69 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size) return BD_ERROR_DEVICE_ERROR; } + int type = 0; + uint32_t chunk = 4096; + unsigned int cur_erase_inst = _erase_instruction; + int size = (int)in_size; + bool erase_failed = false; + int status = QSPIF_BD_ERROR_OK; + // Find region of erased address + int region = _utils_find_addr_region(addr); + // Erase Types of selected region + uint8_t bitfield = _region_erase_types_bitfield[region]; + + tr_debug("DEBUG: erase - addr: %llu, in_size: %llu", addr, in_size); + + // For each iteration erase the largest section supported by current region while (size > 0) { - int err = _wren(); - if (err) { - return err; + + // iterate to find next Largest erase type ( a. supported by region, b. smaller than size) + // find the matching instruction and erase size chunk for that type. + type = _utils_iterate_next_largest_erase_type(bitfield, size, (int)addr, _region_high_boundary[region]); + cur_erase_inst = _erase_type_inst_arr[type]; + chunk = _erase_type_size_arr[type]; + + tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %d , ", + addr, size, cur_erase_inst, chunk); + tr_debug("DEBUG: erase - Region: %d, Type:%d", + region, type); + + _mutex.lock(); + + if (_set_write_enable() != 0) { + tr_error("ERROR: QSPI Erase Device not ready - failed"); + erase_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; } - - // Erase 4kbyte sectors - // TODO support other erase sizes? - uint32_t chunk = 4096; - _cmdwrite(SPIF_SE, 3, 0, addr, NULL); + + _spi_send_erase_command(cur_erase_inst, addr, size); + addr += chunk; size -= chunk; - err = _sync(); - if (err) { - return err; + if ( (size > 0) && (addr > _region_high_boundary[region]) ) { + // erase crossed to next region + region++; + bitfield = _region_erase_types_bitfield[region]; } + + if ( false == _is_mem_ready()) { + tr_error("ERROR: QSPI After Erase Device not ready - failed\n"); + erase_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + _mutex.unlock(); } - return 0; +exit_point: + if (erase_failed) { + _mutex.unlock(); + } + + return status; } bd_size_t SPIFBlockDevice::get_read_size() const @@ -364,11 +623,40 @@ bd_size_t SPIFBlockDevice::get_erase_size() const return SPIF_SE_SIZE; } -bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr) const +// Find minimal erase size supported by the region to which the address belongs to +bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr) { - return SPIF_SE_SIZE; + // Find region of current address + int region = _utils_find_addr_region(addr); + + int min_region_erase_size = _min_common_erase_size; + int8_t type_mask = ERASE_BITMASK_TYPE1; + int i_ind = 0; + + + if (region != -1) { + type_mask = 0x01; + + for (i_ind = 0; i_ind < 4; i_ind++) { + // loop through erase types bitfield supported by region + if (_region_erase_types_bitfield[region] & type_mask) { + + min_region_erase_size = _erase_type_size_arr[i_ind]; + break; + } + type_mask = type_mask << 1; + } + + if (i_ind == 4) { + tr_error("ERROR: no erase type was found for region addr"); + } + } + + return (bd_size_t)min_region_erase_size; } + + bd_size_t SPIFBlockDevice::size() const { if (!_is_initialized) { diff --git a/SPIFBlockDevice.h b/SPIFBlockDevice.h index 2cce398d81..6916b1c3eb 100644 --- a/SPIFBlockDevice.h +++ b/SPIFBlockDevice.h @@ -19,12 +19,28 @@ #include #include "BlockDevice.h" +namespace mbed { + +/** Enum qspif standard error codes + * + * @enum qspif_bd_error + */ +enum qspif_bd_error { + SPIF_BD_ERROR_OK = 0, /*!< no error */ + SPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + SPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + SPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ + SPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ +}; + + +#define SPIF_MAX_REGIONS 10 +#define MAX_NUM_OF_ERASE_TYPES 4 -/** BlockDevice for SPI based flash devices - * such as the MX25R or SST26F016B +/** BlockDevice for SFDP based flash devices over SPI bus * * @code - * // Here's an example using the MX25R SPI flash device on the K82F + * // Here's an example using SPI flash device on K82F target * #include "mbed.h" * #include "SPIFBlockDevice.h" * @@ -138,7 +154,15 @@ public: */ virtual bd_size_t get_erase_size(bd_addr_t addr) const; - /** Get the value of storage when erased + /** Get the size of minimal eraseable sector size of given address + * + * @param addr Any address within block queried for erase sector size (can be any address within flash size offset) + * @return Size of minimal erase sector size, in given address region, in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size(bd_addr_t addr); + + /** Get the value of storage byte after it was erased * * If get_erase_value returns a non-negative byte value, the underlying * storage is set to that value when erased, and storage containing @@ -155,6 +179,48 @@ public: */ virtual bd_size_t size() const; +private: + + /* SFDP Detection and Parsing Functions */ + /****************************************/ + // Parse SFDP Headers and retrieve Basic Param and Sector Map Tables (if exist) + int _sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_t& basic_table_size, + uint32_t& sector_map_table_addr, size_t& sector_map_table_size); + + // Parse and Detect required Basic Parameters from Table + int _sfdp_parse_basic_param_table(uint32_t basic_table_addr, size_t basic_table_size); + + // Parse and read information required by Regions Secotr Map + int _sfdp_parse_sector_map_table(uint32_t sector_map_table_addr, size_t sector_map_table_size); + + // Detect fastest read Bus mode supported by device + int _sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_ptr, bool& set_quad_enable, bool& is_qpi_mode, + unsigned int& read_inst); + + // Set Page size for program + int _sfdp_detect_page_size(uint8_t *basic_param_table_ptr); + + // Detect all supported erase types + int _sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, unsigned int& erase4k_inst, + unsigned int *erase_type_inst_arr, unsigned int *erase_type_size_arr); + + /* Utilities Functions */ + /***********************/ + // Find the region to which the given offset belong to + int _utils_find_addr_region(bd_size_t offset); + + // Iterate on all supported Erase Types of the Region to which the offset belong to. + // Iterates from highest type to lowest + int _utils_iterate_next_largest_erase_type(uint8_t& bitfield, int size, int offset, int boundry); + + // Internal functions + int _wren(); + int _sync(); + void _cmdread(uint8_t op, uint32_t addrc, uint32_t retc, + uint32_t addr, uint8_t *rets); + void _cmdwrite(uint8_t op, uint32_t addrc, uint32_t argc, + uint32_t addr, const uint8_t *args); + private: // Master side hardware SPI _spi; @@ -166,14 +232,45 @@ private: bool _is_initialized; uint32_t _init_ref_count; - // Internal functions - int _wren(); - int _sync(); - void _cmdread(uint8_t op, uint32_t addrc, uint32_t retc, - uint32_t addr, uint8_t *rets); - void _cmdwrite(uint8_t op, uint32_t addrc, uint32_t argc, - uint32_t addr, const uint8_t *args); + bool _is_initialized; + + // Mutex is used to protect Flash device for some QSPI Driver commands that must be done sequentially with no other commands in between + // e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready + PlatformMutex _mutex; + + // Command Instructions + unsigned int _read_instruction; + unsigned int _prog_instruction; + unsigned int _erase_instruction; + unsigned int _erase4k_inst; // Legacy 4K erase instruction (default 0x20h) + + // Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size) + unsigned int _erase_type_inst_arr[MAX_NUM_OF_ERASE_TYPES]; + unsigned int _erase_type_size_arr[MAX_NUM_OF_ERASE_TYPES]; + + // Sector Regions Map + int _regions_count; //number of regions + int _region_size_bytes[QSPIF_MAX_REGIONS]; //regions size in bytes + bd_size_t _region_high_boundary[QSPIF_MAX_REGIONS]; //region high address offset boundary + //Each Region can support a bit combination of any of the 4 Erase Types + uint8_t _region_erase_types_bitfield[QSPIF_MAX_REGIONS]; + int _min_common_erase_size; // minimal common erase size for all regions (0 if none exists) + + int _page_size_bytes; // Page size - 256 Bytes default + bd_size_t _device_size_bytes; + + // Bus speed configuration + qspi_bus_width_t _inst_width; //Bus width for Instruction phase + qspi_bus_width_t _address_width; //Bus width for Address phase + qspi_address_size_t _address_size; // number of bytes for address + qspi_bus_width_t _data_width; //Bus width for Data phase + int _dummy_and_mode_cycles; // Number of Dummy and Mode Bits required by Current Bus Mode + + + + }; +} //namespace mbed #endif /* MBED_SPIF_BLOCK_DEVICE_H */