pinebuds/platform/drivers/norflash/norflash_drv.c
2022-08-15 17:20:27 +08:00

1620 lines
41 KiB
C

/***************************************************************************
*
* Copyright 2015-2019 BES.
* All rights reserved. All unpublished rights reserved.
*
* No part of this work may be used or reproduced in any form or by any
* means, or stored in a database or retrieval system, without prior written
* permission of BES.
*
* Use of this work is governed by a license granted by BES.
* This work contains confidential and proprietary information of
* BES. which is protected by copyright, trade secret,
* trademark and other intellectual property rights.
*
****************************************************************************/
#include "plat_types.h"
#include "cmsis.h"
#include "norflash_drv.h"
#include "cmsis.h"
#include "hal_cache.h"
#include "hal_norflaship.h"
#include "hal_norflash.h"
#include "hal_sleep.h"
#include "hal_trace.h"
#include "hal_timer.h"
#include "norflash_gd25q32c.h"
#include "plat_addr_map.h"
#include "tool_msg.h"
#ifdef PROGRAMMER
#include "task_schedule.h"
#else
#define TASK_SCHEDULE true
#endif
#if (CHIP_FLASH_CTRL_VER >= 2) && !defined(ROM_BUILD) && !defined(PROGRAMMER) && !(defined(CHIP_BEST2001) && (defined(PSRAMUHS_ENABLE) || defined(PSRAM_ENABLE)))
#define FLASH_BURST_WRAP
#endif
#define SAMP_DELAY_PRIO_FALLING_EDGE
#define NORFLASH_UNIQUE_ID_LEN 18
#define NORFLASH_MAX_DIV 0xFF
#define NORFLASH_DEFAULT_MAX_SPEED (104 * 1000 * 1000)
#define NORFLASH_DIV1_MAX_SPEED (99 * 1000 * 1000)
#define NORFLASH_SPEED_RATIO_DENOMINATOR 8
#define NORFLASH_PUYA_ID_PREFIX 0x85
#define NORFLASH_XTS_ID_PREFIX 0x0B
#define XTS_UNIQUE_ID_LEN 16
#define XTS_UNIQUE_ID_CMD 0x5A
#define XTS_UNIQUE_ID_PARAM 0x00019400
// GigaDevice
extern const struct NORFLASH_CFG_T gd25lq64c_cfg;
extern const struct NORFLASH_CFG_T gd25lq32c_cfg;
extern const struct NORFLASH_CFG_T gd25lq16c_cfg;
extern const struct NORFLASH_CFG_T gd25lq80c_cfg;
extern const struct NORFLASH_CFG_T gd25q32c_cfg;
extern const struct NORFLASH_CFG_T gd25q80c_cfg;
extern const struct NORFLASH_CFG_T gd25d40c_cfg;
extern const struct NORFLASH_CFG_T gd25d20c_cfg;
// Puya
extern const struct NORFLASH_CFG_T p25q128l_cfg;
extern const struct NORFLASH_CFG_T p25q64l_cfg;
extern const struct NORFLASH_CFG_T p25q32l_cfg;
extern const struct NORFLASH_CFG_T p25q16l_cfg;
extern const struct NORFLASH_CFG_T p25q80h_cfg;
extern const struct NORFLASH_CFG_T p25q21h_cfg;
extern const struct NORFLASH_CFG_T p25q40h_cfg;
// XTS
extern const struct NORFLASH_CFG_T xt25q08b_cfg;
// EON
extern const struct NORFLASH_CFG_T en25s80b_cfg;
// XMC
extern const struct NORFLASH_CFG_T xm25qh16c_cfg;
extern const struct NORFLASH_CFG_T xm25qh80b_cfg;
static const struct NORFLASH_CFG_T * const flash_list[] = {
// ----------------------
// GigaDevice
// ----------------------
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25LQ64C__)
&gd25lq64c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25LQ32C__)
&gd25lq32c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25LQ6C__)
&gd25lq16c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25LQ80C__)
&gd25lq80c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25Q32C__)
&gd25q32c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25Q80C__)
&gd25q80c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25D40C__)
&gd25d40c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_GD25D20C__)
&gd25d20c_cfg,
#endif
// ----------------------
// Puya
// ----------------------
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q128L__)
&p25q128l_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q64L__)
&p25q64l_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q32L__)
&p25q32l_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q16L__)
&p25q16l_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q80H__)
&p25q80h_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q21H__)
&p25q21h_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_P25Q40H__)
&p25q40h_cfg,
#endif
// ----------------------
// Xinxin
// ----------------------
#if defined(__NORFLASH_XM25QH16C__)
&xm25qh16c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_XM25QH80B__)
&xm25qh80b_cfg,
#endif
// ----------------------
// XTS
// ----------------------
#if defined(__NORFLASH_XT25Q08B__)
&xt25q08b_cfg,
#endif
// ----------------------
// EON
// ----------------------
#if defined(__NORFLASH_EN25S80B__)
&en25s80b_cfg,
#endif
};
// Sample delay will be larger if:
// 1) flash speed is higher (major impact)
// 2) vcore voltage is lower (secondary major impact)
// 3) flash voltage is lower (minor impact)
// Sample delay unit:
// V1: 1/2 source_clk cycle when <= 2, 1 source_clk cycle when >= 2
// V2: 1/2 source_clk cycle when <= 4, 1 source_clk cycle when >= 4
// Flash clock low to output valid delay:
// T_clqv: 7 ns
// Flash IO latency:
// BEST1000/3001/1400: 4 ns
// BEST2000: 5 ns
// BEST2300: 2 ns
// Flash output time: T_clqv + T_io_latency
// Falling edge sample time: one spi_clk cycle (should > flash output time)
#ifdef CHIP_BEST2300
#define FALLING_EDGE_SAMPLE_ADJ_FREQ (110 * 1000 * 1000) // about 9 ns
#else
#define FALLING_EDGE_SAMPLE_ADJ_FREQ (77 * 1000 * 1000) // about 13 ns
#endif
#if (CHIP_FLASH_CTRL_VER <= 1)
#define SAM_EDGE_FALLING (1 << 4)
#define SAM_NEG_PHASE (1 << 5)
#define SAMDLY_MASK (0xF << 0)
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
#define DIV2_SAMP_DELAY_FALLING_EDGE_IDX 1
#define DIVN_SAMP_DELAY_FALLING_EDGE_IDX 2
// Sample delays: 1, 1.5, 2, 3
static const uint8_t samdly_list_divn[] = { 1, SAM_NEG_PHASE | SAM_EDGE_FALLING | 2, 2, 3, };
#else
// Sample delays: 0, 0.5, 1, 1.5, 2, 3, 4, 5, 6, 7
static const uint8_t samdly_list_divn[] = { /*0,*/ SAM_EDGE_FALLING | 1, 1, SAM_NEG_PHASE | SAM_EDGE_FALLING | 2, 2, 3, 4, /*5, 6, 7,*/ };
#endif
#else
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
#ifdef CHIP_BEST1402
#define DIV1_SAMP_DELAY_FALLING_EDGE_IDX 2
#else
#define DIV1_SAMP_DELAY_FALLING_EDGE_IDX 1
#endif
static const uint8_t samdly_list_div1[] = { 0, 1, 2, 3, };
#define DIV2_SAMP_DELAY_FALLING_EDGE_IDX 1
#define DIVN_SAMP_DELAY_FALLING_EDGE_IDX 3
static const uint8_t samdly_list_divn[] = { 2, 3, 4, 5, 6, 7, 8, 9, };
#else
static const uint8_t samdly_list_divn[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, };
#endif
#endif
static uint8_t flash_idx;
static uint8_t div_read;
static uint8_t div_std_read;
static uint8_t div_others;
static uint32_t norflash_op_mode = 0;
static bool falling_edge_adj;
static uint8_t sample_delay_index;
#ifdef FLASH_SUSPEND
static uint32_t check_irq[(USER_IRQn_QTY + 31) / 32];
static bool specific_irq_check;
#endif
#ifdef FLASH_CALIB_DEBUG
static uint32_t norflash_source_clk;
static uint32_t norflash_speed;
static uint8_t calib_matched_idx[DRV_NORFLASH_CALIB_QTY];
static uint8_t calib_matched_cnt[DRV_NORFLASH_CALIB_QTY];
static uint8_t calib_final_idx[DRV_NORFLASH_CALIB_QTY];
#endif
static void norflash_delay(uint32_t us)
{
#ifdef CHIP_BEST1000
hal_sys_timer_delay(US_TO_TICKS(us));
#else
hal_sys_timer_delay_us(us);
#endif
}
#ifdef FLASH_HPM
static int norflash_set_hpm(uint8_t on)
{
if (on) {
norflaship_cmd_addr(GD25Q32C_CMD_HIGH_PERFORMANCE, 0);
} else {
norflaship_cmd_addr(GD25Q32C_CMD_RELEASE_FROM_DP, 0);
}
norflaship_busy_wait();
return 0;
}
#endif
#ifdef FLASH_SUSPEND
static int norflash_suspend(void)
{
norflaship_clear_fifos();
norflaship_ext_tx_cmd(GD25Q32C_CMD_PROGRAM_ERASE_SUSPEND, 0);
// Suspend time: 20~30 us
norflash_delay(40);
return 0;
}
static int norflash_resume(void)
{
norflaship_clear_fifos();
norflaship_ext_tx_cmd(GD25Q32C_CMD_PROGRAM_ERASE_RESUME, 0);
if (flash_list[flash_idx]->id[0] == NORFLASH_PUYA_ID_PREFIX) {
// PUYA flash requires the mean interval of resume to suspend >= 250us
norflash_delay(250);
} else {
// At least resume the work for 100 us to avoid always staying in suspended state
norflash_delay(100);
}
return 0;
}
#endif
uint8_t norflash_read_status_s0_s7(void)
{
uint8_t val;
norflash_read_reg(GD25Q32C_CMD_READ_STATUS_S0_S7, &val, 1);
return val;
}
uint8_t norflash_read_status_s8_s15(void)
{
uint8_t val;
norflash_read_reg(GD25Q32C_CMD_READ_STATUS_S8_S15, &val, 1);
return val;
}
static int norflash_status_WEL(void)
{
uint32_t status;
status = norflash_read_status_s0_s7();
return !!(status & GD25Q32C_WEL_BIT_MASK);
}
static int norflash_status_WIP(void)
{
uint32_t status;
status = norflash_read_status_s0_s7();
return !!(status & GD25Q32C_WIP_BIT_MASK);
}
void norflash_status_WEL_0_wait(void)
{
while (norflash_status_WEL() == 0 && TASK_SCHEDULE);
}
#ifdef FLASH_SUSPEND
int norflash_suspend_check_irq(uint32_t irq_num)
{
uint32_t idx;
uint32_t offset;
idx = irq_num / 32;
offset = irq_num % 32;
if (idx >= ARRAY_SIZE(check_irq)) {
return 1;
}
check_irq[idx] |= (1 << offset);
specific_irq_check = true;
return 0;
}
static int norflash_system_active(void)
{
if (specific_irq_check) {
return hal_sleep_specific_irq_pending(check_irq, ARRAY_SIZE(check_irq));
} else {
return hal_sleep_irq_pending();
}
}
#endif
enum HAL_NORFLASH_RET_T norflash_status_WIP_1_wait(int suspend)
{
while (norflash_status_WIP() && TASK_SCHEDULE) {
#ifdef FLASH_SUSPEND
if (suspend && norflash_system_active()) {
norflash_suspend();
return HAL_NORFLASH_SUSPENDED;
}
#endif
}
return HAL_NORFLASH_OK;
}
#ifdef FLASH_BURST_WRAP
static int norflash_set_burst_wrap(uint32_t len)
{
uint8_t val;
if (len == 64) {
val = (1 << 6) | (1 << 5);
} else if (len == 32) {
val = (1 << 6);
} else if (len == 16) {
val = (1 << 5);
} else if (len == 8) {
val = 0;
} else if (len == 0) {
// Disable wrap around
val = (1 << 4);
} else {
return 1;
}
norflaship_clear_txfifo();
norflaship_write_txfifo(&val, 1);
norflaship_cmd_addr(GD25Q32C_CMD_SET_BURST_WRAP, 0);
norflash_status_WIP_1_wait(0);
return 0;
}
#endif
static int norflash_set_continuous_read(uint8_t on)
{
uint8_t cmd;
if (on) {
norflaship_continuous_read_on();
norflaship_continuous_read_mode_bit(flash_list[flash_idx]->crm_en_bits);
// Continuous Read Mode takes effect after the first read
} else {
norflaship_continuous_read_off();
norflaship_continuous_read_mode_bit(flash_list[flash_idx]->crm_dis_bits);
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
cmd = GD25Q32C_CMD_FAST_QUAD_IO_READ;
} else {
norflaship_quad_mode(0);
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_DUAL_IO) {
cmd = GD25Q32C_CMD_FAST_DUAL_IO_READ;
} else {
norflaship_dual_mode(0);
// Command 0x03 and address 0xFFFFFE will make M4=1 (mode bit 4)
// at both qual and dual continuous read modes, which will disable it.
cmd = GD25Q32C_CMD_STANDARD_READ;
}
}
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_blksize(2);
if (cmd == GD25Q32C_CMD_STANDARD_READ) {
if (div_std_read) {
norflaship_div(div_std_read);
}
}
norflaship_cmd_addr(cmd, 0xFFFFFE);
// Now Continuous Read Mode has been disabled
if (cmd == GD25Q32C_CMD_STANDARD_READ) {
if (div_others) {
norflaship_div(div_others);
}
}
norflaship_clear_rxfifo();
}
norflaship_busy_wait();
return 0;
}
static int norflash_set_quad(uint8_t on)
{
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
if (on) {
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_QE, 1);
} else {
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_QE, 0);
}
return 0;
}
static int norflash_set_quad_io_mode(uint8_t on)
{
norflash_set_quad(on);
if (on) {
norflaship_quad_mode(1);
} else {
norflaship_quad_mode(0);
}
norflaship_busy_wait();
return 0;
}
static int norflash_set_quad_output_mode(uint8_t on)
{
norflash_set_quad(on);
if (on) {
norflaship_rdcmd(GD25Q32C_CMD_FAST_QUAD_OUTPUT_READ);
} else {
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
}
norflaship_busy_wait();
return 0;
}
static uint8_t norflash_set_dual_io_mode(uint8_t on)
{
if (on) {
norflaship_dual_mode(1);
} else {
norflaship_dual_mode(0);
}
norflaship_busy_wait();
return 0;
}
static uint8_t norflash_set_dual_output_mode(uint8_t on)
{
if (on) {
norflaship_rdcmd(GD25Q32C_CMD_FAST_DUAL_OUTPUT_READ);
} else {
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
}
norflaship_busy_wait();
return 0;
}
static uint8_t norflash_set_fast_mode(uint8_t on)
{
if (on) {
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_FAST_READ);
} else {
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
}
norflaship_busy_wait();
return 0;
}
static uint8_t norflash_set_stand_mode(void)
{
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
norflaship_busy_wait();
return 0;
}
uint32_t norflash_get_supported_mode(void)
{
return flash_list[flash_idx]->mode;
}
uint32_t norflash_get_current_mode(void)
{
return norflash_op_mode;
}
union DRV_NORFLASH_SEC_REG_CFG_T norflash_get_security_register_config(void)
{
return flash_list[flash_idx]->sec_reg_cfg;
}
uint32_t norflash_get_block_protect_mask(void)
{
return flash_list[flash_idx]->block_protect_mask;
}
void norflash_reset(void)
{
norflaship_clear_fifos();
// Disable continuous read mode
norflaship_continuous_read_off();
norflaship_busy_wait();
norflaship_blksize(2);
// Command 0x03 and address 0xFFFFFE will make M4=1 (mode bit 4)
// at both qual and dual continuous read modes, which will disable it.
norflaship_cmd_addr(GD25Q32C_CMD_STANDARD_READ, 0xFFFFFE);
// Release from deep power-down
norflaship_cmd_addr(GD25Q32C_CMD_RELEASE_FROM_DP, 0);
// Wait 20us for flash to finish
norflash_delay(40);
// Software reset
norflaship_ext_tx_cmd(GD25Q32C_CMD_ENABLE_RESET, 0);
norflaship_ext_tx_cmd(GD25Q32C_CMD_RESET, 0);
// Reset recovery time: 20~30 us
norflash_delay(50);
norflaship_cmd_done();
// Reset cfg
flash_idx = 0;
div_read = 0;
div_std_read = 0;
div_others = 0;
norflash_op_mode = 0;
falling_edge_adj = false;
}
int norflash_get_size(uint32_t *total_size, uint32_t *block_size, uint32_t *sector_size, uint32_t *page_size)
{
if (total_size) {
*total_size = flash_list[flash_idx]->total_size;
}
if (block_size) {
*block_size = flash_list[flash_idx]->block_size;
}
if (sector_size) {
*sector_size = flash_list[flash_idx]->sector_size;
}
if (page_size) {
*page_size = flash_list[flash_idx]->page_size;
}
return 0;
}
static void norflash_set_cfg_div(void)
{
if (div_others) {
norflaship_div(div_others);
}
}
static void norflash_set_read_div(void)
{
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_STAND_SPI) {
if (div_std_read) {
norflaship_div(div_std_read);
}
} else {
if (div_read) {
norflaship_div(div_read);
}
}
}
int norflash_set_mode(uint32_t op)
{
uint32_t read_mode = 0;
uint32_t ext_mode = 0;
uint32_t program_mode = 0;
uint32_t self_mode;
uint32_t mode;
self_mode = norflash_get_supported_mode();
mode = (self_mode & op);
if (mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
read_mode = HAL_NORFLASH_OP_MODE_QUAD_IO;
} else if (mode & HAL_NORFLASH_OP_MODE_QUAD_OUTPUT) {
read_mode = HAL_NORFLASH_OP_MODE_QUAD_OUTPUT;
} else if (mode & HAL_NORFLASH_OP_MODE_DUAL_IO) {
read_mode = HAL_NORFLASH_OP_MODE_DUAL_IO;
} else if (mode & HAL_NORFLASH_OP_MODE_DUAL_OUTPUT) {
read_mode = HAL_NORFLASH_OP_MODE_DUAL_OUTPUT;
} else if (mode & HAL_NORFLASH_OP_MODE_FAST_SPI) {
read_mode = HAL_NORFLASH_OP_MODE_FAST_SPI;
} else if(mode & HAL_NORFLASH_OP_MODE_STAND_SPI) {
read_mode = HAL_NORFLASH_OP_MODE_STAND_SPI;
} else {
// Op error
return 1;
}
if (mode & HAL_NORFLASH_OP_MODE_QUAD_PAGE_PROGRAM) {
program_mode = HAL_NORFLASH_OP_MODE_QUAD_PAGE_PROGRAM;
} else if (mode & HAL_NORFLASH_OP_MODE_DUAL_PAGE_PROGRAM) {
program_mode = HAL_NORFLASH_OP_MODE_DUAL_PAGE_PROGRAM;
} else if (mode & HAL_NORFLASH_OP_MODE_PAGE_PROGRAM) {
program_mode = HAL_NORFLASH_OP_MODE_PAGE_PROGRAM;
} else {
// Op error
return 1;
}
#ifdef FLASH_BURST_WRAP
if (mode & HAL_NORFLASH_OP_MODE_READ_WRAP) {
ext_mode |= HAL_NORFLASH_OP_MODE_READ_WRAP;
}
#endif
#ifdef FLASH_HPM
if (mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
ext_mode |= HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE;
}
#endif
if (mode & (HAL_NORFLASH_OP_MODE_QUAD_IO | HAL_NORFLASH_OP_MODE_DUAL_IO)) {
if (mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
ext_mode |= HAL_NORFLASH_OP_MODE_CONTINUOUS_READ;
}
}
mode = (read_mode | ext_mode | program_mode);
if (norflash_op_mode != mode) {
norflash_op_mode = mode;
// Continuous read off if flash supported
if ((self_mode & (HAL_NORFLASH_OP_MODE_QUAD_IO | HAL_NORFLASH_OP_MODE_DUAL_IO)) &&
(self_mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ)) {
norflash_set_continuous_read(0);
}
norflaship_quad_mode(0);
norflaship_dual_mode(0);
#ifdef FLASH_HPM
if (mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
// High performance mode on
norflash_set_hpm(1);
}
#endif
if (mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
// Quad io mode
norflash_set_quad_io_mode(1);
} else if (mode & HAL_NORFLASH_OP_MODE_QUAD_OUTPUT) {
// Quad output mode
norflash_set_quad_output_mode(1);
} else if (mode & HAL_NORFLASH_OP_MODE_DUAL_IO) {
// Dual io mode
norflash_set_dual_io_mode(1);
} else if (mode & HAL_NORFLASH_OP_MODE_DUAL_OUTPUT) {
// Dual output mode
norflash_set_dual_output_mode(1);
} else if (mode & HAL_NORFLASH_OP_MODE_FAST_SPI) {
// Fast mode
norflash_set_fast_mode(1);
} else if (mode & HAL_NORFLASH_OP_MODE_STAND_SPI) {
// Standard spi mode
norflash_set_stand_mode();
}
#ifdef FLASH_BURST_WRAP
if (mode & HAL_NORFLASH_OP_MODE_READ_WRAP) {
norflash_set_burst_wrap(32);
hal_cache_wrap_enable(HAL_CACHE_ID_I_CACHE);
hal_cache_wrap_enable(HAL_CACHE_ID_D_CACHE);
}
#endif
#ifdef FLASH_HPM
if ((self_mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) &&
(mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) == 0) {
// High performance mode off
norflash_set_hpm(0);
}
#endif
if (mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
// Continuous read on
norflash_set_continuous_read(1);
}
norflaship_cmd_done();
}
norflash_set_read_div();
return 0;
}
int norflash_pre_operation(void)
{
norflash_set_cfg_div();
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
norflash_set_continuous_read(0);
}
return 0;
}
int norflash_post_operation(void)
{
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
norflash_set_continuous_read(1);
}
norflaship_cmd_done();
norflash_set_read_div();
return 0;
}
int norflash_read_reg(uint8_t cmd, uint8_t *val, uint32_t len)
{
int i;
norflaship_clear_fifos();
#ifdef TRY_EMBEDDED_CMD
if ((cmd == GD25Q32C_CMD_READ_STATUS_S0_S7 || cmd == GD25Q32C_CMD_READ_STATUS_S8_S15) && len == 1) {
norflaship_cmd_addr(cmd, 0);
} else {
if (cmd == GD25Q32C_CMD_ID) {
norflaship_blksize(len);
norflaship_cmd_addr(cmd, 0);
} else {
norflaship_ext_rx_cmd(cmd, 0, len);
}
}
#else
norflaship_ext_rx_cmd(cmd, 0, len);
#endif
norflaship_rxfifo_count_wait(len);
for (i = 0; i < len; i++) {
val[i] = norflaship_read_rxfifo();
}
return 0;
}
int norflash_read_reg_ex(uint8_t cmd, uint8_t *param, uint32_t param_len, uint8_t *val, uint32_t len)
{
int i;
norflaship_clear_fifos();
if (param && param_len > 0) {
norflaship_write_txfifo(param, param_len);
} else {
param_len = 0;
}
norflaship_ext_rx_cmd(cmd, param_len, len);
for (i = 0; i < len; i++) {
norflaship_rxfifo_empty_wait();
val[i] = norflaship_read_rxfifo();
}
return 0;
}
int norflash_write_reg(uint8_t cmd, const uint8_t *val, uint32_t len)
{
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, 0);
norflash_status_WIP_1_wait(0);
norflash_status_WEL_0_wait();
norflaship_clear_txfifo();
norflaship_write_txfifo(val, len);
#ifdef TRY_EMBEDDED_CMD
if (cmd == GD25Q32C_CMD_WRITE_STATUS_S0_S7) {
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_STATUS_S0_S7, 0);
} else {
norflaship_ext_tx_cmd(cmd, len);
}
#else
norflaship_ext_tx_cmd(cmd, len);
#endif
norflash_status_WIP_1_wait(0);
return 0;
}
int norflash_get_id(uint8_t *value, uint32_t len)
{
norflash_pre_operation();
if (len > NORFLASH_ID_LEN) {
len = NORFLASH_ID_LEN;
}
norflash_read_reg(GD25Q32C_CMD_ID, value, len);
norflash_post_operation();
return 0;
}
int norflash_get_unique_id(uint8_t *value, uint32_t len)
{
uint32_t param;
uint8_t cmd;
norflash_pre_operation();
if (flash_list[flash_idx]->id[0] == NORFLASH_XTS_ID_PREFIX) {
if (len > XTS_UNIQUE_ID_LEN) {
len = XTS_UNIQUE_ID_LEN;
}
param = XTS_UNIQUE_ID_PARAM;
cmd = XTS_UNIQUE_ID_CMD;
} else {
if (len > NORFLASH_UNIQUE_ID_LEN) {
len = NORFLASH_UNIQUE_ID_LEN;
}
param = 0;
cmd = GD25Q32C_CMD_UNIQUE_ID;
}
norflash_read_reg_ex(cmd, (uint8_t *)&param, sizeof(param), value, len);
norflash_post_operation();
return 0;
}
static void norflash_get_samdly_list(uint32_t div, const uint8_t **samdly_list_p, uint32_t *size_p)
{
const uint8_t *samdly_list = NULL;
uint32_t size = 0;
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div >= 2) {
samdly_list = samdly_list_divn;
size = ARRAY_SIZE(samdly_list_divn);
}
#else
if (div >= 1) {
if (div == 1) {
samdly_list = samdly_list_div1;
size = ARRAY_SIZE(samdly_list_div1);
} else {
samdly_list = samdly_list_divn;
size = ARRAY_SIZE(samdly_list_divn);
}
}
#endif
if (samdly_list_p) {
*samdly_list_p = samdly_list;
}
if (size_p) {
*size_p = size;
}
}
void norflash_set_sample_delay_index(uint32_t index)
{
const uint8_t *samdly_list;
uint32_t size;
uint32_t div;
sample_delay_index = index;
div = norflaship_get_div();
norflash_get_samdly_list(div, &samdly_list, &size);
if (index < size) {
#if (CHIP_FLASH_CTRL_VER <= 1)
norflaship_pos_neg(samdly_list[index] & SAM_EDGE_FALLING);
norflaship_neg_phase(samdly_list[index] & SAM_NEG_PHASE);
norflaship_samdly(samdly_list[index] & SAMDLY_MASK);
#else
norflaship_samdly(samdly_list[index]);
#endif
}
}
uint32_t norflash_get_sample_delay_index(void)
{
return sample_delay_index;
}
static bool norflash_calib_flash_id_valid(void)
{
uint8_t id[HAL_NORFLASH_DEVICE_ID_LEN];
const uint8_t *cmp_id;
norflash_get_id(id, sizeof(id));
cmp_id = flash_list[flash_idx]->id;
if (id[0] == cmp_id[0] && id[1] == cmp_id[1] && id[2] == cmp_id[2]) {
return true;
}
return false;
}
static bool norflash_calib_magic_word_valid(void)
{
uint32_t magic;
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
#endif
norflaship_clear_rxfifo();
#if (FLASH_NC_BASE == FLASH_BASE)
hal_cache_invalidate(HAL_CACHE_ID_D_CACHE, FLASH_BASE, sizeof(magic));
magic = *(volatile uint32_t *)FLASH_BASE;
#else
magic = *(volatile uint32_t *)FLASH_NC_BASE;
#endif
if (magic == BOOT_MAGIC_NUMBER) {
return true;
}
return false;
}
int norflash_sample_delay_calib(enum DRV_NORFLASH_CALIB_T type)
{
int i;
uint32_t matched_cnt = 0;
uint32_t matched_idx = 0;
uint32_t div;
uint32_t size;
bool valid;
if (type >= DRV_NORFLASH_CALIB_QTY) {
return 1;
}
#if defined(ROM_BUILD) || defined(PROGRAMMER)
if (type != DRV_NORFLASH_CALIB_FLASH_ID) {
return 0;
}
#endif
div = norflaship_get_div();
if (div == 0) {
return -1;
#if (CHIP_FLASH_CTRL_VER <= 1)
} else if (div == 1) {
return -2;
#endif
}
norflash_get_samdly_list(div, NULL, &size);
for (i = 0; i < size; i++) {
norflaship_busy_wait();
norflash_set_sample_delay_index(i);
if (type == DRV_NORFLASH_CALIB_FLASH_ID) {
valid = norflash_calib_flash_id_valid();
} else {
valid = norflash_calib_magic_word_valid();
}
if (valid) {
if (matched_cnt == 0) {
matched_idx = i;
}
matched_cnt++;
} else if (matched_cnt) {
break;
}
}
#ifdef FLASH_CALIB_DEBUG
calib_matched_idx[type] = matched_idx;
calib_matched_cnt[type] = matched_cnt;
#endif
if (matched_cnt) {
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
if (matched_cnt == 2) {
uint32_t falling_edge_idx;
if (0) {
#if (CHIP_FLASH_CTRL_VER >= 2)
} else if (div == 1) {
falling_edge_idx = DIV1_SAMP_DELAY_FALLING_EDGE_IDX;
#endif
} else if (div == 2) {
falling_edge_idx = DIV2_SAMP_DELAY_FALLING_EDGE_IDX;
if (falling_edge_adj) {
falling_edge_idx++;
}
} else {
falling_edge_idx = DIVN_SAMP_DELAY_FALLING_EDGE_IDX;
}
if (matched_idx <= falling_edge_idx &&
falling_edge_idx < matched_idx + matched_cnt) {
matched_idx = falling_edge_idx;
matched_cnt = 1;
}
}
#endif
matched_idx += matched_cnt / 2;
norflash_set_sample_delay_index(matched_idx);
#ifdef FLASH_CALIB_DEBUG
calib_final_idx[type] = matched_idx;
#endif
return 0;
}
#ifdef FLASH_CALIB_DEBUG
calib_final_idx[type] = -1;
#endif
return 1;
}
void norflash_show_calib_result(void)
{
#ifdef FLASH_CALIB_DEBUG
union DRV_NORFLASH_SPEED_RATIO_T ratio;
uint32_t div;
uint32_t size;
const uint8_t *list;
int i;
TRACE(0, "FLASH_CALIB_RESULT:");
TRACE(0, "<FREQ>\nsource_clk=%u speed=%u flash_max=%u", norflash_source_clk, norflash_speed, flash_list[flash_idx]->max_speed);
ratio = flash_list[flash_idx]->speed_ratio;
TRACE(0, "<RATIO>\nstd_read=%u/8 others=%u/8", (ratio.s.std_read + 1), (ratio.s.others + 1));
div = norflaship_get_div();
TRACE(0, "<DIV>\ndiv=%u", div);
norflash_get_samdly_list(div, &list, &size);
TRACE(0, "<SAMDLY LIST>");
if (list == NULL || size == 0) {
TRACE(0, "NONE");
} else {
DUMP8("%02X ", list, size);
}
TRACE(0, "<CALIB RESULT>");
for (i = 0; i < DRV_NORFLASH_CALIB_QTY; i++) {
TRACE(0, "type=%d idx=%02u cnt=%02u final=%02u",
i, calib_matched_idx[i], calib_matched_cnt[i], calib_final_idx[i]);
}
TRACE(0, "\t");
#endif
}
int norflash_init_sample_delay_by_div(uint32_t div)
{
if (div == 0) {
return -1;
} if (div == 1) {
#if (CHIP_FLASH_CTRL_VER <= 1)
return -2;
#else
norflaship_samdly(1);
#endif
} else if (div == 2 && !falling_edge_adj) {
// Set sample delay to clock falling edge
#if (CHIP_FLASH_CTRL_VER <= 1)
norflaship_pos_neg(1);
norflaship_neg_phase(1);
norflaship_samdly(2);
#else
norflaship_samdly(3);
#endif
} else {
// Set sample delay to nearest to but not later than clock falling edge
#if (CHIP_FLASH_CTRL_VER <= 1)
norflaship_pos_neg(0);
norflaship_neg_phase(0);
norflaship_samdly(2);
#else
norflaship_samdly(4);
#endif
}
return 0;
}
int norflash_init_div(const struct HAL_NORFLASH_CONFIG_T *cfg)
{
uint32_t max_speed;
uint32_t std_read_speed;
uint32_t others_speed;
union DRV_NORFLASH_SPEED_RATIO_T ratio;
uint32_t div;
#ifdef FLASH_CALIB_DEBUG
norflash_source_clk = cfg->source_clk;
norflash_speed = cfg->speed;
#endif
max_speed = flash_list[flash_idx]->max_speed;
ratio = flash_list[flash_idx]->speed_ratio;
if (max_speed == 0) {
max_speed = NORFLASH_DEFAULT_MAX_SPEED;
}
if (max_speed > cfg->speed) {
max_speed = cfg->speed;
}
if (max_speed > cfg->source_clk) {
max_speed = cfg->source_clk;
}
std_read_speed = max_speed * (1 + ratio.s.std_read) / NORFLASH_SPEED_RATIO_DENOMINATOR;
others_speed = max_speed * (1 + ratio.s.others) / NORFLASH_SPEED_RATIO_DENOMINATOR;
div = (cfg->source_clk + max_speed - 1) / max_speed;
div_read = (div < NORFLASH_MAX_DIV) ? div : NORFLASH_MAX_DIV;
div = (cfg->source_clk + std_read_speed - 1) / std_read_speed;
div_std_read = (div < NORFLASH_MAX_DIV) ? div : NORFLASH_MAX_DIV;
div = (cfg->source_clk + others_speed - 1) / others_speed;
div_others = (div < NORFLASH_MAX_DIV) ? div : NORFLASH_MAX_DIV;
if (div_read == 2 && max_speed >= FALLING_EDGE_SAMPLE_ADJ_FREQ) {
falling_edge_adj = true;
} else {
falling_edge_adj = false;
}
if (div_read && div_std_read && div_others) {
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div_read == 1) {
return -1;
}
#else
if (div_read == 1 && max_speed > NORFLASH_DIV1_MAX_SPEED) {
return -1;
}
#endif
// Init sample delay according to div_read
norflash_init_sample_delay_by_div(div_read);
// Still in command mode
norflaship_div(div_others);
return 0;
}
return 1;
}
int norflash_match_chip(const uint8_t *id, uint32_t len)
{
const uint8_t *cmp_id;
if (len == NORFLASH_ID_LEN) {
for (flash_idx = 0; flash_idx < ARRAY_SIZE(flash_list); flash_idx++) {
cmp_id = flash_list[flash_idx]->id;
if (id[0] == cmp_id[0] && id[1] == cmp_id[1] && id[2] == cmp_id[2]) {
return true;
}
}
}
return false;
}
#ifdef PUYA_FLASH_ERASE_PAGE_ENABLE
static void norflaship_ext_cmd_addr(uint8_t cmd, uint32_t addr)
{
uint8_t buff[3];
buff[2] = (uint8_t)(addr & 0xff);
buff[1] = (uint8_t)((addr >> 8) & 0xff);
buff[0] = (uint8_t)((addr >> 16) & 0xff);
norflaship_clear_txfifo();
norflaship_write_txfifo(buff, 3);
norflaship_ext_tx_cmd(cmd, 3);
}
#endif
enum HAL_NORFLASH_RET_T norflash_erase(uint32_t start_address, enum DRV_NORFLASH_ERASE_T type, int suspend)
{
enum HAL_NORFLASH_RET_T ret;
if (flash_list[flash_idx]->mode & HAL_NORFLASH_OP_MODE_ERASE_IN_STD) {
if(norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
norflash_set_quad_io_mode(0);
} else if(norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_OUTPUT) {
norflash_set_quad_output_mode(0);
}
}
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
// Need 1us. Or norflash_status_WEL_0_wait(), which needs 6us.
switch(type) {
#ifdef PUYA_FLASH_ERASE_PAGE_ENABLE
case DRV_NORFLASH_ERASE_PAGE:
norflaship_ext_cmd_addr(PUYA_FLASH_CMD_PAGE_ERASE, start_address);
break;
#endif
case DRV_NORFLASH_ERASE_SECTOR:
norflaship_cmd_addr(GD25Q32C_CMD_SECTOR_ERASE, start_address);
break;
case DRV_NORFLASH_ERASE_BLOCK:
norflaship_cmd_addr(GD25Q32C_CMD_BLOCK_ERASE, start_address);
break;
case DRV_NORFLASH_ERASE_CHIP:
norflaship_cmd_addr(GD25Q32C_CMD_CHIP_ERASE, start_address);
break;
}
norflaship_busy_wait();
#ifdef FLASH_SUSPEND
// PUYA flash requires the first delay of erase >= 400us
if (flash_list[flash_idx]->id[0] == NORFLASH_PUYA_ID_PREFIX) {
norflash_delay(400);
}
#endif
ret = norflash_status_WIP_1_wait(suspend);
if (flash_list[flash_idx]->mode & HAL_NORFLASH_OP_MODE_ERASE_IN_STD) {
if(norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
norflash_set_quad_io_mode(1);
} else if(norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_OUTPUT) {
norflash_set_quad_output_mode(1);
}
}
norflaship_cmd_done();
return ret;
}
enum HAL_NORFLASH_RET_T norflash_write(uint32_t start_address, const uint8_t *buffer, uint32_t len, int suspend)
{
enum HAL_NORFLASH_RET_T ret;
uint32_t POSSIBLY_UNUSED remains;
if (len > GD25Q32C_PAGE_SIZE) {
return HAL_NORFLASH_ERR;
}
norflaship_clear_txfifo();
#if (CHIP_FLASH_CTRL_VER >= 2)
norflaship_blksize(len);
#endif
remains = norflaship_write_txfifo(buffer, len);
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_PAGE_PROGRAM) {
norflaship_cmd_addr(GD25Q32C_CMD_QUAD_PAGE_PROGRAM, start_address);
} else if (norflash_op_mode & HAL_NORFLASH_OP_MODE_DUAL_PAGE_PROGRAM) {
norflaship_cmd_addr(GD25Q32C_CMD_DUAL_PAGE_PROGRAM, start_address);
} else {
norflaship_cmd_addr(GD25Q32C_CMD_PAGE_PROGRAM, start_address);
}
#if (CHIP_FLASH_CTRL_VER >= 2)
while (remains > 0) {
buffer += len - remains;
len = remains;
remains = norflaship_write_txfifo(buffer, len);
}
#endif
norflaship_busy_wait();
#ifdef FLASH_SUSPEND
// PUYA flash requires the first delay of byte program >= 450us
if (flash_list[flash_idx]->id[0] == NORFLASH_PUYA_ID_PREFIX && len < GD25Q32C_PAGE_SIZE) {
norflash_delay(450);
}
#endif
ret = norflash_status_WIP_1_wait(suspend);
norflaship_cmd_done();
return ret;
}
#ifdef FLASH_SUSPEND
enum HAL_NORFLASH_RET_T norflash_erase_resume(int suspend)
{
// TODO: Need to check SUS1 bit in status reg?
enum HAL_NORFLASH_RET_T ret;
norflash_pre_operation();
norflash_resume();
ret = norflash_status_WIP_1_wait(suspend);
norflash_post_operation();
return ret;
}
enum HAL_NORFLASH_RET_T norflash_write_resume(int suspend)
{
// TODO: Need to check SUS2 bit in status reg?
return norflash_erase_resume(suspend);
}
#endif
int norflash_read(uint32_t start_address, uint8_t *buffer, uint32_t len)
{
uint32_t index = 0;
uint8_t val;
if (len > NORFLASHIP_RXFIFO_SIZE) {
return 1;
}
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_blksize(len);
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
/* Quad , only fast */
norflaship_cmd_addr(GD25Q32C_CMD_FAST_QUAD_IO_READ, start_address);
} else if (norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_OUTPUT) {
/* Dual, only fast */
norflaship_cmd_addr(GD25Q32C_CMD_FAST_QUAD_OUTPUT_READ, start_address);
} else if (norflash_op_mode & HAL_NORFLASH_OP_MODE_DUAL_IO) {
/* Dual, only fast */
norflaship_cmd_addr(GD25Q32C_CMD_FAST_DUAL_IO_READ, start_address);
} else if (norflash_op_mode & HAL_NORFLASH_OP_MODE_DUAL_OUTPUT) {
/* Dual, only fast */
norflaship_cmd_addr(GD25Q32C_CMD_FAST_DUAL_OUTPUT_READ, start_address);
} else if (norflash_op_mode & HAL_NORFLASH_OP_MODE_FAST_SPI){
/* fast */
norflaship_cmd_addr(GD25Q32C_CMD_STANDARD_FAST_READ, start_address);
} else {
/* normal */
norflaship_cmd_addr(GD25Q32C_CMD_STANDARD_READ, start_address);
}
while (1) {
norflaship_rxfifo_empty_wait();
val = norflaship_read_rxfifo();
if (buffer) {
buffer[index] = val;
}
++index;
if (index >= len) {
break;
}
}
norflaship_cmd_done();
return 0;
}
void norflash_sleep(void)
{
norflash_pre_operation();
#ifdef FLASH_HPM
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
norflash_set_hpm(0);
}
#endif
norflaship_cmd_addr(GD25Q32C_CMD_DEEP_POWER_DOWN, 0);
}
void norflash_wakeup(void)
{
norflaship_cmd_addr(GD25Q32C_CMD_RELEASE_FROM_DP, 0);
// Wait 20us for flash to finish
norflash_delay(40);
#ifdef FLASH_HPM
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
norflash_set_hpm(1);
}
#endif
norflash_post_operation();
}
int norflash_init_status(uint32_t status)
{
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_INIT, status);
return 0;
}
int norflash_set_block_protection(uint32_t bp)
{
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_BP, bp);
return 0;
}
#ifdef FLASH_SECURITY_REGISTER
int norflash_security_register_lock(uint32_t id)
{
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_LB, id);
return 0;
}
enum HAL_NORFLASH_RET_T norflash_security_register_erase(uint32_t start_address)
{
enum HAL_NORFLASH_RET_T ret;
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
// Need 1us. Or norflash_status_WEL_0_wait(), which needs 6us.
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_ERASE, start_address);
ret = norflash_status_WIP_1_wait(0);
norflaship_cmd_done();
return ret;
}
enum HAL_NORFLASH_RET_T norflash_security_register_write(uint32_t start_address, const uint8_t *buffer, uint32_t len)
{
enum HAL_NORFLASH_RET_T ret;
uint32_t remains;
// Security register page size might be larger than normal page size
// E.g., the size of P25Q32L and P25Q64L is 1024
norflaship_clear_txfifo();
#if (CHIP_FLASH_CTRL_VER <= 1)
uint32_t div = 0;
if (len > NORFLASHIP_TXFIFO_SIZE) {
div = norflaship_get_div();
// Slow down to avoid tx fifo underflow (it takes about 10 cpu cycles to fill one byte)
norflaship_div(16);
}
remains = norflaship_v1_write_txfifo_safe(buffer, len);
#else
norflaship_blksize(len);
remains = norflaship_write_txfifo(buffer, len);
#endif
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_PROGRAM, start_address);
#if (CHIP_FLASH_CTRL_VER <= 1)
if (remains) {
norflaship_v1_write_txfifo_all(buffer, len);
}
#else
while (remains > 0) {
buffer += len - remains;
len = remains;
remains = norflaship_write_txfifo(buffer, len);
}
#endif
norflaship_busy_wait();
ret = norflash_status_WIP_1_wait(0);
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div) {
// Restore the old div
norflaship_div(div);
}
#endif
norflaship_cmd_done();
return ret;
}
int norflash_security_register_read(uint32_t start_address, uint8_t *buffer, uint32_t len)
{
uint32_t index = 0;
if (len > NORFLASHIP_RXFIFO_SIZE) {
return 1;
}
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_blksize(len);
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_READ, start_address);
while (1) {
norflaship_rxfifo_empty_wait();
buffer[index] = norflaship_read_rxfifo();
++index;
if (index >= len) {
break;
}
}
norflaship_cmd_done();
return 0;
}
uint32_t norflash_security_register_enable_read(void)
{
uint32_t mode;
mode = norflash_op_mode;
norflash_set_mode(0);
norflaship_busy_wait();
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
#endif
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_rdcmd(GD25Q32C_CMD_SECURITY_REGISTER_READ);
norflaship_busy_wait();
return mode;
}
void norflash_security_register_disable_read(uint32_t mode)
{
norflaship_busy_wait();
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
#endif
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
norflaship_busy_wait();
norflash_set_mode(mode);
}
#endif