blob: 7fe7ab47444000e378281bd68cd444207950d26a [file] [log] [blame]
/**
****************************************************************************************
*
* @file qspi_automode.c
*
* @brief Access QSPI flash when running in auto mode
*
* Copyright (c) 2016, Dialog Semiconductor
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the copyright holder 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include "sdk_defs.h"
#include "hw_cpm.h"
#include "hw_qspi.h"
#include "qspi_automode.h"
/*
* QSPI controller allows to execute code directly from QSPI flash.
* When code is executing from flash there is no possibility to reprogram it.
* To be able to modify flash memory while it is used for code execution it must me assured that
* during the time needed for erase/write no code is running from flash.
*/
/*
* Flash specific defines
*/
/* SUS bit delay after SUSPEND command */
#define FLASH_SUS_DELAY (20) // in usec
/* Flash page size */
#define FLASH_MAX_WRITE_SIZE dg_configFLASH_MAX_WRITE_SIZE
/*
* Use QUAD mode for page write.
*
* Note: If the flash does not support QUAD mode or it is not connected for QUAD mode set it to 0
* (single mode).
*/
#ifndef QUAD_MODE
#define QUAD_MODE 1
#endif
#ifndef ERASE_IN_AUTOMODE
#define ERASE_IN_AUTOMODE 1
#endif
#ifndef FLASH_FORCE_24BIT_ADDRESSING
#define FLASH_FORCE_24BIT_ADDRESSING 0 // Force 24 bit addressing for devices > 128Mbits
#endif
/*
* WARNING: The Autodetect mode will greatly increase both the code and the used RetRAM size!!!!
* Use with extreme caution!!
*/
#if dg_configFLASH_AUTODETECT == 1
#define FLASH_AUTODETECT 1 // Enable Flash auto-detection
#else
#if !defined(dg_configFLASH_MANUFACTURER_ID)
#error Please define dg_configFLASH_MANUFACTURER_ID !!!
#endif
#if !defined(dg_configFLASH_DEVICE_TYPE)
#error Please define dg_configFLASH_DEVICE_TYPE !!!
#endif
#if !defined(dg_configFLASH_DENSITY)
#error Please define dg_configFLASH_DENSITY !!!
#endif
#define FLASH_AUTODETECT 0
#endif
#if FLASH_AUTODETECT == 1
#include "qspi_w25q80ew.h"
#include "qspi_mx25u51245.h"
#include "qspi_gd25lq80b.h"
#else
#ifndef dg_configFLASH_HEADER_FILE
#error Please define macro dg_configFLASH_HEADER_FILE to the header file name that contains the respective implementation
#endif
#include dg_configFLASH_HEADER_FILE
#endif
#if (FLASH_AUTODETECT == 1)
static const qspi_flash_config_t *flash_config_table[] =
{
&flash_w25q80ew_config, // This is the default one
&flash_mx25u51245_config,
&flash_gd25lq80b_config,
};
__RETAINED static qspi_flash_config_t flash_autodetect_config;
__RETAINED qspi_flash_config_t *flash_config;
#endif
/*
* Function definitions
*/
/**
* \brief Set bus mode to single or QUAD mode.
*
* \param[in] mode Can be single (HW_QSPI_BUS_MODE_SINGLE) or quad (HW_QSPI_BUS_MODE_QUAD) mode.
*
* \note DUAL mode page program so is not supported by this function.
*/
static inline void qspi_set_bus_mode(HW_QSPI_BUS_MODE mode) __attribute__((always_inline));
static inline void qspi_set_bus_mode(HW_QSPI_BUS_MODE mode)
{
if (mode == HW_QSPI_BUS_MODE_SINGLE)
{
QSPIC->QSPIC_CTRLBUS_REG = REG_MSK(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_SINGLE);
QSPIC->QSPIC_CTRLMODE_REG |=
BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO2_OEN, 1) |
BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO2_DAT, 1) |
BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO3_OEN, 1) |
BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO3_DAT, 1);
}
else
{
#if QUAD_MODE
QSPIC->QSPIC_CTRLBUS_REG = REG_MSK(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_QUAD);
QSPIC->QSPIC_CTRLMODE_REG &=
~(BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO2_OEN, 1) |
BITS32(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_IO3_OEN, 1));
#endif
}
}
/**
* \brief Set the mode of the QSPI controller (manual or auto)
*
* \param[in] automode True for auto and false for manual mode setting.
*/
static inline void qspi_set_automode(bool automode) __attribute__((always_inline));
static inline void qspi_set_automode(bool automode)
{
REG_SETF(QSPIC, QSPIC_CTRLMODE_REG, QSPIC_AUTO_MD, automode);
}
/**
* \brief Write to the Flash the contents of a buffer
*
* \param[in] wbuf Pointer to the beginning of the buffer
* \param[in] wlen The number of bytes to be written
*
* \note The data are transferred as bytes (8 bits wide). No optimization is done in trying to use
* faster access methods (i.e. transfer words instead of bytes whenever it is possible).
*/
QSPI_SECTION static void qspi_write(const uint8_t *wbuf, size_t wlen)
{
size_t i;
hw_qspi_cs_enable();
for (i = 0; i < wlen; ++i)
{
hw_qspi_write8(wbuf[i]);
}
hw_qspi_cs_disable();
}
/**
* \brief Write an arbitrary number of bytes to the Flash and then read an arbitrary number of bytes
* from the Flash in one transaction
*
* \param[in] wbuf Pointer to the beginning of the buffer that contains the data to be written
* \param[in] wlen The number of bytes to be written
* \param[in] rbuf Pointer to the beginning of the buffer than the read data are stored
* \param[in] rlen The number of bytes to be read
*
* \note The data are transferred as bytes (8 bits wide). No optimization is done in trying to use
* faster access methods (i.e. transfer words instead of bytes whenever it is possible).
*/
QSPI_SECTION static void qspi_transact(const uint8_t *wbuf, size_t wlen, uint8_t *rbuf, size_t rlen)
{
size_t i;
hw_qspi_cs_enable();
for (i = 0; i < wlen; ++i)
{
hw_qspi_write8(wbuf[i]);
}
for (i = 0; i < rlen; ++i)
{
rbuf[i] = hw_qspi_read8();
}
hw_qspi_cs_disable();
}
QSPI_SECTION static inline bool flash_erase_program_in_progress(void) __attribute__((always_inline));
QSPI_SECTION static inline bool flash_erase_program_in_progress(void)
{
__DBG_QSPI_VOLATILE__ uint8_t status;
uint8_t cmd[] = { flash_config->read_erase_progress_opcode };
qspi_transact(cmd, 1, &status, 1);
return ((status & (1 << flash_config->erase_in_progress_bit)) != 0) == flash_config->erase_in_progress_bit_high_level;
}
QSPI_SECTION static inline bool flash_is_busy(void) __attribute__((always_inline));
QSPI_SECTION static inline bool flash_is_busy(void)
{
return (flash_read_status_register() & FLASH_STATUS_BUSY_MASK) != 0;
}
/**
* \brief Exit from continuous mode.
*/
QSPI_SECTION static inline void flash_reset_continuous_mode(HW_QSPI_BREAK_SEQ_SIZE break_seq_size) __attribute__((
always_inline));
QSPI_SECTION static inline void flash_reset_continuous_mode(HW_QSPI_BREAK_SEQ_SIZE break_seq_size)
{
hw_qspi_cs_enable();
hw_qspi_write8(CMD_EXIT_CONTINUOUS_MODE);
if (break_seq_size == HW_QSPI_BREAK_SEQ_SIZE_2B)
{
hw_qspi_write8(CMD_EXIT_CONTINUOUS_MODE);
}
hw_qspi_cs_disable();
while (flash_is_busy());
}
/**
* \brief Get Device ID when Flash is not in Power Down mode
*
* \return uint8_t The Device ID of the Flash
*
* \note The function blocks until the Flash executes the command.
*/
QSPI_SECTION __attribute__((unused)) static uint8_t flash_get_id(void)
{
uint8_t id;
hw_qspi_cs_enable();
hw_qspi_write32(CMD_RELEASE_POWER_DOWN);
id = hw_qspi_read8();
hw_qspi_cs_disable();
while (flash_is_busy());
return id;
}
/**
* \brief Set WEL (Write Enable Latch) bit of the Status Register of the Flash
* \details The WEL bit must be set prior to every Page Program, Quad Page Program, Sector Erase,
* Block Erase, Chip Erase, Write Status Register and Erase/Program Security Registers
* instruction. In the case of Write Status Register command, any status bits will be written
* as non-volatile bits.
*
* \note This function blocks until the Flash has processed the command and it will be repeated if,
* for any reason, the command was not successfully executed by the Flash.
*/
QSPI_SECTION static void flash_write_enable(void)
{
__DBG_QSPI_VOLATILE__ uint8_t status;
uint8_t cmd[] = { CMD_WRITE_ENABLE };
do
{
qspi_write(cmd, 1);
/* Verify */
do
{
status = flash_read_status_register();
}
while (status & FLASH_STATUS_BUSY_MASK);
}
while (!(status & FLASH_STATUS_WEL_MASK));
}
/**
* \brief Read the Status Register 1 of the Flash
*
* \return uint8_t The value of the Status Register 1 of the Flash.
*/
QSPI_SECTION static uint8_t flash_read_status_register(void)
{
__DBG_QSPI_VOLATILE__ uint8_t status;
uint8_t cmd[] = { CMD_READ_STATUS_REGISTER };
qspi_transact(cmd, 1, &status, 1);
return status;
}
/**
* \brief Write the Status Register 1 of the Flash
*
* \param[in] value The value to be written.
*
* \note This function blocks until the Flash has processed the command. No verification that the
* value has been actually written is done though. It is up to the caller to decide whether
* such verification is needed or not and execute it on its own.
*/
QSPI_SECTION __attribute__((unused)) static void flash_write_status_register(uint8_t value)
{
uint8_t cmd[2] = { CMD_WRITE_STATUS_REGISTER, value };
qspi_write(cmd, 2);
/* Wait for the Flash to process the command */
while (flash_is_busy());
}
/**
* \brief Fast copy of a buffer to a FIFO
* \details Implementation of a fast copy of the contents of a buffer to a FIFO in assembly. All
* addresses are word aligned.
*
* \param[in] start Pointer to the beginning of the buffer
* \param[in] end Pointer to the end of the buffer
* \param[in] Pointer to the FIFO
*
* \warning No validity checks are made! It is the responsibility of the caller to make sure that
* sane values are passed to this function.
*/
static inline
void fast_write_to_fifo32(uint32_t start, uint32_t end, uint32_t dest) __attribute__((always_inline));
static inline void fast_write_to_fifo32(uint32_t start, uint32_t end, uint32_t dest)
{
__asm volatile("copy: \n"
" ldmia %[start]!, {r3} \n"
" str r3, [%[dest]] \n"
" cmp %[start], %[end] \n"
" blt copy \n"
:
: /* output */
[start] "l"(start), [end] "r"(end), [dest] "l"(dest) : /* inputs (%0, %1, %2) */
"r3"); /* registers that are destroyed */
}
/**
* \brief Write data (up to 1 page) to Flash
*
* \param[in] addr The address of the Flash where the data will be written. It may be anywhere in a
* page.
* \param[in] buf Pointer to the beginning of the buffer that contains the data to be written.
* \param[in] size The number of bytes to be written.
*
* \return size_t The number of bytes written.
*
* \warning The boundary of the page where addr belongs to, will not be crossed! The caller should
* issue another flash_write_page() call in order to write the remaining data to the next
* page.
*/
QSPI_SECTION static size_t flash_write_page(uint32_t addr, const uint8_t *buf, uint16_t size)
{
size_t i = 0;
size_t odd = ((uint32_t) buf) & 3;
size_t size_aligned32;
size_t tmp;
DBG_SET_HIGH(FLASH_DEBUG, FLASHDBG_PAGE_PROG);
flash_write_enable();
/* Reduce max write size, that can reduce interrupt latency time */
if (size > FLASH_MAX_WRITE_SIZE)
{
size = FLASH_MAX_WRITE_SIZE;
}
/* Make sure write will not cross page boundary */
tmp = 256 - (addr & 0xFF);
if (size > tmp)
{
size = tmp;
}
hw_qspi_cs_enable();
if (flash_config->address_size == HW_QSPI_ADDR_SIZE_32)
{
hw_qspi_write8(flash_config->page_program_opcode);
#if QUAD_MODE
if (flash_config->quad_page_program_address == true)
{
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
}
#endif
hw_qspi_write32((addr >> 24) | ((addr >> 8) & 0xFF00) | ((addr << 8) & 0xFF0000) | (addr << 24));
#if QUAD_MODE
if (flash_config->quad_page_program_address == false)
{
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
}
#endif
}
else
{
if (flash_config->quad_page_program_address == true)
{
hw_qspi_write8(flash_config->page_program_opcode);
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
#endif
hw_qspi_write16(((addr >> 16) & 0xFF) | (addr & 0xFF00));
hw_qspi_write8(addr & 0xFF);
}
else
{
hw_qspi_write32(CMD_QUAD_PAGE_PROGRAM | ((addr >> 8) & 0xFF00) | ((addr << 8) & 0xFF0000) | (addr << 24));
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
#endif
}
}
if (odd)
{
odd = 4 - odd;
for (i = 0; i < odd && i < size; ++i)
{
hw_qspi_write8(buf[i]);
}
}
size_aligned32 = ((size - i) & ~0x3);
if (size_aligned32)
{
fast_write_to_fifo32((uint32_t)(buf + i), (uint32_t)(buf + i + size_aligned32),
(uint32_t) & (QSPIC->QSPIC_WRITEDATA_REG));
i += size_aligned32;
}
for (; i < size; i++)
{
hw_qspi_write8(buf[i]);
}
hw_qspi_cs_disable();
DBG_SET_LOW(FLASH_DEBUG, FLASHDBG_PAGE_PROG);
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
#endif
return i;
}
/**
* \brief Erase a sector of the Flash
*
* \param[in] addr The address of the sector to be erased.
*
* \note This function blocks until the Flash has processed the command.
*/
QSPI_SECTION __attribute__((unused)) static void flash_erase_sector(uint32_t addr)
{
flash_write_enable();
if (flash_config->address_size == HW_QSPI_ADDR_SIZE_32)
{
uint8_t cmd[5] = { flash_config->erase_opcode, addr >> 24, addr >> 16, addr >> 8, addr };
qspi_write(cmd, 5);
}
else
{
uint8_t cmd[4] = { flash_config->erase_opcode, addr >> 16, addr >> 8, addr };
qspi_write(cmd, 4);
}
/* Wait for the Flash to process the command */
while (flash_erase_program_in_progress());
}
/**
* \brief Check if the Flash can accept commands
*
* \return bool True if the Flash is not busy else false.
*
*/
QSPI_SECTION_NO_INLINE static bool qspi_writable(void)
{
bool writable;
/*
* From now on QSPI may not be available, turn off interrupts.
*/
GLOBAL_INT_DISABLE();
/*
* Turn on command entry mode.
*/
flash_activate_command_entry_mode();
/*
* Check if flash is ready.
*/
writable = !(flash_is_busy());
/*
* Restore auto mode.
*/
flash_deactivate_command_entry_mode();
/*
* Let other code to be executed including QSPI one.
*/
GLOBAL_INT_RESTORE();
return writable;
}
/**
* \brief Get the status of an Erase when it is done automatically by the QSPI controller
*
* \return uint8_t The status of the Erase
* 0: No Erase
* 1: Pending erase request
* 2: Erase procedure is running
* 3: Suspended Erase procedure
* 4: Finishing the Erase procedure
*/
QSPI_SECTION static uint8_t qspi_get_erase_status(void)
{
QSPIC->QSPIC_CHCKERASE_REG = 0;
return HW_QSPIC_REG_GETF(ERASECTRL, ERS_STATE);
}
/**
* \brief Get the address size used by the QSPI controller
*
* \return The address size used (HW_QSPI_ADDR_SIZE_24 or HW_QSPI_ADDR_SIZE_32).
*/
static inline HW_QSPI_ADDR_SIZE qspi_get_address_size(void) __attribute__((always_inline));
static inline HW_QSPI_ADDR_SIZE qspi_get_address_size(void)
{
return (HW_QSPI_ADDR_SIZE)HW_QSPIC_REG_GETF(CTRLMODE, USE_32BA);
}
__RETAINED_CODE void flash_activate_command_entry_mode(void)
{
/*
* Turn off auto mode to allow write.
*/
qspi_set_automode(false);
/*
* Switch to single mode for command entry.
*/
qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
/*
* Exit continuous mode (QPI mode), after this the flash will interpret commands again.
*/
if (flash_config->send_once != 0)
{
flash_reset_continuous_mode(flash_config->break_seq_size);
}
}
__RETAINED_CODE void flash_deactivate_command_entry_mode(void)
{
flash_config->deactivate_command_entry_mode();
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
#endif
qspi_set_automode(true);
}
#if (dg_configDISABLE_BACKGROUND_FLASH_OPS == 0)
__RETAINED_CODE void flash_erase_sector_manual_mode(uint32_t addr)
{
/*
* Turn on command entry mode.
*/
flash_activate_command_entry_mode();
/*
* Issue the erase sector command.
*/
flash_write_enable();
if (flash_config->address_size == HW_QSPI_ADDR_SIZE_32)
{
uint8_t cmd[5] = { flash_config->erase_opcode, addr >> 24, addr >> 16, addr >> 8, addr };
qspi_write(cmd, 5);
}
else
{
uint8_t cmd[4] = { flash_config->erase_opcode, addr >> 16, addr >> 8, addr };
qspi_write(cmd, 4);
}
/*
* Flash stays in manual mode.
*/
}
__RETAINED_CODE size_t flash_program_page_manual_mode(uint32_t addr, const uint8_t *buf, uint16_t size)
{
size_t written = flash_write_page(addr, buf, size);
/*
* Flash stays in manual mode.
*/
return written;
}
__RETAINED_CODE bool qspi_check_program_erase_in_progress(void)
{
return flash_is_busy();
}
__RETAINED_CODE bool qspi_check_and_suspend(void)
{
uint8_t cmd[] = { flash_config->erase_suspend_opcode };
bool am = hw_qspi_get_automode();
bool ret = true;
if (am)
{
/*
* Turn on command entry mode.
*/
flash_activate_command_entry_mode();
}
/*
* Suspend action.
*/
DBG_SET_HIGH(FLASH_DEBUG, FLASHDBG_SUSPEND_ACTION);
/*
* Check if an operation is ongoing.
*/
while (flash_erase_program_in_progress())
{
qspi_write(cmd, 1);
}
hw_cpm_delay_usec(FLASH_SUS_DELAY); // Wait for SUS bit to be updated
DBG_SET_LOW(FLASH_DEBUG, FLASHDBG_SUSPEND_ACTION);
if (flash_config->is_suspended() == false)
{
ret = false;
}
/*
* Restore auto mode.
*/
flash_deactivate_command_entry_mode();
return ret;
}
__RETAINED_CODE void qspi_resume(void)
{
uint8_t cmd[] = { flash_config->erase_resume_opcode };
do
{
/*
* Turn on command entry mode.
*/
flash_activate_command_entry_mode();
/*
* Check if suspended.
*/
if (flash_config->is_suspended() == false)
{
break;
}
/*
* Wait for flash to become ready again.
*/
do
{
/*
* Resume action.
*/
qspi_write(cmd, 1);
/*
* Check if SUS bit is cleared.
*/
}
while (flash_config->is_suspended());
}
while (0);
/*
* Flash stays in manual mode.
*/
}
#endif
/**
* \brief Erase sector (via CPM background processing or using the QSPI controller)
*
* \details This function will execute a Flash sector erase operation. The operation will either be
* carried out immediately (dg_configDISABLE_BACKGROUND_FLASH_OPS is set to 1) or it will be
* deferred to be executed by the CPM when the system becomes idle (when
* dg_configDISABLE_BACKGROUND_FLASH_OPS is set to 0, default value). In the latter case, the
* caller will block until the CPM completes the registered erase operation.
*
* \param[in] addr The address of the sector to be erased.
*/
QSPI_SECTION_NO_INLINE static void qspi_erase_sector(uint32_t addr)
{
/* Wait for previous erase to end */
while (qspi_get_erase_status() != 0)
{
}
if (qspi_get_address_size() == HW_QSPI_ADDR_SIZE_32)
{
addr >>= 12;
}
else
{
addr >>= 4;
}
/* Setup erase block page */
HW_QSPIC_REG_SETF(ERASECTRL, ERS_ADDR, addr);
/* Fire erase */
HW_QSPIC_REG_SETF(ERASECTRL, ERASE_EN, 1);
}
/**
* \brief Write data to a page in the Flash (via CPM background processing or using the QSPI
* controller)
*
* \details This function will execute a Flash program page operation. The operation will either be
* carried out immediately (dg_configDISABLE_BACKGROUND_FLASH_OPS is set to 1) or it will be
* deferred to be executed by the CPM when the system becomes idle (when
* dg_configDISABLE_BACKGROUND_FLASH_OPS is set to 0, default value). In the latter case, the
* caller will block until the CPM completes the registered page program operation.
*
* \param[in] addr The address of the Flash where the data will be written. It may be anywhere in a
* page.
* \param[in] buf Pointer to the beginning of the buffer that contains the data to be written.
* \param[in] size The number of bytes to be written.
*
* \return size_t The number of bytes written.
*
* \warning The caller should ensure that buf does not point to QSPI mapped memory.
*/
QSPI_SECTION_NO_INLINE static size_t write_page(uint32_t addr, const uint8_t *buf, uint16_t size)
{
size_t written;
/*
* From now on QSPI may not be available, turn of interrupts.
*/
GLOBAL_INT_DISABLE();
/*
* Turn on command entry mode.
*/
flash_activate_command_entry_mode();
/*
* Write data into the page of the Flash.
*/
written = flash_write_page(addr, buf, size);
/* Wait for the Flash to process the command */
while (flash_erase_program_in_progress());
/*
* Restore auto mode.
*/
flash_deactivate_command_entry_mode();
/*
* Let other code to be executed including QSPI one.
*/
GLOBAL_INT_RESTORE();
return written;
}
/**
* \brief Erase a sector of the Flash
*
* \details The time and the way that the operation will be carried out depends on the following
* settings:
* ERASE_IN_AUTOMODE = 0: the command is issued immediately in manual mode
* ERASE_IN_AUTOMODE = 1:
* dg_configDISABLE_BACKGROUND_FLASH_OPS = 0: the operation is executed manually by the
* CPM when the system becomes idle
* dg_configDISABLE_BACKGROUND_FLASH_OPS = 1: the operation is executed automatically
* by the QSPI controller.
*
* \param[in] addr The address of the sector to be erased.
*/
QSPI_SECTION_NO_INLINE static void erase_sector(uint32_t addr)
{
#if ERASE_IN_AUTOMODE
/*
* Erase sector in automode
*/
qspi_erase_sector(addr);
/*
* Wait for erase to finish
*/
while (qspi_get_erase_status())
{
}
#else
/*
* From now on QSPI may not be available, turn of interrupts.
*/
GLOBAL_INT_DISABLE();
/*
* Turn off auto mode to allow write.
*/
qspi_set_automode(false);
qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
/*
* Get the Flash out of Power Down mode.
*/
flash_release_power_down();
/*
* Exit continuous mode, after this the flash will interpret commands again.
*/
flash_reset_continuous_mode(flash_config->break_seq_size);
/*
* Execute erase command.
*/
flash_erase_sector(addr);
/*
* Restore auto mode.
*/
flash_deactivate_command_entry_mode();
/*
* Let other code to be executed including QSPI one.
*/
GLOBAL_INT_RESTORE();
#endif
}
uint32_t qspi_automode_get_code_buffer_size(void)
{
/* Return 1 in case some code will pass this value to memory allocation function */
return 1;
}
void qspi_automode_set_code_buffer(void *ram)
{
(void) ram; /* Unused */
}
bool qspi_automode_writable(void)
{
return qspi_writable();
}
size_t qspi_automode_write_flash_page(uint32_t addr, const uint8_t *buf, size_t size)
{
while (!qspi_automode_writable())
{
}
return write_page(addr, buf, size);
}
void qspi_automode_erase_flash_sector(uint32_t addr)
{
while (!qspi_automode_writable())
{
}
erase_sector(addr);
}
void qspi_automode_erase_chip(void)
{
flash_activate_command_entry_mode();
hw_qspi_cs_enable();
hw_qspi_write8(CMD_WRITE_ENABLE);
hw_qspi_cs_disable();
hw_qspi_cs_enable();
hw_qspi_write8(CMD_CHIP_ERASE);
hw_qspi_cs_disable();
hw_qspi_cs_enable();
hw_qspi_write8(CMD_READ_STATUS_REGISTER);
while (hw_qspi_read8() & FLASH_STATUS_BUSY_MASK);
hw_qspi_cs_disable();
flash_deactivate_command_entry_mode();
}
size_t qspi_automode_read(uint32_t addr, uint8_t *buf, size_t len)
{
memcpy(buf, (void *)(MEMORY_QSPIF_BASE + addr), len);
return len;
}
QSPI_SECTION void qspi_automode_flash_power_up(void)
{
hw_cpm_delay_usec(flash_config->power_down_delay);
/* Interrupts must be turned off since the flash goes in manual mode, and
* code (e.g. for an ISR) cannot be fetched from flash during this time
*/
GLOBAL_INT_DISABLE();
// Do not call flash_activate_command_entry_mode(). This function will call
// flash_reset_continuous_mode which will try to send break sequence to the QSPI Flash
// which is in power-down mode.
qspi_set_automode(false);
qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
hw_qspi_cs_enable();
hw_qspi_write8(CMD_RELEASE_POWER_DOWN);
hw_qspi_cs_disable();
flash_deactivate_command_entry_mode();
/*
* The flash is in auto mode again. Re-enable the interrupts
*/
GLOBAL_INT_RESTORE();
hw_cpm_delay_usec(flash_config->release_power_down_delay);
}
QSPI_SECTION void qspi_automode_flash_power_down(void)
{
flash_activate_command_entry_mode();
hw_qspi_cs_enable();
hw_qspi_write8(CMD_ENTER_POWER_DOWN);
hw_qspi_cs_disable();
// Do not call flash_deactivate_command_entry_mode(). This function will call
// flash_config->deactivate_command_entry_mode() which will try to send commands to the QSPI Flash
// which has already been set in power-down mode.
// flash_deactivate_command_entry_mode();
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
#endif
qspi_set_automode(true);
}
static qspi_config qspi_cfg =
{
HW_QSPI_ADDR_SIZE_24, HW_QSPI_POL_HIGH, HW_QSPI_SAMPLING_EDGE_NEGATIVE
};
/**
* \brief Read the JEDEC manufacturer ID, device type and device density using command 0x9F
*
* \param[in] manufacturer_id Pointer to the variable where the manufacturer ID will be returned
* \param[in] device_type Pointer to the variable where the device type will be returned
* \param[in] density Pointer to the variable where the device density will be returned
*/
#if FLASH_AUTODETECT
QSPI_SECTION static void flash_read_jedec_id(uint8_t *manufacturer_id, uint8_t *device_type, uint8_t *density)
{
uint8_t cmd[] = { CMD_READ_JEDEC_ID };
uint8_t buffer[3];
qspi_set_automode(false);
qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
// reset continuous mode using both one and two break bytes to cover all cases
flash_reset_continuous_mode(HW_QSPI_BREAK_SEQ_SIZE_2B);
flash_reset_continuous_mode(HW_QSPI_BREAK_SEQ_SIZE_1B);
qspi_transact(cmd, 1, buffer, 3);
*manufacturer_id = buffer[0];
*device_type = buffer[1];
*density = buffer[2];
#if QUAD_MODE
qspi_set_bus_mode(HW_QSPI_BUS_MODE_QUAD);
#endif
qspi_set_automode(true);
}
#endif
/**
* \brief Configure dummy bytes in QSPI controller
*
* \param[in] count Number of dummy bytes (not including extra byte)
*/
QSPI_SECTION __attribute__((unused)) static void qspi_automode_set_dummy_bytes_count(uint8_t count)
{
if (count == 3)
{
HW_QSPIC_REG_SETF(BURSTCMDB, DMY_FORCE, 1);
}
else
{
QSPIC->QSPIC_BURSTCMDB_REG =
(QSPIC->QSPIC_BURSTCMDB_REG &
~(REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_FORCE) |
REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_NUM))) |
BITS32(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_NUM,
dummy_num[count]);
}
}
__RETAINED_CODE bool qspi_automode_init(void)
{
uint8_t device_type;
uint8_t device_density;
hw_qspi_enable_clock();
#if __DBG_QSPI_ENABLED
REG_SETF(CRG_TOP, CLK_AMBA_REG, QSPI_DIV, 3);
#endif
#if FLASH_AUTODETECT
uint8_t manufacturer_id;
uint8_t i;
flash_read_jedec_id(&manufacturer_id, &device_type, &device_density);
const qspi_flash_config_t *flash_config_init = flash_config_table[0]; // default to first entry.
for (i = 0; i < sizeof(flash_config_table) / sizeof(qspi_flash_config_t *); i++)
{
if ((flash_config_table[i]->manufacturer_id == manufacturer_id) &&
(flash_config_table[i]->device_type == device_type) &&
(flash_config_table[i]->device_density == device_density))
{
flash_config_init = flash_config_table[i];
break;
}
}
flash_autodetect_config = *flash_config_init;
flash_config = &flash_autodetect_config;
#else
device_type = dg_configFLASH_DEVICE_TYPE;
device_density = dg_configFLASH_DENSITY;
#endif
/*
* Copy the selected flash struct from flash into retram
*/
flash_config->initialize(device_type, device_density);
uint8_t read_opcode;
if (flash_config->address_size == HW_QSPI_ADDR_SIZE_32)
{
read_opcode = CMD_FAST_READ_QUAD_4B;
qspi_cfg.address_size = HW_QSPI_ADDR_SIZE_32; // It will be set when hw_qspi_init() is called
}
else
{
read_opcode = CMD_FAST_READ_QUAD;
}
/*
* Setup erase instruction that will be sent by QSPI controller to erase sector in automode.
*/
hw_qspi_set_erase_instruction(flash_config->erase_opcode, HW_QSPI_BUS_MODE_SINGLE,
HW_QSPI_BUS_MODE_SINGLE, 15, 5);
/*
* Setup instruction pair that will temporarily suspend erase operation to allow read.
*/
hw_qspi_set_suspend_resume_instructions(flash_config->erase_suspend_opcode, HW_QSPI_BUS_MODE_SINGLE,
flash_config->erase_resume_opcode, HW_QSPI_BUS_MODE_SINGLE, 7);
/*
* QSPI controller must send write enable before erase, this sets it up.
*/
hw_qspi_set_write_enable_instruction(CMD_WRITE_ENABLE, HW_QSPI_BUS_MODE_SINGLE);
/*
* Setup instruction that will be used to periodically check erase operation status.
* Check LSB which is 1 when erase is in progress.
*/
hw_qspi_set_read_status_instruction(flash_config->read_erase_progress_opcode, HW_QSPI_BUS_MODE_SINGLE,
HW_QSPI_BUS_MODE_SINGLE, flash_config->erase_in_progress_bit, flash_config->erase_in_progress_bit_high_level ? 1 : 0,
20, 0);
/*
* This sequence is necessary if flash is working in continuous read mode, when instruction
* is not sent on every read access just address. Sending 0xFFFF will exit this mode.
* This sequence is sent only when QSPI is working in automode and decides to send one of
* instructions above.
* If flash is working in DUAL bus mode sequence should be 0xFFFF and size should be
* HW_QSPI_BREAK_SEQ_SIZE_2B.
*/
hw_qspi_set_break_sequence(0xFFFF, HW_QSPI_BUS_MODE_SINGLE, flash_config->break_seq_size, 0);
/*
* If application starts from FLASH then bootloader must have set read instruction.
*/
if (dg_configCODE_LOCATION != NON_VOLATILE_IS_FLASH)
{
hw_qspi_init(&qspi_cfg);
hw_qspi_set_div(HW_QSPI_DIV_1);
}
#ifdef ENABLE_AUTOMODE_CONFIGURATION_FOR_READ
flash_activate_command_entry_mode();
hw_qspi_set_read_instruction(read_opcode, flash_config->send_once, flash_config->get_dummy_bytes(),
HW_QSPI_BUS_MODE_SINGLE,
HW_QSPI_BUS_MODE_QUAD, HW_QSPI_BUS_MODE_QUAD,
HW_QSPI_BUS_MODE_QUAD);
hw_qspi_set_extra_byte(flash_config->extra_byte, HW_QSPI_BUS_MODE_QUAD, 0);
hw_qspi_set_address_size(flash_config->address_size);
flash_deactivate_command_entry_mode();
HW_QSPIC_REG_SETF(BURSTCMDB, CS_HIGH_MIN, 0);
#else
(void) read_opcode;
#endif
return true;
}
QSPI_SECTION void qspi_automode_sys_clock_cfg(sys_clk_t sys_clk)
{
if ((CRG_TOP->CLK_AMBA_REG & (1 << REG_POS(CRG_TOP, CLK_AMBA_REG, QSPI_ENABLE))) != 0) // If clock is enabled
{
/* Some of the sys_clk_cfg() implementations put the flash in command entry mode, where the flash is
* not available for code execution. We must make sure that no interrupt (that may cause a cache miss)
* hits during this time.
*/
GLOBAL_INT_DISABLE();
flash_config->sys_clk_cfg(sys_clk);
GLOBAL_INT_RESTORE();
}
}
const qspi_ucode_t *qspi_automode_get_ucode(void)
{
return &flash_config->ucode_wakeup;
}