improvements in ext-flash driver

This commit is contained in:
Xenofon (Fontas) Fafoutis 2018-04-04 11:08:18 +01:00
parent 1c335e9faa
commit b4b6ab1bdf
2 changed files with 105 additions and 84 deletions

View File

@ -109,13 +109,22 @@ static spi_device_t flash_spi_configuration_default = {
.spi_pha = 0, .spi_pha = 0,
.spi_pol = 0 .spi_pol = 0
}; };
/*---------------------------------------------------------------------------*/
static spi_device_t *flash_spi_configuration; /**
* Get spi configuration, return default configuration if NULL
*/
static spi_device_t*
get_spi_conf(spi_device_t *conf) {
if(conf == NULL) {
return &flash_spi_configuration_default;
}
return conf;
}/*---------------------------------------------------------------------------*/
/** /**
* Clear external flash CSN line * Clear external flash CSN line
*/ */
static bool static bool
select_on_bus(void) select_on_bus(spi_device_t *flash_spi_configuration)
{ {
if(spi_select(flash_spi_configuration) == SPI_DEV_STATUS_OK) { if(spi_select(flash_spi_configuration) == SPI_DEV_STATUS_OK) {
return true; return true;
@ -127,7 +136,7 @@ select_on_bus(void)
* Set external flash CSN line * Set external flash CSN line
*/ */
static void static void
deselect(void) deselect(spi_device_t *flash_spi_configuration)
{ {
spi_deselect(flash_spi_configuration); spi_deselect(flash_spi_configuration);
} }
@ -137,19 +146,19 @@ deselect(void)
* \return True when successful. * \return True when successful.
*/ */
static bool static bool
wait_ready(void) wait_ready(spi_device_t *flash_spi_configuration)
{ {
bool ret; bool ret;
const uint8_t wbuf[1] = { BLS_CODE_READ_STATUS }; const uint8_t wbuf[1] = { BLS_CODE_READ_STATUS };
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
ret = spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)); ret = spi_write(flash_spi_configuration, wbuf, sizeof(wbuf));
if(ret != SPI_DEV_STATUS_OK) { if(ret != SPI_DEV_STATUS_OK) {
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
@ -164,7 +173,7 @@ wait_ready(void)
if(ret != SPI_DEV_STATUS_OK) { if(ret != SPI_DEV_STATUS_OK) {
/* Error */ /* Error */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
@ -173,7 +182,7 @@ wait_ready(void)
break; break;
} }
} }
deselect(); deselect(flash_spi_configuration);
return true; return true;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -185,23 +194,23 @@ wait_ready(void)
* was powered down * was powered down
*/ */
static uint8_t static uint8_t
verify_part(void) verify_part(spi_device_t *flash_spi_configuration)
{ {
const uint8_t wbuf[] = { BLS_CODE_MDID, 0xFF, 0xFF, 0x00 }; const uint8_t wbuf[] = { BLS_CODE_MDID, 0xFF, 0xFF, 0x00 };
uint8_t rbuf[2] = { 0, 0 }; uint8_t rbuf[2] = { 0, 0 };
bool ret; bool ret;
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return VERIFY_PART_LOCKED; return VERIFY_PART_LOCKED;
} }
if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) { if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) {
deselect(); deselect(flash_spi_configuration);
return VERIFY_PART_ERROR; return VERIFY_PART_ERROR;
} }
ret = spi_read(flash_spi_configuration, rbuf, sizeof(rbuf)); ret = spi_read(flash_spi_configuration, rbuf, sizeof(rbuf));
deselect(); deselect(flash_spi_configuration);
if(ret != SPI_DEV_STATUS_OK) { if(ret != SPI_DEV_STATUS_OK) {
return VERIFY_PART_ERROR; return VERIFY_PART_ERROR;
} }
@ -219,31 +228,31 @@ verify_part(void)
* the status register is accessible. * the status register is accessible.
*/ */
static bool static bool
power_down(void) power_down(spi_device_t *flash_spi_configuration)
{ {
uint8_t cmd; uint8_t cmd;
uint8_t i; uint8_t i;
/* First, wait for the device to be ready */ /* First, wait for the device to be ready */
if(wait_ready() == false) { if(wait_ready(flash_spi_configuration) == false) {
/* Entering here will leave the device in standby instead of powerdown */ /* Entering here will leave the device in standby instead of powerdown */
return false; return false;
} }
cmd = BLS_CODE_PD; cmd = BLS_CODE_PD;
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
if(spi_write_byte(flash_spi_configuration, cmd) != SPI_DEV_STATUS_OK) { if(spi_write_byte(flash_spi_configuration, cmd) != SPI_DEV_STATUS_OK) {
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
deselect(); deselect(flash_spi_configuration);
i = 0; i = 0;
while(i < 10) { while(i < 10) {
if(verify_part() == VERIFY_PART_POWERED_DOWN) { if(verify_part(flash_spi_configuration) == VERIFY_PART_POWERED_DOWN) {
/* Device is powered down */ /* Device is powered down */
return true; return true;
} }
@ -251,7 +260,7 @@ power_down(void)
} }
/* Should not be required */ /* Should not be required */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -260,23 +269,23 @@ power_down(void)
* \return True if the command was written successfully * \return True if the command was written successfully
*/ */
static bool static bool
power_standby(void) power_standby(spi_device_t *flash_spi_configuration)
{ {
uint8_t cmd; uint8_t cmd;
bool success; bool success;
cmd = BLS_CODE_RPD; cmd = BLS_CODE_RPD;
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
success = (spi_write(flash_spi_configuration, &cmd, sizeof(cmd)) == SPI_DEV_STATUS_OK); success = (spi_write(flash_spi_configuration, &cmd, sizeof(cmd)) == SPI_DEV_STATUS_OK);
if(success) { if(success) {
success = wait_ready() == true ? true : false; success = wait_ready(flash_spi_configuration) == true ? true : false;
} }
deselect(); deselect(flash_spi_configuration);
return success; return success;
} }
@ -286,17 +295,17 @@ power_standby(void)
* \return True when successful. * \return True when successful.
*/ */
static bool static bool
write_enable(void) write_enable(spi_device_t *flash_spi_configuration)
{ {
bool ret; bool ret;
const uint8_t wbuf[] = { BLS_CODE_WRITE_ENABLE }; const uint8_t wbuf[] = { BLS_CODE_WRITE_ENABLE };
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
ret = (spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) == SPI_DEV_STATUS_OK); ret = (spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) == SPI_DEV_STATUS_OK);
deselect(); deselect(flash_spi_configuration);
if(ret == false) { if(ret == false) {
return false; return false;
@ -305,8 +314,12 @@ write_enable(void)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_open() ext_flash_open(spi_device_t *conf)
{ {
spi_device_t *flash_spi_configuration;
flash_spi_configuration = get_spi_conf(conf);
/* Check if platform has ext-flash */ /* Check if platform has ext-flash */
if(flash_spi_configuration->pin_spi_sck == 255) { if(flash_spi_configuration->pin_spi_sck == 255) {
return false; return false;
@ -316,37 +329,51 @@ ext_flash_open()
return false; return false;
} }
/* Default output to clear chip select */ /* Default output to clear chip select */
deselect(); deselect(flash_spi_configuration);
/* Put the part is standby mode */ /* Put the part is standby mode */
power_standby(); power_standby(flash_spi_configuration);
return verify_part() == VERIFY_PART_OK ? true : false; if (verify_part(flash_spi_configuration) == VERIFY_PART_OK) {
return true;
}
/* Failed to verify */
spi_release(flash_spi_configuration);
return false;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_close() ext_flash_close(spi_device_t *conf)
{ {
/* Put the part in low power mode */ bool ret;
if(power_down() == false) { spi_device_t *flash_spi_configuration;
return false;
}
flash_spi_configuration = get_spi_conf(conf);
/* Put the part in low power mode */
ret = power_down(flash_spi_configuration);
/* SPI is released no matter if power_down() succeeds or fails */
if(spi_release(flash_spi_configuration) != SPI_DEV_STATUS_OK) { if(spi_release(flash_spi_configuration) != SPI_DEV_STATUS_OK) {
return false; return false;
} }
return true; return ret;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_read(uint32_t offset, uint32_t length, uint8_t *buf) ext_flash_read(spi_device_t *conf, uint32_t offset, uint32_t length, uint8_t *buf)
{ {
uint8_t wbuf[4]; uint8_t wbuf[4];
bool ret; bool ret;
spi_device_t *flash_spi_configuration;
flash_spi_configuration = get_spi_conf(conf);
/* Wait till previous erase/program operation completes */ /* Wait till previous erase/program operation completes */
if(wait_ready() == false) { if(wait_ready(flash_spi_configuration) == false) {
return false; return false;
} }
@ -359,36 +386,40 @@ ext_flash_read(uint32_t offset, uint32_t length, uint8_t *buf)
wbuf[2] = (offset >> 8) & 0xff; wbuf[2] = (offset >> 8) & 0xff;
wbuf[3] = offset & 0xff; wbuf[3] = offset & 0xff;
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) { if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) {
/* failure */ /* failure */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
ret = (spi_read(flash_spi_configuration, buf, length) == SPI_DEV_STATUS_OK); ret = (spi_read(flash_spi_configuration, buf, length) == SPI_DEV_STATUS_OK);
deselect(); deselect(flash_spi_configuration);
return ret; return ret;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_write(uint32_t offset, uint32_t length, const uint8_t *buf) ext_flash_write(spi_device_t *conf, uint32_t offset, uint32_t length, const uint8_t *buf)
{ {
uint8_t wbuf[4]; uint8_t wbuf[4];
uint32_t ilen; /* interim length per instruction */ uint32_t ilen; /* interim length per instruction */
spi_device_t *flash_spi_configuration;
flash_spi_configuration = get_spi_conf(conf);
while(length > 0) { while(length > 0) {
/* Wait till previous erase/program operation completes */ /* Wait till previous erase/program operation completes */
if(wait_ready() == false) { if(wait_ready(flash_spi_configuration) == false) {
return false; return false;
} }
if(write_enable() == false) { if(write_enable(flash_spi_configuration) == false) {
return false; return false;
} }
@ -410,30 +441,30 @@ ext_flash_write(uint32_t offset, uint32_t length, const uint8_t *buf)
* is not imposed here since above instructions * is not imposed here since above instructions
* should be enough to delay * should be enough to delay
* as much. */ * as much. */
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) { if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) {
/* failure */ /* failure */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
if(spi_write(flash_spi_configuration, buf, ilen) != SPI_DEV_STATUS_OK) { if(spi_write(flash_spi_configuration, buf, ilen) != SPI_DEV_STATUS_OK) {
/* failure */ /* failure */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
buf += ilen; buf += ilen;
deselect(); deselect(flash_spi_configuration);
} }
return true; return true;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_erase(uint32_t offset, uint32_t length) ext_flash_erase(spi_device_t *conf, uint32_t offset, uint32_t length)
{ {
/* /*
* Note that Block erase might be more efficient when the floor map * Note that Block erase might be more efficient when the floor map
@ -443,6 +474,10 @@ ext_flash_erase(uint32_t offset, uint32_t length)
uint8_t wbuf[4]; uint8_t wbuf[4];
uint32_t i, numsectors; uint32_t i, numsectors;
uint32_t endoffset = offset + length - 1; uint32_t endoffset = offset + length - 1;
spi_device_t *flash_spi_configuration;
flash_spi_configuration = get_spi_conf(conf);
offset = (offset / EXT_FLASH_ERASE_SECTOR_SIZE) * EXT_FLASH_ERASE_SECTOR_SIZE; offset = (offset / EXT_FLASH_ERASE_SECTOR_SIZE) * EXT_FLASH_ERASE_SECTOR_SIZE;
numsectors = (endoffset - offset + EXT_FLASH_ERASE_SECTOR_SIZE - 1) / EXT_FLASH_ERASE_SECTOR_SIZE; numsectors = (endoffset - offset + EXT_FLASH_ERASE_SECTOR_SIZE - 1) / EXT_FLASH_ERASE_SECTOR_SIZE;
@ -451,11 +486,11 @@ ext_flash_erase(uint32_t offset, uint32_t length)
for(i = 0; i < numsectors; i++) { for(i = 0; i < numsectors; i++) {
/* Wait till previous erase/program operation completes */ /* Wait till previous erase/program operation completes */
if(wait_ready() == false) { if(wait_ready(flash_spi_configuration) == false) {
return false; return false;
} }
if(write_enable() == false) { if(write_enable(flash_spi_configuration) == false) {
return false; return false;
} }
@ -463,16 +498,16 @@ ext_flash_erase(uint32_t offset, uint32_t length)
wbuf[2] = (offset >> 8) & 0xff; wbuf[2] = (offset >> 8) & 0xff;
wbuf[3] = offset & 0xff; wbuf[3] = offset & 0xff;
if(select_on_bus() == false) { if(select_on_bus(flash_spi_configuration) == false) {
return false; return false;
} }
if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) { if(spi_write(flash_spi_configuration, wbuf, sizeof(wbuf)) != SPI_DEV_STATUS_OK) {
/* failure */ /* failure */
deselect(); deselect(flash_spi_configuration);
return false; return false;
} }
deselect(); deselect(flash_spi_configuration);
offset += EXT_FLASH_ERASE_SECTOR_SIZE; offset += EXT_FLASH_ERASE_SECTOR_SIZE;
} }
@ -481,33 +516,19 @@ ext_flash_erase(uint32_t offset, uint32_t length)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool bool
ext_flash_test(void) ext_flash_init(spi_device_t *conf)
{ {
flash_spi_configuration = &flash_spi_configuration_default; if(ext_flash_open(conf) == false) {
if(ext_flash_open() == false) {
return false; return false;
} }
if(ext_flash_close() == false) { if(ext_flash_close(conf) == false) {
return false; return false;
} }
LOG_INFO("Flash test successful\n"); LOG_INFO("Flash init successful\n");
return true; return true;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
bool
ext_flash_init(spi_device_t *conf)
{
if(conf == NULL) {
flash_spi_configuration = &flash_spi_configuration_default;
} else {
flash_spi_configuration = conf;
}
return ext_flash_test();
}
/*---------------------------------------------------------------------------*/
/** @} */ /** @} */

View File

@ -49,19 +49,22 @@
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** /**
* \brief Initialize storage driver. * \brief Initialize storage driver.
* \param SPI bus configuration struct. NULL for default.
* \return True when successful. * \return True when successful.
*/ */
bool ext_flash_open(void); bool ext_flash_open(spi_device_t *conf);
/** /**
* \brief Close the storage driver * \brief Close the storage driver
* \param SPI bus configuration struct. NULL for default.
* *
* This call will put the device in its lower power mode (power down). * This call will put the device in its lower power mode (power down).
*/ */
bool ext_flash_close(void); bool ext_flash_close(spi_device_t *conf);
/** /**
* \brief Read storage content * \brief Read storage content
* \param SPI bus configuration struct. NULL for default.
* \param offset Address to read from * \param offset Address to read from
* \param length Number of bytes to read * \param length Number of bytes to read
* \param buf Buffer where to store the read bytes * \param buf Buffer where to store the read bytes
@ -69,10 +72,11 @@ bool ext_flash_close(void);
* *
* buf must be allocated by the caller * buf must be allocated by the caller
*/ */
bool ext_flash_read(uint32_t offset, uint32_t length, uint8_t *buf); bool ext_flash_read(spi_device_t *conf, uint32_t offset, uint32_t length, uint8_t *buf);
/** /**
* \brief Erase storage sectors corresponding to the range. * \brief Erase storage sectors corresponding to the range.
* \param SPI bus configuration struct. NULL for default.
* \param offset Address to start erasing * \param offset Address to start erasing
* \param length Number of bytes to erase * \param length Number of bytes to erase
* \return True when successful. * \return True when successful.
@ -80,26 +84,22 @@ bool ext_flash_read(uint32_t offset, uint32_t length, uint8_t *buf);
* The erase operation will be sector-wise, therefore a call to this function * The erase operation will be sector-wise, therefore a call to this function
* will generally start the erase procedure at an address lower than offset * will generally start the erase procedure at an address lower than offset
*/ */
bool ext_flash_erase(uint32_t offset, uint32_t length); bool ext_flash_erase(spi_device_t *conf, uint32_t offset, uint32_t length);
/** /**
* \brief Write to storage sectors. * \brief Write to storage sectors.
* \param SPI bus configuration struct. NULL for default.
* \param offset Address to write to * \param offset Address to write to
* \param length Number of bytes to write * \param length Number of bytes to write
* \param buf Buffer holding the bytes to be written * \param buf Buffer holding the bytes to be written
* *
* \return True when successful. * \return True when successful.
*/ */
bool ext_flash_write(uint32_t offset, uint32_t length, const uint8_t *buf); bool ext_flash_write(spi_device_t *conf, uint32_t offset, uint32_t length, const uint8_t *buf);
/**
* \brief Test the flash (power on self-test)
* \return True when successful.
*/
bool ext_flash_test(void);
/** /**
* \brief Initialise the external flash * \brief Initialise the external flash
* \param SPI bus configuration struct. NULL for default.
* *
* This function will explicitly put the part in its lowest power mode * This function will explicitly put the part in its lowest power mode
* (power-down). * (power-down).