Files
SDK_GD32W51x/MBL/source_ns/mbl_qspi_flash.c
2023-05-18 18:53:00 +08:00

492 lines
20 KiB
C

/*!
\file mbl_qspi_flash.c
\brief Non-secure MBL qspi flash file for GD32W51x WiFi SDK
\version 2021-10-30, V1.0.0, firmware for GD32W51x
*/
/*
Copyright (c) 2021, GigaDevice Semiconductor Inc.
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 "gd32w51x.h"
#include "mbl_qspi_flash.h"
#include "platform_def.h"
/*!
\brief configure qspi flash gpio
\param[in] none
\param[out] none
\retval none
*/
static void qspi_flash_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
gpio_af_set(GPIOA, GPIO_AF_3, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
gpio_af_set(GPIOB, GPIO_AF_3, GPIO_PIN_3 | GPIO_PIN_4);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3 | GPIO_PIN_4);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN_3 | GPIO_PIN_4);
}
/*!
\brief enable qspi flash write
\param[in] none
\param[out] none
\retval none
*/
static void qspi_flash_write_enable(void)
{
qspi_command_struct sCommand;
qspi_autopolling_struct sConfig;
/* Enable write operations ------------------------------------------ */
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = WRITE_ENABLE_CMD;
sCommand.addr_mode = QSPI_ADDR_NONE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = 0;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_NONE;
sCommand.data_length = 0;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
qspi_command(&sCommand);
/* Configure automatic polling mode to wait for write enabling ---- */
sConfig.match = 0x02;
sConfig.mask = 0x02;
sConfig.match_mode = QSPI_MATCH_MODE_AND;
sConfig.statusbytes_size = 1;
sConfig.interval = 0x10;
sConfig.auto_stop = QSPI_AUTO_STOP_ENABLE;
sCommand.instruction = READ_STATUS_REG1_CMD;
sCommand.data_mode = QSPI_DATA_1_LINE;
qspi_autopolling(&sCommand, &sConfig);
}
/*!
\brief configure automatic polling mode to wait for memory ready
\param[in] none
\param[out] none
\retval none
*/
static void qspi_flash_autopolling_ready(void)
{
qspi_command_struct sCommand;
qspi_autopolling_struct sConfig;
/* Configure automatic polling mode to wait for memory ready ------ */
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = READ_STATUS_REG1_CMD;
sCommand.addr_mode = QSPI_ADDR_NONE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = 0;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_1_LINE;
sCommand.data_length = 0;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
sConfig.match = 0x00;
sConfig.mask = 0x01;
sConfig.match_mode = QSPI_MATCH_MODE_AND;
sConfig.statusbytes_size = 1;
sConfig.interval = 0x10;
sConfig.auto_stop = QSPI_AUTO_STOP_ENABLE;
qspi_autopolling(&sCommand, &sConfig);
}
static void qspi_tcfg_fmc_set(void)
{
qspi_command_struct sCommand;
#if ( QSPI_FLASH_MODE == QSPI_FLASH_4_LINES )
sCommand.instruction_mode = (BITS(8,9) & ((uint32_t)(1) << 8)); // 1 line
sCommand.instruction = (BITS(0,7) & (uint32_t)(QUAD_IO_FAST_READ_CMD)); // 0xeb
sCommand.addr_mode = (BITS(10,11) & ((uint32_t)(3) << 10)); // 4 lines
sCommand.addr_size = (BITS(12,13) & ((uint32_t)(2) << 12)); // 24 bits address
sCommand.addr = 0;
sCommand.altebytes_mode = (BITS(14,15) & ((uint32_t)(3) << 14)); // 4 lines
sCommand.altebytes_size = 0; // 8 bits
sCommand.altebytes = 0;
sCommand.dummycycles = (BITS(18,22) & ((uint32_t)(4) << 18)); // 4 dummy cycles
sCommand.data_mode = (BITS(24,25) & ((uint32_t)(3) << 24)); // 4 lines
sCommand.data_length = 0;
sCommand.sioo_mode = 0;
#elif ( QSPI_FLASH_MODE == QSPI_FLASH_2_LINES )
sCommand.instruction_mode = (BITS(8,9) & ((uint32_t)(1) << 8))(0x1); // 1 line
sCommand.instruction = (BITS(0,7) & (uint32_t)(DUAL_IO_FAST_READ_CMD)); // 0xBB
sCommand.addr_mode = (BITS(10,11) & ((uint32_t)(2) << 10)); // 2 lines
sCommand.addr_size = (BITS(12,13) & ((uint32_t)(2) << 12)); // 24 bits address
sCommand.addr = 0;
sCommand.altebytes_mode = (BITS(14,15) & ((uint32_t)(2) << 14)); // 2 lines
sCommand.altebytes_size = 0; // 8 bits
sCommand.altebytes = 0;
/*
according to GD25Q16C datasheet, there are 4 alternate bits and 2 dummy cycles for 0xBB cmd,
but GD32W51x QSPI mode has alternate bits's limit to 8bits, so set sCommand.altebytes_size
to 8, so alternate data[3:0]'s transfer costs 2 cycles on two lines transfer, so set
sCommand.dummycycles to 0(which not means there is no dummy cycles)
*/
sCommand.dummycycles = 0; // 0 dummy cycles
sCommand.data_mode = (BITS(24,25) & ((uint32_t)(2) << 24)); // 2 lines
sCommand.data_length = 0;
sCommand.sioo_mode = 0;
#elif ( QSPI_FLASH_MODE == QSPI_FLASH_1_LINE )
sCommand.instruction_mode = (BITS(8,9) & ((uint32_t)(1) << 8))(0x1); // 1 line
sCommand.instruction = (BITS(0,7) & (uint32_t)(READ_DATA_BYTE_CMD)); // 0x03
sCommand.addr_mode = (BITS(10,11) & ((uint32_t)(1) << 10)); // 1 line
sCommand.addr_size = (BITS(12,13) & ((uint32_t)(2) << 12)); // 24 bits address
sCommand.addr = 0;
sCommand.altebytes_mode = 0; // no alternate byte
sCommand.altebytes_size = 0; // 8 bits
sCommand.altebytes = 0;
sCommand.dummycycles = 0; // 0 dummy cycles
sCommand.data_mode = (BITS(24,25) & ((uint32_t)(1) << 24)); // 1 line
sCommand.data_length = 0;
sCommand.sioo_mode = 0;
#endif
while((QSPI_CTL & QSPI_FLAG_BUSY) != 0U){
}
QSPI_TCFGF = sCommand.instruction_mode | sCommand.instruction | sCommand.addr_mode | \
sCommand.addr_size | sCommand.addr | sCommand.altebytes_mode | sCommand.altebytes | \
sCommand.dummycycles | sCommand.data_mode | sCommand.data_length | sCommand.sioo_mode;
}
/*!
\brief configure qspi flash to quad mode
\param[in] none
\param[out] none
\retval none
*/
static void qspi_flash_quad_enable_set(void)
{
uint32_t id = 0;
uint8_t mode_s = 0;
uint16_t mode = 0;
qspi_command_struct sCommand;
qspi_flash_read_id(&id);
qspi_flash_write_enable();
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = 0;
sCommand.addr_mode = QSPI_ADDR_NONE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = 0;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_1_LINE;
sCommand.data_length = 1;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
sCommand.instruction = READ_STATUS_REG1_CMD;
qspi_command(&sCommand);
qspi_receive(&mode_s);
mode |= (uint16_t)mode_s;
sCommand.instruction = READ_STATUS_REG_CMD;
qspi_command(&sCommand);
qspi_receive(&mode_s);
mode |= (uint16_t)mode_s << 8;
if (mode & 0x0200) {
/* quad mode, do nothing */
} else {
switch ((id & 0x00ff0000) >> 16) {
case 0x16: //GD32Q32
case 0x17: //GD32Q64
case 0x18: //GD32Q128
mode = mode >> 8;
mode |= 0x02;
sCommand.instruction = WRITE_STATUS_REG_CMD;//write flash status[s15-s8]
qspi_command(&sCommand);
qspi_transmit((uint8_t *)&mode);
break;
case 0x14: //GD32Q80
case 0x15: //GD32Q16
case 0x19: //GD32Q256
/* To fully support GD32Q256, user has to set bit8(ADS) of Status Registers
to enable 4-bytes Address Mode, which enabled by cmd 0xB7(the Enter 4-Byte
Address Mode command enables accessing the address length of 32-bit for
the memory area of higher density (larger than 128Mb), The device default
is in 24-bit address mode); and then change all qspi related operations's
address size from QSPI_ADDR_24_BITS to QSPI_ADDR_32_BITS. */
default:
mode |= (0x0200);
sCommand.instruction = WRITE_STATUS_REG1_CMD;//write flash status[s15-s0]
sCommand.data_length = 2;
qspi_command(&sCommand);
qspi_transmit((uint8_t *)&mode);
break;
}
qspi_flash_autopolling_ready();
}
}
/*!
\brief configure qspi flash
\param[in] clock_prescaler: prescaler of qspi clock
\param[out] none
\retval none
*/
void qspi_flash_config(uint32_t clock_prescaler)
{
qspi_init_struct Init;
// rcu_periph_clock_enable(RCU_GTZC);
// tzgpc_tzspc_peripheral_attributes_config(TZGPC_PERIPH_QSPI_FLASHREG, TZGPC_TZSPC_PERIPH_SEC);
qspi_flash_gpio_config();
// qspi_deinit();
rcu_periph_clock_enable(RCU_QSPI);
Init.prescaler = clock_prescaler; /* QSPI clock = AHBCLK/(ClockPrescaler+1) */
Init.fifo_threshold = 4;
Init.sample_shift = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // QSPI_SAMPLE_SHIFTING_NONE;
Init.flash_size = 23; /* 2^(FlashSize+1) ***** number of address bits = FlashSize + 1*/
Init.cs_high_time = QSPI_CS_HIGH_TIME_1_CYCLE;
Init.clock_mode = QSPI_CLOCK_MODE_0;
qspi_init(&Init);
qspi_flash_quad_enable_set();
qspi_tcfg_fmc_set();
}
/*!
\brief erase qspi flash sector
\param[in] address: qspi flash address
\param[out] none
\retval 0
*/
int32_t qspi_flash_sector_erase(uint32_t address)
{
qspi_command_struct sCommand;
qspi_flash_write_enable();
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = SECTOR_ERASE_CMD;
sCommand.addr_mode = QSPI_ADDR_1_LINE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_NONE;
sCommand.data_length = 0;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
qspi_command(&sCommand);
qspi_flash_autopolling_ready();
return 0;
}
/*!
\brief erase qspi flash full chip
\param[in] none
\param[out] none
\retval 0
*/
int32_t qspi_flash_chip_erase(void)
{
qspi_command_struct sCommand;
qspi_flash_write_enable();
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = CHIP_ERASE_CMD;
sCommand.addr_mode = QSPI_ADDR_NONE;
sCommand.addr_size = QSPI_ADDR_8_BITS;
sCommand.addr = 0;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_NONE;
sCommand.data_length = 0;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
qspi_command(&sCommand);
qspi_flash_autopolling_ready();
return 0;
}
/*!
\brief read qspi flash device id
\param[in] None
\param[out] None
\retval 0
*/
int32_t qspi_flash_read_id(void *id)
{
qspi_command_struct sCommand = {0};
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = CHIP_READ_ID_CMD;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_1_LINE;
sCommand.data_length = 3;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
qspi_command(&sCommand);
qspi_receive(id);
return 0;
}
/*!
\brief read qspi flash
\param[in] adress: flash's internal address to read from
\param[out] data: pointer to the buffer that receives the data read from the flash
\param[in] size: size of data read from flash
\retval 0
*/
int32_t qspi_flash_read(uint32_t address, void *data, uint32_t size)
{
qspi_command_struct sCommand;
#if ( QSPI_FLASH_MODE == QSPI_FLASH_4_LINES )
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = QUAD_IO_FAST_READ_CMD;
sCommand.addr_mode = QSPI_ADDR_4_LINES;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_4_LINES;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 4;
sCommand.data_mode = QSPI_DATA_4_LINES;
sCommand.data_length = size;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
#elif ( QSPI_FLASH_MODE == QSPI_FLASH_2_LINES )
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = DUAL_IO_FAST_READ_CMD;
sCommand.addr_mode = QSPI_ADDR_2_LINES;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_2_LINES;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
/*
according to GD25Q16C datasheet, there are 4 alternate bits and 2 dummy cycles for 0xBB cmd,
but GD32W51x QSPI mode has alternate bits's limit to 8bits, so set sCommand.altebytes_size
to 8, so alternate data[3:0]'s transfer costs 2 cycles on two lines transfer, so set
sCommand.dummycycles to 0(which not means there is no dummy cycles)
*/
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_2_LINES;
sCommand.data_length = size;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
#elif ( QSPI_FLASH_MODE == QSPI_FLASH_1_LINE )
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = READ_DATA_BYTE_CMD;
sCommand.addr_mode = QSPI_ADDR_1_LINE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_1_LINE;
sCommand.data_length = size;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
#endif
qspi_command(&sCommand);
qspi_receive(data);
return 0;
}
/*!
\brief program qspi flash page
\param[in] adress: flash's internal address to write to
\param[in] data: pointer to the buffer that receives the data read from the flash
\param[in] size: size of data read from flash
\param[out] none
\retval 0
*/
int32_t qspi_flash_page_program(uint32_t address, const uint8_t *data, uint32_t size)
{
qspi_command_struct sCommand;
/* Writing Sequence 4 Line------------------------------------------ */
qspi_flash_write_enable();
#if ( QSPI_FLASH_MODE == QSPI_FLASH_4_LINES )
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = QUAD_IN_FAST_PROG_CMD;
sCommand.addr_mode = QSPI_ADDR_1_LINE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_4_LINES;
sCommand.data_length = size;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
#elif ( QSPI_FLASH_MODE == QSPI_FLASH_2_LINES ) || ( QSPI_FLASH_MODE == QSPI_FLASH_1_LINE )
sCommand.instruction_mode = QSPI_INSTRUCTION_1_LINE;
sCommand.instruction = PAGE_PROG_CMD;
sCommand.addr_mode = QSPI_ADDR_1_LINE;
sCommand.addr_size = QSPI_ADDR_24_BITS;
sCommand.addr = address;
sCommand.altebytes_mode = QSPI_ALTE_BYTES_NONE;
sCommand.altebytes_size = QSPI_ALTE_BYTES_8_BITS;
sCommand.altebytes = 0;
sCommand.dummycycles = 0;
sCommand.data_mode = QSPI_DATA_1_LINE;
sCommand.data_length = size;
sCommand.sioo_mode = QSPI_SIOO_INST_EVERY_CMD;
#endif
qspi_command(&sCommand);
qspi_transmit((uint8_t *)data);
qspi_flash_autopolling_ready();
return 0;
}