blob: c818402c920720fa55da912414fe4e5233888ca6 [file] [log] [blame]
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "flash.h"
#include "wlct_os.h"
bool g_debug = false;
void flash_base::set_exit_flag(bool bExit)
{
m_bExit = bExit;
}
flash::flash(DType device_type)
:flash_base(device_type)
{
m_pDeviceAccss = NULL;
m_pFlashAccss = NULL;
if (device_type == MST_SPARROW)
m_size = FLASH_SIZE_SPARROW;
if (device_type == MST_MARLON)
m_size = FLASH_SIZE_MARLON;
if (device_type == MST_TALYN)
m_size = FLASH_SIZE_TALYN;
m_hw_read_chunk_size = 256;
m_hw_write_chunk_size = 256;
m_page_erased = new bool [m_size / FLASH_PAGE_SIZE];
if (!m_page_erased)
{
ERR("Cannot allocate page_erased buffer of size %d\n", m_size / FLASH_PAGE_SIZE);
exit(1);
}
memset(m_page_erased, 0, m_size / FLASH_PAGE_SIZE);
m_buffer = NULL;
update_erased_flash(false);
flag_reading_not_done = false;
m_address = 0;
m_length = 0;
}
flash::~flash(void)
{
if (m_pFlashAccss != NULL)
{
wfa_release_lock(m_pFlashAccss);
close ();
}
free_buffer();
delete[] m_page_erased;
}
void flash::free_buffer ()
{
if (m_buffer != NULL)
{
delete [] m_buffer;
}
}
#ifdef _WINDOWS
void readThread(void* pFlashDevice)
#else
void *readThread(void* pFlashDevice)
#endif
{
flash *pFlash = (flash*)pFlashDevice;
pFlash->set_flag_reading_not_done(true);
u_int32_t address = pFlash->get_address();
u_int32_t length = pFlash->get_length();
BYTE* buffer = (BYTE*)pFlash->get_buffer_ptr();
pFlash->read( address , length , buffer);
pFlash->set_flag_reading_not_done(false);
#ifndef _WINDOWS
return NULL;
#endif
}
int flash::read_async( const u_int32_t address, const u_int32_t length )
{
if (flag_reading_not_done)
{
return BUFFER_NOT_READY;
}
m_address = address;
m_length = length;
free_buffer(); //free memory if already allocated before
m_buffer = new BYTE[length];
#ifdef _WINDOWS
m_thread = (HANDLE)_beginthread(readThread, 0 , this);
#else
if (0 != pthread_create(&m_thread, NULL, readThread, this))
{
ERR("Failed to create Read thread\n");
free_buffer();
return BUFFER_NOT_READY;
}
#endif
return BUFFER_OK;
}
int flash::open (const char *device_name, DType dtype, bool ignore_lock)
{
int res = 0;
res = CreateDeviceAccessHandler( device_name, dtype, &m_pDeviceAccss);
if (res != 0)
return res;
deviceType = dtype;
res = wfa_create_flash_access_handler(dtype, m_pDeviceAccss, &m_pFlashAccss);
if (res != 0)
return res;
res = wfa_boot_done(m_pFlashAccss);
if (res != 0)
return res;
if (ignore_lock) {
res = wfa_force_lock(m_pFlashAccss);
} else {
if (wfa_get_lock(m_pFlashAccss) == false) {
ERR("Failed getting Flash lock."
"Use -ignore_lock option to ignore locking mechanism\n");
return (-1);
}
}
res = retrive_info();
return res;
}
int flash::open (void *m_pDeviceAccss, DType dtype, bool ignore_lock)
{
int res = 0;
if (m_pDeviceAccss == NULL)
return -1;
deviceType = dtype;
res = wfa_create_flash_access_handler(dtype, m_pDeviceAccss, &m_pFlashAccss);
if (res != 0)
return res;
res = wfa_boot_done(m_pFlashAccss);
if (res != 0)
return res;
if (ignore_lock) {
res = wfa_force_lock(m_pFlashAccss);
} else {
if (wfa_get_lock(m_pFlashAccss) == false) {
ERR("Failed getting Flash lock."
"Use -ignore_lock option to ignore locking mechanism\n");
return (-1);
}
}
res = retrive_info();
return res;
}
void flash::close ()
{
if (m_pFlashAccss)
{
wfa_close_flash_access_handler(m_pFlashAccss);
m_pFlashAccss = NULL;
}
int res = CloseDeviceAccessHandler(m_pDeviceAccss);
WLCT_UNREFERENCED_PARAM(res);
if (g_debug ) DBG("Closed device\n");
}
int flash::retrive_info()
{
int res = 0;
res = wfa_run_command(m_pFlashAccss, FC_RDID, INSTRUCTION_DATA_PHASE, false, 0, 20);
if (res != 0)
{
#ifdef _WINDOWS /* TODO: Handle error in linux */
// 1 - OK
// 2 = cancel
//int mb = MessageBox(NULL, L"Failed access to flash.\nPress OK to continue or\nCancel to abort.", L"Error", MB_OKCANCEL );
//if (mb == 2)
{
return res;
}
//res = 0;
#endif
}
memset((void*)&m_info, 0, sizeof(m_info));
res = wfa_get_data(m_pFlashAccss,(BYTE*)&m_info, sizeof(m_info) - 4);
if (res != 0)
return 0;
INFO ("Flash Manufactured by ");
switch (m_info.manufacturer_id)
{
case 0x20:
{
INFO("Numonyx ");
switch (m_info.device_id1)
{
case 0x80 :
{
INFO("M25PE ");
break;
}
case 0x20 :
{
INFO("M25P ");
break;
}
default : INFO("Unknown device ID ");
}
switch (m_info.device_id2)
{
case 0x11 :
{
INFO("Capacity of 1Mbits ");
break;
}
case 0x12 :
{
INFO("Capacity of 2Mbits ");
break;
}
case 0x14 :
{
INFO("Capacity of 8Mbits ");
break;
}
default : INFO("Unknown capacity ");
}
break;
}
case 0xC2:
{
INFO("Macronix ");
switch (m_info.device_id1)
{
case 0x20 :
{
INFO("MX25L8006E ");
break;
}
default : INFO("Unknown device ID ");
}
switch (m_info.device_id2)
{
case 0x11 :
{
INFO("Capacity of 1Mbits ");
break;
}
case 0x12 :
{
INFO("Capacity of 2Mbits ");
break;
}
case 0x13 :
{
INFO("Capacity of 4Mbits ");
break;
}
case 0x14 :
{
INFO("Capacity of 8Mbits ");
break;
}
default : INFO("Unknown capacity ");
}
break;
}
case 0x1F:
{
INFO("Atmel ");
switch (m_info.device_id1)
{
case 0x43 :
{
INFO("AT25DF021 ");
break;
}
default : INFO("Unknown device ID ");
}
break;
}
default:
{
INFO("Unknown manufacturer");
}
}
//if (m_info.extended_string_length != 0) {
//INFO("Extended INFO %s ", m_info.extended_string);
//}
INFO("\n --------------------------- \n");
return res;
}
int flash::read( const u_int32_t address, const u_int32_t length, BYTE *buffer ) const
{
int res = 0;
if (address & 0x1) {
ERR("address 0x%04x is not word aligned\n",address);
return -1;
}
if (length & 0x1) {
ERR("length 0x%04x is not word aligned\n",length);
return -1;
}
if (address + length > this->m_size) {
ERR("address 0x%04x + length 0x%04x exceeds flash size of 0x%04x\n",address,length,this->m_size);
//return -1;
}
u_int32_t offset = 0;
while (!m_bExit && offset < length )
{
u_int16_t current_size = FLASH_PAGE_SIZE - ((address + offset) & 0xff); // Part of a page
if (current_size > length ) {
current_size = (u_int16_t)length;
}
if (offset + current_size > length ) {
current_size = (u_int16_t)(length - offset);
}
//while ( offset < length ) {
// if( m_bExit ) {
// reruen(-1);
// }
// u_int32_t curr_length = min(m_hw_read_chunk_size, length - offset);
u_int32_t curr_address = address + offset;
if (g_debug) DBG("Reading 0x%04x bytes from address 0x%04x\n", current_size, curr_address);
res = wfa_run_command(m_pFlashAccss, FC_READ, INSTRUCTION_ADDRESS_DATA_PHASE, false, curr_address, current_size);
if (res != 0)
break;
res = wfa_get_data(m_pFlashAccss,buffer + offset, current_size);
if (res != 0)
break;
offset += current_size;
}
return res;
}
int flash::write( const u_int32_t address, const u_int32_t length, const BYTE *buffer, bool verify )
{
//unused param
(void)verify;
int res = -1;
res = write_wen();
if (res != 0)
return res;
u_int32_t offset = 0;
while (!m_bExit && offset < length )
{
u_int16_t current_size = FLASH_PAGE_SIZE - ((address + offset) & 0xff); // Part of a page
if (current_size > length ) {
current_size = (u_int16_t)length;
}
if (offset + current_size > length ) {
current_size = (u_int16_t)(length - offset);
}
// u_int32_t curr_length = min(m_hw_read_chunk_size, length - offset);
u_int32_t curr_address = address + offset;
if (g_debug) DBG("Reading 0x%04x bytes from address 0x%04x\n", current_size, curr_address);
res = wfa_set_data(m_pFlashAccss,buffer + offset, current_size);
if (res != 0)
break;
res = wfa_run_command(m_pFlashAccss, FC_PW, INSTRUCTION_ADDRESS_DATA_PHASE, true, curr_address, current_size);
if (res != 0)
break;
offset += current_size;
}
return res;
}
int flash::program( const u_int32_t address, const u_int32_t length, const BYTE *buffer, bool verify )
{
int res = -1;
// u_int8_t type = m_erased ? FC_PP : FC_PW;
// u_int8_t type = FC_PW;
u_int8_t type = FC_PP;
if (address & 0x1) {
ERR("address 0x%04x is not word aligned\n",address);
return -1;
}
if (length & 0x1) {
ERR("length 0x%04x is not word aligned\n",length);
return -1;
}
if (address + length > this->m_size) {
ERR("address 0x%04x + length 0x%04x exceeds flash size of 0x%04x\n",address,length,this->m_size);
return -1;
}
u_int32_t offset = 0;
BYTE *verify_buffer = new BYTE[FLASH_PAGE_SIZE];
if (!verify_buffer) {
ERR("Cannot allocate verify buffer\n");
return -1;
}
u_int32_t loop = 0;
bool dbg_verify = false;
while (!m_bExit && offset < length )
{
loop++;
if (loop%50 == 0 || loop < 10)
dbg_verify = true;
else
dbg_verify = false;
u_int16_t current_size = FLASH_PAGE_SIZE - ((address + offset) & 0xff); // Part of a page
if (current_size > length ) {
current_size = (u_int16_t)length;
}
if (offset + current_size > length ) {
current_size = (u_int16_t)(length - offset);
}
if (type == FC_PP) {
bool all_ff = true;
for (u_int32_t i = 0; i < current_size ; i+=2) {
if ( (*(u_int16_t*)(buffer + offset + i)) != (u_int16_t)(-1) ) {
all_ff = false;
break;
}
}
if (all_ff) {
if (g_debug) {
DBG(" - skipped empty page 0x%04x - ", address+offset);
} else {
INFO("\rSkip - ");
}
offset += current_size;
INFO("%03.2f%%", (float)offset / (float)length * 100);
if (g_debug) DBG("\n");
continue;
}
}
INFO("\rWrite - ");
res = page_write_program(address+offset, current_size, &buffer[offset], type);
if (res != 0)
break;
if (verify || dbg_verify) {
INFO("\rRead - ");
res = read(address+offset, current_size, verify_buffer);
if (res != 0)
break;
INFO("\rVerify - ");
if( memcmp(&buffer[offset], verify_buffer, current_size) != 0 ) {
ERR("Data verification failed on address 0x%04x length 0x%03x\n", address+offset, current_size);
res = -1;
break;
}
}
offset += current_size;
INFO("%03.2f%%", (float)offset / (float)length * 100);
if (g_debug) DBG("\n");
}
delete[] verify_buffer;
printf("\n");
return res;
}
int flash::page_write_program( const u_int32_t address, const u_int16_t length, const BYTE *buffer, u_int8_t type )
{
int res = 0;
if( m_bExit ) {
return (-1);
}
if (type == FC_PP) {
if ( !is_erased(address) ) {
res = sub_sector_erase( address );
// sector_erase( address );
}
if (g_debug) DBG("Programing page address 0x%04x length 0x%03x\n", address, length);
}
else {
if (g_debug) DBG("Writing page address 0x%04x length 0x%03x\n", address, length);
}
res = write_wen();
if (res != 0)
return res;
res = wfa_set_data(m_pFlashAccss, buffer, length);
if (res != 0)
return res;
res = wfa_run_command(m_pFlashAccss, type, INSTRUCTION_ADDRESS_DATA_PHASE, true, address, length);
if (res != 0)
return res;
res = wait_wip();
return res;
}
int flash::write_wen (void) const
{
int res = 0;
// execute_cmd(0x0106);
res = wfa_run_command(m_pFlashAccss, FC_WREN, INSTRUCTION_PHASE, true, 0, 0);
return res;
}
int flash::erase (void)
{
int res = 0;
res = wfa_boot_done(m_pFlashAccss);
if (res != 0)
return res;
res = write_wen();
if (res != 0)
return res;
//erase
res = wfa_run_command(m_pFlashAccss, FC_BE, INSTRUCTION_PHASE, true, 0, 0);
if (res != 0)
return res;
res = wait_wip();
if (res != 0)
return res;
update_erased_flash( true );
return res;
}
int flash::page_erase(u_int32_t address)
{
int res = 0;
write_wen();
if (res != 0)
return res;
res = wfa_run_command(m_pFlashAccss, FC_PE, INSTRUCTION_ADDRESS_PHASE, true, address, 0);
if (res != 0)
return res;
res = wait_wip();
if (res != 0)
return res;
if (g_debug) DBG("Cmd is 0x%04x, Status is 0x%04x\n",FC_PE, get_status() );
if (g_debug) DBG("Erased page address 0x%04x\n", address);
update_erased_page(address, true);
return res;
}
int flash::sector_erase(u_int32_t address)
{
int res = 0;
res = write_wen();
if (res != 0)
return res;
res = wfa_run_command(m_pFlashAccss, FC_SE, INSTRUCTION_ADDRESS_PHASE, true, address, 0);
if (res != 0)
return res;
res = wait_wip();
if (res != 0)
return res;
if (g_debug) DBG("Cmd is 0x%04x, Status is 0x%04x\n",FC_SE, get_status() );
if (g_debug) DBG("Erased sector address 0x%04x\n", address);
update_erased_sector(address, true);
return res;
}
int flash::sub_sector_erase(u_int32_t address)
{
int res = 0;
printf("Erasing sub_sector %d\n", address / SUB_SECTOR_SIZE);
res = write_wen();
if (res != 0)
return res;
res = wfa_run_command(m_pFlashAccss, FC_SSE, INSTRUCTION_ADDRESS_PHASE, true, address, 0);
if (res != 0)
return res;
res = wait_wip();
if (res != 0)
return res;
if (g_debug) DBG("Cmd is 0x%04x, Status is 0x%04x\n",FC_SSE, get_status() );
if (g_debug) {
DBG(" - Erased sub_sector address 0x%04x - ", address);
} else {
INFO("\rErase - ");
}
update_erased_sub_sector(address, true);
return res;
}
u_int8_t flash::get_status() const
{
int res = 0;
res = wfa_run_command(m_pFlashAccss, FC_RDSR, INSTRUCTION_DATA_PHASE, false, 0, 1);
u_int32_t value;
unsigned long size = sizeof(u_int32_t);
res = wfa_get_data(m_pFlashAccss, (BYTE*)&value, size);
WLCT_UNREFERENCED_PARAM(res);
return (u_int8_t)value;
}
int flash::wait_wip (void) const
{
int res = 0;
u_int16_t value;
u_int32_t timeout = 0;
WLCT_UNREFERENCED_PARAM(res);
do {
value = get_status();
if (EXTRACT(value, 0, 1) == 0) {
if (EXTRACT(value, 1, 1) == 1 ) {
ERR("WIP bit is 0 but WEL bit is 1. Status = 0x%04x\n", value);
return (-1);
}
return 0;
}
timeout++;
} while (timeout <= 1000000 );
ERR("wait_wip:: timeout reached\n");
return -1;
}
void flash::update_erased_page (u_int32_t offset, bool erased )
{
m_page_erased[offset/FLASH_PAGE_SIZE] = erased;
}
void flash::update_erased_sub_sector (u_int32_t offset, bool erased )
{
u_int32_t page_offset = offset / SUB_SECTOR_SIZE * SUB_SECTOR_SIZE;
for (u_int32_t i =0; i < PAGES_PER_SUB_SECTOR; i++) {
update_erased_page( page_offset + i * FLASH_PAGE_SIZE, erased );
}
}
void flash::update_erased_sector (u_int32_t offset, bool erased )
{
u_int32_t page_offset = offset / SECTOR_SIZE * SECTOR_SIZE;
for (u_int32_t i =0; i < SUB_SECTORS_PER_SECTOR; i++) {
update_erased_sub_sector( page_offset + i * SUB_SECTOR_SIZE, erased );
}
}
void flash::update_erased_flash (bool erased )
{
for (u_int32_t i =0; i < SECTORS_PER_FLASH; i++) {
update_erased_sector( i * SECTOR_SIZE, erased );
}
}
bool flash::is_erased( u_int32_t page_offset ) const{
u_int32_t page_index = page_offset/FLASH_PAGE_SIZE;
bool erased = m_page_erased[page_index];
return erased;
}
void flash::clear_erased( u_int32_t page_offset ) {
u_int32_t page_index = page_offset/FLASH_PAGE_SIZE;
m_page_erased[page_index] = false;
}
int flash_file::open (const char *device_name, DType dtype, bool ignore_lock = true)
{
//unused param
(void)ignore_lock;
// m_device_name = device_name;
m_handler = fopen( device_name, "rb+");
m_buffer = new char [m_size];
if (!m_buffer) {
ERR("Cannot allocate a buffer of size %u\n", m_size);
return (-1);
}
if (0 == m_handler) { // File doesn't exist
if (g_debug) DBG("file %s type %d does not exist. Creating...\n", device_name, dtype);
m_handler = fopen( device_name, "wb");
if (0 == m_handler) {
ERR("Failed creating file %s type %d\n", device_name, dtype);
return (-1);
}
memset(m_buffer, 0, m_size);
} else {
int res = fread(m_buffer, 1, m_size, (FILE*)m_handler);
if( (u_int32_t)res != m_size ) {
ERR("Expected %d bytes and read %d bytes\n", m_size, res);
return (-1);
}
rewind((FILE*)m_handler);
//res = fclose((FILE*)m_handler);
//if (0 != res) {
// ERR("Failed closing file\n");
// return (-1);
//}
if (g_debug) DBG("Opened file %s type %d\n", device_name, dtype);
}
return 0;
}
void flash_file::close ()
{
//m_handler = fopen( this->m_device_name.c_str(), "w");
//if (0 == m_handler) {
// ERR("Failed opening file for writing\n");
// return (-1);
//}
int res = fwrite(m_buffer, 1, m_size, (FILE*)m_handler);
if( (u_int32_t)res != m_size ) {
ERR("Expected %d bytes and wrote %d bytes\n", m_size, res);
return;
}
res = fclose((FILE*)m_handler);
if (0 != res) {
ERR("Failed closing device\n");
return;
}
if (g_debug ) DBG("Closed device\n");
}
int flash_file::erase (void)
{
memset(m_buffer, 0xff, m_size);
this->update_erased_flash( true );
return 0;
}
flash_file::flash_file(DType device_type)
:flash(device_type)
{
m_buffer = 0;
}
flash_file::~flash_file(void)
{
if (0 != m_handler) {
close ();
m_handler = 0;
}
if (0 != m_buffer) {
delete m_buffer;
m_buffer = 0;
}
}
int flash_file::page_write_program( const u_int32_t address, const u_int16_t length, const BYTE *buffer, u_int8_t type )
{
if( m_bExit ) {
return(-1);
}
if (type == FC_PP) {
if ( !this->is_erased(address) ) {
sub_sector_erase( address );
// sector_erase( address );
}
if (g_debug) DBG("Programing page address 0x%04x length 0x%03x\n", address, length);
}
else {
if (g_debug) DBG("Writing page address 0x%04x length 0x%03x\n", address, length);
}
memcpy(m_buffer+address, buffer, length);
return 0;
}
int flash_file::sub_sector_erase(u_int32_t address)
{
printf("Erasing sub_sector %d\n", address / SUB_SECTOR_SIZE);
memset(m_buffer+address, 0xff, SUB_SECTOR_SIZE);
if (g_debug) {
DBG(" - Erased sub_sector address 0x%04x - ", address);
} else {
INFO("\rErase - ");
}
this->update_erased_sub_sector(address, true);
return 0;
}
int flash_file::read( const u_int32_t address, const u_int32_t length, BYTE *buffer ) const
{
if (address & 0x1) {
ERR("address 0x%04x is not word aligned\n",address);
return(-1);
}
if (length & 0x1) {
ERR("length 0x%04x is not word aligned\n",length);
return(-1);
}
if (address + length > this->m_size) {
ERR("address 0x%04x + length 0x%04x exceeds flash size of 0x%04x\n",address,length,this->m_size);
return(-1);
}
memcpy(buffer, m_buffer+address, length);
return 0;
}