/** ****************************************************************************** * @file spi_flash.c * @author William Xu * @version V1.0.0 * @date 16-Sep-2014 * @brief This file provides all the headers of flash operation functions. ****************************************************************************** * UNPUBLISHED PROPRIETARY SOURCE CODE * Copyright (c) 2016 MXCHIP Inc. * * The contents of this file may not be disclosed to third parties, copied or * duplicated in any form, in whole or in part, without the prior written * permission of MXCHIP Corporation. ****************************************************************************** */ #include "platform_logging.h" #include "platform.h" #include "spi_flash.h" #include "spi_flash_internal.h" #include "spi_flash_platform_interface.h" #include /* for NULL */ #define sFLASH_SPI_PAGESIZE 0x100 int sflash_read_ID( const sflash_handle_t* const handle, void* const data_addr ) { return generic_sflash_command( handle, SFLASH_READ_JEDEC_ID, 0, NULL, 3, NULL, data_addr ); } int sflash_enter_dpmode( const sflash_handle_t* const handle ) { return generic_sflash_command( handle, SFLASH_DEEP_POWER_DOWN, 0, NULL, 0, NULL, NULL ); } int sflash_write_enable( const sflash_handle_t* const handle ) { if ( handle->write_allowed == SFLASH_WRITE_ALLOWED ) { /* Send write-enable command */ int status = generic_sflash_command( handle, SFLASH_WRITE_ENABLE, 0, NULL, 0, NULL, NULL ); if ( status != 0 ) { return status; } /* Check status register */ unsigned char status_register; if ( 0 != ( status = sflash_read_status_register( handle, &status_register ) ) ) { return status; } /* Check if Block protect bits are set */ if ( status_register != SFLASH_STATUS_REGISTER_WRITE_ENABLED ) { /* Disable protection for all blocks */ if (0 != ( status = sflash_write_status_register( handle, 0 ) ) ) { return status; } /* Re-Enable writing */ if (0 != ( status = generic_sflash_command( handle, SFLASH_WRITE_ENABLE, 0, NULL, 0, NULL, NULL ) ) ) { return status; } } return 0; } else { return -1; } } int sflash_chip_erase( const sflash_handle_t* const handle ) { int status = sflash_write_enable( handle ); if ( status != 0 ) { return status; } return generic_sflash_command( handle, SFLASH_CHIP_ERASE1, 0, NULL, 0, NULL, NULL ); } int sflash_sector_erase ( const sflash_handle_t* const handle, unsigned long device_address ) { char device_address_array[3] = { ( ( device_address & 0x00FF0000 ) >> 16 ), ( ( device_address & 0x0000FF00 ) >> 8 ), ( ( device_address & 0x000000FF ) >> 0 ) }; int retval; int status = sflash_write_enable( handle ); if ( status != 0 ) { return status; } retval = generic_sflash_command( handle, SFLASH_SECTOR_ERASE, 3, device_address_array, 0, NULL, NULL ); check_string(retval == 0, "SPI Flash erase error"); return retval; } int sflash_read_status_register( const sflash_handle_t* const handle, void* const dest_addr ) { return generic_sflash_command( handle, SFLASH_READ_STATUS_REGISTER, 0, NULL, 1, NULL, dest_addr ); } int sflash_read( const sflash_handle_t* const handle, unsigned long device_address, void* const data_addr, unsigned int size ) { char device_address_array[3] = { ( ( device_address & 0x00FF0000 ) >> 16 ), ( ( device_address & 0x0000FF00 ) >> 8 ), ( ( device_address & 0x000000FF ) >> 0 ) }; return generic_sflash_command( handle, SFLASH_READ, 3, device_address_array, size, NULL, data_addr ); } // int sflash_get_size( const sflash_handle_t* const handle, unsigned long* const size ) // { // *size = 0; /* Unknown size to start with */ // #ifdef SFLASH_SUPPORT_MACRONIX_PARTS // if ( handle->device_id == SFLASH_ID_MX25L8006E ) // { // *size = 0x100000; /* 1MByte */ // } // #endif /* ifdef SFLASH_SUPPORT_MACRONIX_PARTS */ // #ifdef SFLASH_SUPPORT_WINBOND_PARTS // if ( handle->device_id == SFLASH_ID_W25X80AVSIG ) // { // *size = 0x100000; /* 1MByte */ // } // #endif /* ifdef SFLASH_SUPPORT_MACRONIX_PARTS */ // #ifdef SFLASH_SUPPORT_SST_PARTS // if ( handle->device_id == SFLASH_ID_SST25VF080B ) // { // *size = 0x100000; /* 1MByte */ // } // #endif /* ifdef SFLASH_SUPPORT_SST_PARTS */ // return 0; // } int sflash_get_size( const sflash_handle_t* const handle, /*@out@*/ unsigned long* const size ) { *size = 0; /* Unknown size to start with */ #ifdef SFLASH_SUPPORT_MACRONIX_PARTS if ( handle->device_id == SFLASH_ID_MX25L8006E ) { *size = (unsigned long) 0x100000; /* 1MByte */ } else if ( handle->device_id == SFLASH_ID_MX25L1606E ) { *size = (unsigned long) 0x200000; /* 2MByte */ } #endif /* ifdef SFLASH_SUPPORT_MACRONIX_PARTS */ #ifdef SFLASH_SUPPORT_SST_PARTS if ( handle->device_id == SFLASH_ID_SST25VF080B ) { *size = (unsigned long) 0x100000; /* 1MByte */ } #endif /* ifdef SFLASH_SUPPORT_SST_PARTS */ #ifdef SFLASH_SUPPORT_EON_PARTS if ( handle->device_id == SFLASH_ID_EN25QH16 ) { *size = (unsigned long) 0x200000; /* 2MByte */ } #endif /* ifdef SFLASH_SUPPORT_EON_PARTS */ #ifdef SFLASH_SUPPORT_WINBOND_PARTS if ( handle->device_id == SFLASH_ID_W25X80AVSIG ) { *size = (unsigned long) 0x100000; /* 1MByte */ } #endif /* ifdef SFLASH_SUPPORT_WINBOND_PARTS */ return 0; } int sflash_write_page( const sflash_handle_t* const handle, unsigned long device_address, const void* const data_addr, int size ) { int status; int write_size; int max_write_size = 256; unsigned char enable_before_every_write = 1; unsigned char* data_addr_ptr = (unsigned char*) data_addr; unsigned char curr_device_address[3]; if ( handle->write_allowed == SFLASH_WRITE_ALLOWED ) { } else { return -1; } /* Some manufacturers support programming an entire page in one command. */ #ifdef SFLASH_SUPPORT_MACRONIX_PARTS if ( SFLASH_MANUFACTURER( handle->device_id ) == SFLASH_MANUFACTURER_MACRONIX ) { max_write_size = 256; /* TODO: this should be 256, but that causes write errors */ enable_before_every_write = 1; } #endif /* ifdef SFLASH_SUPPORT_MACRONIX_PARTS */ #ifdef SFLASH_SUPPORT_WINBOND_PARTS if ( SFLASH_MANUFACTURER( handle->device_id ) == SFLASH_MANUFACTURER_WINBOND ) { max_write_size = 256; /* TODO: this should be 256, but that causes write errors */ enable_before_every_write = 1; } #endif /* ifdef SFLASH_SUPPORT_MACRONIX_PARTS */ #ifdef SFLASH_SUPPORT_SST_PARTS if ( SFLASH_MANUFACTURER( handle->device_id ) == SFLASH_MANUFACTURER_SST ) { max_write_size = 1; enable_before_every_write = 1; } #endif /* ifdef SFLASH_SUPPORT_SST_PARTS */ #ifdef SFLASH_SUPPORT_EON_PARTS if ( SFLASH_MANUFACTURER( handle->device_id ) == SFLASH_MANUFACTURER_EON ) { max_write_size = (unsigned int) 1; enable_before_every_write = 1; } #endif /* ifdef SFLASH_SUPPORT_EON_PARTS */ if ( ( enable_before_every_write == 0 ) && ( 0 != ( status = sflash_write_enable( handle ) ) ) ) { return status; } /* Generic x-bytes-at-a-time write */ while ( size > 0 ) { write_size = ( size > max_write_size )? max_write_size : size; curr_device_address[0] = ( ( device_address & 0x00FF0000 ) >> 16 ); curr_device_address[1] = ( ( device_address & 0x0000FF00 ) >> 8 ); curr_device_address[2] = ( ( device_address & 0x000000FF ) >> 0 ); if ( ( enable_before_every_write == 1 ) && ( 0 != ( status = sflash_write_enable( handle ) ) ) ) { return status; } if ( 0 != ( status = generic_sflash_command( handle, SFLASH_WRITE, 3, curr_device_address, write_size, data_addr_ptr, NULL ) ) ) { return status; } data_addr_ptr += write_size; device_address += write_size; size -= write_size; } return 0; } /** * @brief Writes block of data to the FLASH. In this function, the number of * WRITE cycles are reduced, using Page WRITE sequence. * @param pBuffer: pointer to the buffer containing the data to be written * to the FLASH. * @param WriteAddr: FLASH's internal address to write to. * @param NumByteToWrite: number of bytes to write to the FLASH. * @retval None */ int sflash_write( const sflash_handle_t* const handle, unsigned long device_address, const void* const data_addr, unsigned int size ) { int status; uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; unsigned char* data_addr_ptr = (unsigned char*) data_addr; Addr = device_address % sFLASH_SPI_PAGESIZE; count = sFLASH_SPI_PAGESIZE - Addr; NumOfPage = size / sFLASH_SPI_PAGESIZE; NumOfSingle = size % sFLASH_SPI_PAGESIZE; if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned */ { if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */ { status = sflash_write_page( handle, device_address, data_addr_ptr, size ); //sFLASH_WritePage(pBuffer, WriteAddr, NumByteToWrite); } else /*!< NumByteToWrite > sFLASH_PAGESIZE */ { while (NumOfPage--) { status = sflash_write_page( handle, device_address, data_addr_ptr, sFLASH_SPI_PAGESIZE ); //sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE); device_address += sFLASH_SPI_PAGESIZE; data_addr_ptr += sFLASH_SPI_PAGESIZE; } status = sflash_write_page( handle, device_address, data_addr_ptr, NumOfSingle); } } else /*!< WriteAddr is not sFLASH_PAGESIZE aligned */ { if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */ { if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */ { temp = NumOfSingle - count; status = sflash_write_page( handle, device_address, data_addr_ptr, count ); //sFLASH_WritePage(pBuffer, WriteAddr, count); device_address += count; data_addr_ptr += count; status = sflash_write_page( handle, device_address, data_addr_ptr, temp); } else { status = sflash_write_page(handle, device_address, data_addr_ptr, size); } } else /*!< NumByteToWrite > sFLASH_PAGESIZE */ { size -= count; NumOfPage = size / sFLASH_SPI_PAGESIZE; NumOfSingle = size % sFLASH_SPI_PAGESIZE; status = sflash_write_page( handle, device_address, data_addr_ptr, count ); //sFLASH_WritePage(pBuffer, WriteAddr, count); device_address += count; data_addr_ptr += count; while (NumOfPage--) { status = sflash_write_page( handle, device_address, data_addr_ptr, sFLASH_SPI_PAGESIZE ); //sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE); device_address += sFLASH_SPI_PAGESIZE; data_addr_ptr += sFLASH_SPI_PAGESIZE; } if (NumOfSingle != 0) { status = sflash_write_page( handle, device_address, data_addr_ptr, NumOfSingle ); //sFLASH_WritePage(pBuffer, WriteAddr, NumOfSingle); } } } return status; } int sflash_write_status_register( const sflash_handle_t* const handle, char value ) { char status_register_val = value; #ifdef SFLASH_SUPPORT_SST_PARTS /* SST parts require enabling writing to the status register */ if ( SFLASH_MANUFACTURER( handle->device_id ) == SFLASH_MANUFACTURER_SST ) { int status; if ( 0 != ( status = generic_sflash_command( handle, SFLASH_ENABLE_WRITE_STATUS_REGISTER, 0, NULL, 0, NULL, NULL ) ) ) { return status; } } #endif /* ifdef SFLASH_SUPPORT_SST_PARTS */ return generic_sflash_command( handle, SFLASH_WRITE_STATUS_REGISTER, 0, NULL, 1, &status_register_val, NULL ); } int deinit_sflash( /*@out@*/ sflash_handle_t* const handle) { int status; (void) handle; status = sflash_platform_deinit( ); if ( status != 0 ) { return status; } return 0; } int init_sflash( /*@out@*/ sflash_handle_t* const handle, /*@shared@*/ void* peripheral_id, sflash_write_allowed_t write_allowed_in ) { int status; device_id_t tmp_device_id; status = sflash_platform_init( peripheral_id, &handle->platform_peripheral ); if ( status != 0 ) { return status; } handle->write_allowed = write_allowed_in; handle->device_id = 0; status = sflash_read_ID( handle, &tmp_device_id ); if ( status != 0 ) { return status; } handle->device_id = ( ((uint32_t) tmp_device_id.id[0]) << 16 ) + ( ((uint32_t) tmp_device_id.id[1]) << 8 ) + ( ((uint32_t) tmp_device_id.id[2]) << 0 ); if ( write_allowed_in == SFLASH_WRITE_ALLOWED ) { /* Enable writing */ if (0 != ( status = sflash_write_enable( handle ) ) ) { return status; } } return 0; } static inline int is_write_command( sflash_command_t cmd ) { return ( ( cmd == SFLASH_WRITE ) || ( cmd == SFLASH_CHIP_ERASE1 ) || ( cmd == SFLASH_CHIP_ERASE2 ) || ( cmd == SFLASH_SECTOR_ERASE ) || ( cmd == SFLASH_BLOCK_ERASE_MID ) || ( cmd == SFLASH_BLOCK_ERASE_LARGE ) )? 1 : 0; } int generic_sflash_command( const sflash_handle_t* const handle, sflash_command_t cmd, unsigned long num_initial_parameter_bytes, /*@null@*/ /*@observer@*/ const void* const parameter_bytes, unsigned long num_data_bytes, /*@null@*/ /*@observer@*/ const void* const data_MOSI, /*@null@*/ /*@out@*/ /*@dependent@*/ void* const data_MISO ) { int status; sflash_platform_message_segment_t segments[3] = { { &cmd, NULL, (unsigned long) 1 }, { parameter_bytes, NULL, num_initial_parameter_bytes }, /*@-compdef@*/ /* Lint: Tell lint that it is OK that data_MISO is not completely defined */ { data_MOSI, data_MISO, num_data_bytes } /*@+compdef@*/ }; status = sflash_platform_send_recv( handle->platform_peripheral, segments, (unsigned int) 3 ); if ( status != 0 ) { /*@-mustdefine@*/ /* Lint: do not need to define data_MISO due to failure */ return status; /*@+mustdefine@*/ } if ( is_write_command( cmd ) == 1 ) { unsigned char status_register; /* write commands require waiting until chip is finished writing */ do { status = sflash_read_status_register( handle, &status_register ); if ( status != 0 ) { /*@-mustdefine@*/ /* Lint: do not need to define data_MISO due to failure */ return status; /*@+mustdefine@*/ } } while( ( status_register & SFLASH_STATUS_REGISTER_BUSY ) != (unsigned char) 0 ); } /*@-mustdefine@*/ /* Lint: lint does not realise data_MISO was set by sflash_platform_send_recv */ return 0; /*@+mustdefine@*/ }