pinebuds/platform/drivers/norflash/norflash_drv.c

1599 lines
39 KiB
C
Raw Permalink Normal View History

2022-08-15 04:20:27 -05:00
/***************************************************************************
*
* 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 "norflash_drv.h"
#include "cmsis.h"
#include "hal_cache.h"
#include "hal_norflash.h"
#include "hal_norflaship.h"
2022-08-15 04:20:27 -05:00
#include "hal_sleep.h"
#include "hal_timer.h"
#include "hal_trace.h"
2022-08-15 04:20:27 -05:00
#include "norflash_gd25q32c.h"
#include "plat_addr_map.h"
#include "plat_types.h"
2022-08-15 04:20:27 -05:00
#include "tool_msg.h"
#ifdef PROGRAMMER
#include "task_schedule.h"
#else
#define TASK_SCHEDULE true
2022-08-15 04:20:27 -05:00
#endif
#if (CHIP_FLASH_CTRL_VER >= 2) && !defined(ROM_BUILD) && \
!defined(PROGRAMMER) && \
!(defined(CHIP_BEST2001) && \
(defined(PSRAMUHS_ENABLE) || defined(PSRAM_ENABLE)))
2022-08-15 04:20:27 -05:00
#define FLASH_BURST_WRAP
#endif
#define SAMP_DELAY_PRIO_FALLING_EDGE
#define NORFLASH_UNIQUE_ID_LEN 18
2022-08-15 04:20:27 -05:00
#define NORFLASH_MAX_DIV 0xFF
2022-08-15 04:20:27 -05:00
#define NORFLASH_DEFAULT_MAX_SPEED (104 * 1000 * 1000)
2022-08-15 04:20:27 -05:00
#define NORFLASH_DIV1_MAX_SPEED (99 * 1000 * 1000)
2022-08-15 04:20:27 -05:00
#define NORFLASH_SPEED_RATIO_DENOMINATOR 8
2022-08-15 04:20:27 -05:00
#define NORFLASH_PUYA_ID_PREFIX 0x85
2022-08-15 04:20:27 -05:00
#define NORFLASH_XTS_ID_PREFIX 0x0B
2022-08-15 04:20:27 -05:00
#define XTS_UNIQUE_ID_LEN 16
#define XTS_UNIQUE_ID_CMD 0x5A
#define XTS_UNIQUE_ID_PARAM 0x00019400
2022-08-15 04:20:27 -05:00
// 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
// ----------------------
2022-08-15 04:20:27 -05:00
#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
// ----------------------
2022-08-15 04:20:27 -05:00
#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
// ----------------------
2022-08-15 04:20:27 -05:00
#if defined(__NORFLASH_XM25QH16C__)
&xm25qh16c_cfg,
#endif
#if defined(__NORFLASH_ALL__) || defined(__NORFLASH_XM25QH80B__)
&xm25qh80b_cfg,
#endif
// ----------------------
// XTS
// ----------------------
2022-08-15 04:20:27 -05:00
#if defined(__NORFLASH_XT25Q08B__)
&xt25q08b_cfg,
#endif
// ----------------------
// EON
// ----------------------
2022-08-15 04:20:27 -05:00
#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
2022-08-15 04:20:27 -05:00
#else
#define FALLING_EDGE_SAMPLE_ADJ_FREQ (77 * 1000 * 1000) // about 13 ns
2022-08-15 04:20:27 -05:00
#endif
#if (CHIP_FLASH_CTRL_VER <= 1)
#define SAM_EDGE_FALLING (1 << 4)
#define SAM_NEG_PHASE (1 << 5)
#define SAMDLY_MASK (0xF << 0)
2022-08-15 04:20:27 -05:00
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
#define DIV2_SAMP_DELAY_FALLING_EDGE_IDX 1
#define DIVN_SAMP_DELAY_FALLING_EDGE_IDX 2
2022-08-15 04:20:27 -05:00
// Sample delays: 1, 1.5, 2, 3
static const uint8_t samdly_list_divn[] = {
1,
SAM_NEG_PHASE | SAM_EDGE_FALLING | 2,
2,
3,
};
2022-08-15 04:20:27 -05:00
#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,*/};
2022-08-15 04:20:27 -05:00
#endif
#else
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
#ifdef CHIP_BEST1402
#define DIV1_SAMP_DELAY_FALLING_EDGE_IDX 2
2022-08-15 04:20:27 -05:00
#else
#define DIV1_SAMP_DELAY_FALLING_EDGE_IDX 1
2022-08-15 04:20:27 -05:00
#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,
};
2022-08-15 04:20:27 -05:00
#else
static const uint8_t samdly_list_divn[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
};
2022-08-15 04:20:27 -05:00
#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) {
2022-08-15 04:20:27 -05:00
#ifdef CHIP_BEST1000
hal_sys_timer_delay(US_TO_TICKS(us));
2022-08-15 04:20:27 -05:00
#else
hal_sys_timer_delay_us(us);
2022-08-15 04:20:27 -05:00
#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();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
#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;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
#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;
2022-08-15 04:20:27 -05:00
}
uint8_t norflash_read_status_s8_s15(void) {
uint8_t val;
norflash_read_reg(GD25Q32C_CMD_READ_STATUS_S8_S15, &val, 1);
return val;
2022-08-15 04:20:27 -05:00
}
static int norflash_status_WEL(void) {
uint32_t status;
status = norflash_read_status_s0_s7();
return !!(status & GD25Q32C_WEL_BIT_MASK);
2022-08-15 04:20:27 -05:00
}
static int norflash_status_WIP(void) {
uint32_t status;
status = norflash_read_status_s0_s7();
return !!(status & GD25Q32C_WIP_BIT_MASK);
2022-08-15 04:20:27 -05:00
}
void norflash_status_WEL_0_wait(void) {
while (norflash_status_WEL() == 0 && TASK_SCHEDULE)
;
2022-08-15 04:20:27 -05:00
}
#ifdef FLASH_SUSPEND
int norflash_suspend_check_irq(uint32_t irq_num) {
uint32_t idx;
uint32_t offset;
2022-08-15 04:20:27 -05:00
idx = irq_num / 32;
offset = irq_num % 32;
2022-08-15 04:20:27 -05:00
if (idx >= ARRAY_SIZE(check_irq)) {
return 1;
}
2022-08-15 04:20:27 -05:00
check_irq[idx] |= (1 << offset);
specific_irq_check = true;
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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();
}
2022-08-15 04:20:27 -05:00
}
#endif
enum HAL_NORFLASH_RET_T norflash_status_WIP_1_wait(int suspend) {
while (norflash_status_WIP() && TASK_SCHEDULE) {
2022-08-15 04:20:27 -05:00
#ifdef FLASH_SUSPEND
if (suspend && norflash_system_active()) {
norflash_suspend();
return HAL_NORFLASH_SUSPENDED;
2022-08-15 04:20:27 -05:00
}
#endif
}
2022-08-15 04:20:27 -05:00
return HAL_NORFLASH_OK;
2022-08-15 04:20:27 -05:00
}
#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;
}
2022-08-15 04:20:27 -05:00
norflaship_clear_txfifo();
norflaship_write_txfifo(&val, 1);
norflaship_cmd_addr(GD25Q32C_CMD_SET_BURST_WRAP, 0);
norflash_status_WIP_1_wait(0);
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
#endif
static int norflash_set_continuous_read(uint8_t on) {
uint8_t cmd;
2022-08-15 04:20:27 -05:00
if (on) {
norflaship_continuous_read_on();
2022-08-15 04:20:27 -05:00
norflaship_continuous_read_mode_bit(flash_list[flash_idx]->crm_en_bits);
2022-08-15 04:20:27 -05:00
// Continuous Read Mode takes effect after the first read
} else {
norflaship_continuous_read_off();
2022-08-15 04:20:27 -05:00
norflaship_continuous_read_mode_bit(flash_list[flash_idx]->crm_dis_bits);
2022-08-15 04:20:27 -05:00
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_QUAD_IO) {
cmd = GD25Q32C_CMD_FAST_QUAD_IO_READ;
} else {
norflaship_quad_mode(0);
2022-08-15 04:20:27 -05:00
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_DUAL_IO) {
cmd = GD25Q32C_CMD_FAST_DUAL_IO_READ;
} else {
norflaship_dual_mode(0);
2022-08-15 04:20:27 -05:00
// 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;
}
2022-08-15 04:20:27 -05:00
}
norflaship_clear_rxfifo();
2022-08-15 04:20:27 -05:00
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);
}
2022-08-15 04:20:27 -05:00
}
norflaship_clear_rxfifo();
}
2022-08-15 04:20:27 -05:00
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
static uint8_t norflash_set_stand_mode(void) {
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
norflaship_busy_wait();
return 0;
2022-08-15 04:20:27 -05:00
}
uint32_t norflash_get_supported_mode(void) {
return flash_list[flash_idx]->mode;
2022-08-15 04:20:27 -05:00
}
uint32_t norflash_get_current_mode(void) { return norflash_op_mode; }
2022-08-15 04:20:27 -05:00
union DRV_NORFLASH_SEC_REG_CFG_T norflash_get_security_register_config(void) {
return flash_list[flash_idx]->sec_reg_cfg;
2022-08-15 04:20:27 -05:00
}
uint32_t norflash_get_block_protect_mask(void) {
return flash_list[flash_idx]->block_protect_mask;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
static void norflash_set_cfg_div(void) {
if (div_others) {
norflaship_div(div_others);
}
}
2022-08-15 04:20:27 -05:00
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);
2022-08-15 04:20:27 -05:00
}
} else {
if (div_read) {
norflaship_div(div_read);
2022-08-15 04:20:27 -05:00
}
}
}
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;
}
2022-08-15 04:20:27 -05:00
#ifdef FLASH_BURST_WRAP
if (mode & HAL_NORFLASH_OP_MODE_READ_WRAP) {
ext_mode |= HAL_NORFLASH_OP_MODE_READ_WRAP;
}
2022-08-15 04:20:27 -05:00
#endif
#ifdef FLASH_HPM
if (mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
ext_mode |= HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE;
}
2022-08-15 04:20:27 -05:00
#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;
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
mode = (read_mode | ext_mode | program_mode);
2022-08-15 04:20:27 -05:00
if (norflash_op_mode != mode) {
norflash_op_mode = mode;
2022-08-15 04:20:27 -05:00
// 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);
}
2022-08-15 04:20:27 -05:00
norflaship_quad_mode(0);
2022-08-15 04:20:27 -05:00
norflaship_dual_mode(0);
2022-08-15 04:20:27 -05:00
#ifdef FLASH_HPM
if (mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
// High performance mode on
norflash_set_hpm(1);
}
2022-08-15 04:20:27 -05:00
#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();
}
2022-08-15 04:20:27 -05:00
#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);
}
2022-08-15 04:20:27 -05:00
#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);
}
2022-08-15 04:20:27 -05:00
#endif
if (mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
// Continuous read on
norflash_set_continuous_read(1);
2022-08-15 04:20:27 -05:00
}
norflaship_cmd_done();
}
2022-08-15 04:20:27 -05:00
norflash_set_read_div();
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_pre_operation(void) {
norflash_set_cfg_div();
2022-08-15 04:20:27 -05:00
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
norflash_set_continuous_read(0);
}
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_post_operation(void) {
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_CONTINUOUS_READ) {
norflash_set_continuous_read(1);
}
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
norflash_set_read_div();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_read_reg(uint8_t cmd, uint8_t *val, uint32_t len) {
int i;
2022-08-15 04:20:27 -05:00
norflaship_clear_fifos();
2022-08-15 04:20:27 -05:00
#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);
2022-08-15 04:20:27 -05:00
} else {
norflaship_ext_rx_cmd(cmd, 0, len);
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
#else
norflaship_ext_rx_cmd(cmd, 0, len);
2022-08-15 04:20:27 -05:00
#endif
norflaship_rxfifo_count_wait(len);
for (i = 0; i < len; i++) {
val[i] = norflaship_read_rxfifo();
}
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
}
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();
2022-08-15 04:20:27 -05:00
norflaship_clear_txfifo();
norflaship_write_txfifo(val, len);
2022-08-15 04:20:27 -05:00
#ifdef TRY_EMBEDDED_CMD
if (cmd == GD25Q32C_CMD_WRITE_STATUS_S0_S7) {
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_STATUS_S0_S7, 0);
} else {
2022-08-15 04:20:27 -05:00
norflaship_ext_tx_cmd(cmd, len);
}
#else
norflaship_ext_tx_cmd(cmd, len);
2022-08-15 04:20:27 -05:00
#endif
norflash_status_WIP_1_wait(0);
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_get_id(uint8_t *value, uint32_t len) {
norflash_pre_operation();
2022-08-15 04:20:27 -05:00
if (len > NORFLASH_ID_LEN) {
len = NORFLASH_ID_LEN;
}
norflash_read_reg(GD25Q32C_CMD_ID, value, len);
2022-08-15 04:20:27 -05:00
norflash_post_operation();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_get_unique_id(uint8_t *value, uint32_t len) {
uint32_t param;
uint8_t cmd;
2022-08-15 04:20:27 -05:00
norflash_pre_operation();
2022-08-15 04:20:27 -05:00
if (flash_list[flash_idx]->id[0] == NORFLASH_XTS_ID_PREFIX) {
if (len > XTS_UNIQUE_ID_LEN) {
len = XTS_UNIQUE_ID_LEN;
2022-08-15 04:20:27 -05:00
}
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);
2022-08-15 04:20:27 -05:00
norflash_post_operation();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div >= 2) {
samdly_list = samdly_list_divn;
size = ARRAY_SIZE(samdly_list_divn);
}
2022-08-15 04:20:27 -05:00
#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);
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
#endif
if (samdly_list_p) {
*samdly_list_p = samdly_list;
}
if (size_p) {
*size_p = size;
}
2022-08-15 04:20:27 -05:00
}
void norflash_set_sample_delay_index(uint32_t index) {
const uint8_t *samdly_list;
uint32_t size;
uint32_t div;
2022-08-15 04:20:27 -05:00
sample_delay_index = index;
2022-08-15 04:20:27 -05:00
div = norflaship_get_div();
2022-08-15 04:20:27 -05:00
norflash_get_samdly_list(div, &samdly_list, &size);
2022-08-15 04:20:27 -05:00
if (index < size) {
2022-08-15 04:20:27 -05:00
#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);
2022-08-15 04:20:27 -05:00
#else
norflaship_samdly(samdly_list[index]);
2022-08-15 04:20:27 -05:00
#endif
}
2022-08-15 04:20:27 -05:00
}
uint32_t norflash_get_sample_delay_index(void) { return sample_delay_index; }
2022-08-15 04:20:27 -05:00
static bool norflash_calib_flash_id_valid(void) {
uint8_t id[HAL_NORFLASH_DEVICE_ID_LEN];
const uint8_t *cmp_id;
2022-08-15 04:20:27 -05:00
norflash_get_id(id, sizeof(id));
cmp_id = flash_list[flash_idx]->id;
2022-08-15 04:20:27 -05:00
if (id[0] == cmp_id[0] && id[1] == cmp_id[1] && id[2] == cmp_id[2]) {
return true;
}
return false;
2022-08-15 04:20:27 -05:00
}
static bool norflash_calib_magic_word_valid(void) {
uint32_t magic;
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
2022-08-15 04:20:27 -05:00
#endif
norflaship_clear_rxfifo();
2022-08-15 04:20:27 -05:00
#if (FLASH_NC_BASE == FLASH_BASE)
hal_cache_invalidate(HAL_CACHE_ID_D_CACHE, FLASH_BASE, sizeof(magic));
magic = *(volatile uint32_t *)FLASH_BASE;
2022-08-15 04:20:27 -05:00
#else
magic = *(volatile uint32_t *)FLASH_NC_BASE;
2022-08-15 04:20:27 -05:00
#endif
if (magic == BOOT_MAGIC_NUMBER) {
return true;
}
return false;
2022-08-15 04:20:27 -05:00
}
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;
}
2022-08-15 04:20:27 -05:00
#if defined(ROM_BUILD) || defined(PROGRAMMER)
if (type != DRV_NORFLASH_CALIB_FLASH_ID) {
return 0;
}
2022-08-15 04:20:27 -05:00
#endif
div = norflaship_get_div();
2022-08-15 04:20:27 -05:00
if (div == 0) {
return -1;
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
} else if (div == 1) {
return -2;
2022-08-15 04:20:27 -05:00
#endif
}
2022-08-15 04:20:27 -05:00
norflash_get_samdly_list(div, NULL, &size);
2022-08-15 04:20:27 -05:00
for (i = 0; i < size; i++) {
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
norflash_set_sample_delay_index(i);
2022-08-15 04:20:27 -05:00
if (type == DRV_NORFLASH_CALIB_FLASH_ID) {
valid = norflash_calib_flash_id_valid();
} else {
valid = norflash_calib_magic_word_valid();
}
2022-08-15 04:20:27 -05:00
if (valid) {
if (matched_cnt == 0) {
matched_idx = i;
}
matched_cnt++;
} else if (matched_cnt) {
break;
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
#ifdef FLASH_CALIB_DEBUG
calib_matched_idx[type] = matched_idx;
calib_matched_cnt[type] = matched_cnt;
2022-08-15 04:20:27 -05:00
#endif
if (matched_cnt) {
2022-08-15 04:20:27 -05:00
#ifdef SAMP_DELAY_PRIO_FALLING_EDGE
if (matched_cnt == 2) {
uint32_t falling_edge_idx;
2022-08-15 04:20:27 -05:00
if (0) {
2022-08-15 04:20:27 -05:00
#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++;
2022-08-15 04:20:27 -05:00
}
} 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;
}
}
2022-08-15 04:20:27 -05:00
#endif
matched_idx += matched_cnt / 2;
norflash_set_sample_delay_index(matched_idx);
2022-08-15 04:20:27 -05:00
#ifdef FLASH_CALIB_DEBUG
calib_final_idx[type] = matched_idx;
2022-08-15 04:20:27 -05:00
#endif
return 0;
}
2022-08-15 04:20:27 -05:00
#ifdef FLASH_CALIB_DEBUG
calib_final_idx[type] = -1;
2022-08-15 04:20:27 -05:00
#endif
return 1;
2022-08-15 04:20:27 -05:00
}
void norflash_show_calib_result(void) {
2022-08-15 04:20:27 -05:00
#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");
2022-08-15 04:20:27 -05:00
#endif
}
int norflash_init_sample_delay_by_div(uint32_t div) {
if (div == 0) {
return -1;
}
if (div == 1) {
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
return -2;
2022-08-15 04:20:27 -05:00
#else
norflaship_samdly(1);
2022-08-15 04:20:27 -05:00
#endif
} else if (div == 2 && !falling_edge_adj) {
// Set sample delay to clock falling edge
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
norflaship_pos_neg(1);
norflaship_neg_phase(1);
norflaship_samdly(2);
2022-08-15 04:20:27 -05:00
#else
norflaship_samdly(3);
2022-08-15 04:20:27 -05:00
#endif
} else {
// Set sample delay to nearest to but not later than clock falling edge
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
norflaship_pos_neg(0);
norflaship_neg_phase(0);
norflaship_samdly(2);
2022-08-15 04:20:27 -05:00
#else
norflaship_samdly(4);
2022-08-15 04:20:27 -05:00
#endif
}
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
#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;
}
2022-08-15 04:20:27 -05:00
if (div_read && div_std_read && div_others) {
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div_read == 1) {
return -1;
}
2022-08-15 04:20:27 -05:00
#else
if (div_read == 1 && max_speed > NORFLASH_DIV1_MAX_SPEED) {
return -1;
2022-08-15 04:20:27 -05:00
}
#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;
}
2022-08-15 04:20:27 -05:00
return 1;
2022-08-15 04:20:27 -05:00
}
int norflash_match_chip(const uint8_t *id, uint32_t len) {
const uint8_t *cmp_id;
2022-08-15 04:20:27 -05:00
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;
}
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
return false;
2022-08-15 04:20:27 -05:00
}
#ifdef PUYA_FLASH_ERASE_PAGE_ENABLE
static void norflaship_ext_cmd_addr(uint8_t cmd, uint32_t addr) {
uint8_t buff[3];
2022-08-15 04:20:27 -05:00
buff[2] = (uint8_t)(addr & 0xff);
buff[1] = (uint8_t)((addr >> 8) & 0xff);
buff[0] = (uint8_t)((addr >> 16) & 0xff);
2022-08-15 04:20:27 -05:00
norflaship_clear_txfifo();
norflaship_write_txfifo(buff, 3);
norflaship_ext_tx_cmd(cmd, 3);
2022-08-15 04:20:27 -05:00
}
#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;
2022-08-15 04:20:27 -05:00
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);
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
// Need 1us. Or norflash_status_WEL_0_wait(), which needs 6us.
switch (type) {
2022-08-15 04:20:27 -05:00
#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();
2022-08-15 04:20:27 -05:00
#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);
}
2022-08-15 04:20:27 -05:00
#endif
ret = norflash_status_WIP_1_wait(suspend);
2022-08-15 04:20:27 -05:00
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);
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return ret;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
if (len > GD25Q32C_PAGE_SIZE) {
return HAL_NORFLASH_ERR;
}
2022-08-15 04:20:27 -05:00
norflaship_clear_txfifo();
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER >= 2)
norflaship_blksize(len);
2022-08-15 04:20:27 -05:00
#endif
remains = norflaship_write_txfifo(buffer, len);
2022-08-15 04:20:27 -05:00
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);
}
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER >= 2)
while (remains > 0) {
buffer += len - remains;
len = remains;
remains = norflaship_write_txfifo(buffer, len);
}
2022-08-15 04:20:27 -05:00
#endif
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
#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);
}
2022-08-15 04:20:27 -05:00
#endif
ret = norflash_status_WIP_1_wait(suspend);
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return ret;
2022-08-15 04:20:27 -05:00
}
#ifdef FLASH_SUSPEND
enum HAL_NORFLASH_RET_T norflash_erase_resume(int suspend) {
// TODO: Need to check SUS1 bit in status reg?
2022-08-15 04:20:27 -05:00
enum HAL_NORFLASH_RET_T ret;
2022-08-15 04:20:27 -05:00
norflash_pre_operation();
2022-08-15 04:20:27 -05:00
norflash_resume();
2022-08-15 04:20:27 -05:00
ret = norflash_status_WIP_1_wait(suspend);
2022-08-15 04:20:27 -05:00
norflash_post_operation();
2022-08-15 04:20:27 -05:00
return ret;
2022-08-15 04:20:27 -05:00
}
enum HAL_NORFLASH_RET_T norflash_write_resume(int suspend) {
// TODO: Need to check SUS2 bit in status reg?
2022-08-15 04:20:27 -05:00
return norflash_erase_resume(suspend);
2022-08-15 04:20:27 -05:00
}
#endif
int norflash_read(uint32_t start_address, uint8_t *buffer, uint32_t len) {
uint32_t index = 0;
uint8_t val;
2022-08-15 04:20:27 -05:00
if (len > NORFLASHIP_RXFIFO_SIZE) {
return 1;
}
2022-08-15 04:20:27 -05:00
norflaship_clear_rxfifo();
2022-08-15 04:20:27 -05:00
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
norflaship_blksize(len);
2022-08-15 04:20:27 -05:00
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);
}
2022-08-15 04:20:27 -05:00
while (1) {
norflaship_rxfifo_empty_wait();
2022-08-15 04:20:27 -05:00
val = norflaship_read_rxfifo();
if (buffer) {
buffer[index] = val;
}
2022-08-15 04:20:27 -05:00
++index;
if (index >= len) {
break;
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
void norflash_sleep(void) {
norflash_pre_operation();
2022-08-15 04:20:27 -05:00
#ifdef FLASH_HPM
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
norflash_set_hpm(0);
}
2022-08-15 04:20:27 -05:00
#endif
norflaship_cmd_addr(GD25Q32C_CMD_DEEP_POWER_DOWN, 0);
2022-08-15 04:20:27 -05:00
}
void norflash_wakeup(void) {
norflaship_cmd_addr(GD25Q32C_CMD_RELEASE_FROM_DP, 0);
// Wait 20us for flash to finish
norflash_delay(40);
2022-08-15 04:20:27 -05:00
#ifdef FLASH_HPM
if (norflash_op_mode & HAL_NORFLASH_OP_MODE_HIGH_PERFORMANCE) {
norflash_set_hpm(1);
}
2022-08-15 04:20:27 -05:00
#endif
norflash_post_operation();
2022-08-15 04:20:27 -05:00
}
int norflash_init_status(uint32_t status) {
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
2022-08-15 04:20:27 -05:00
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_INIT, status);
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
int norflash_set_block_protection(uint32_t bp) {
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
2022-08-15 04:20:27 -05:00
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_BP, bp);
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
#ifdef FLASH_SECURITY_REGISTER
int norflash_security_register_lock(uint32_t id) {
if (flash_list[flash_idx]->write_status == NULL) {
return -1;
}
2022-08-15 04:20:27 -05:00
flash_list[flash_idx]->write_status(DRV_NORFLASH_W_STATUS_LB, id);
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
enum HAL_NORFLASH_RET_T
norflash_security_register_erase(uint32_t start_address) {
enum HAL_NORFLASH_RET_T ret;
2022-08-15 04:20:27 -05:00
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
// Need 1us. Or norflash_status_WEL_0_wait(), which needs 6us.
2022-08-15 04:20:27 -05:00
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_ERASE, start_address);
2022-08-15 04:20:27 -05:00
ret = norflash_status_WIP_1_wait(0);
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return ret;
2022-08-15 04:20:27 -05:00
}
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;
2022-08-15 04:20:27 -05:00
// Security register page size might be larger than normal page size
// E.g., the size of P25Q32L and P25Q64L is 1024
2022-08-15 04:20:27 -05:00
norflaship_clear_txfifo();
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
uint32_t div = 0;
2022-08-15 04:20:27 -05:00
if (len > NORFLASHIP_TXFIFO_SIZE) {
div = norflaship_get_div();
2022-08-15 04:20:27 -05:00
// Slow down to avoid tx fifo underflow (it takes about 10 cpu cycles to
// fill one byte)
norflaship_div(16);
}
2022-08-15 04:20:27 -05:00
remains = norflaship_v1_write_txfifo_safe(buffer, len);
2022-08-15 04:20:27 -05:00
#else
norflaship_blksize(len);
2022-08-15 04:20:27 -05:00
remains = norflaship_write_txfifo(buffer, len);
2022-08-15 04:20:27 -05:00
#endif
norflaship_cmd_addr(GD25Q32C_CMD_WRITE_ENABLE, start_address);
2022-08-15 04:20:27 -05:00
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_PROGRAM, start_address);
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
if (remains) {
norflaship_v1_write_txfifo_all(buffer, len);
}
2022-08-15 04:20:27 -05:00
#else
while (remains > 0) {
buffer += len - remains;
len = remains;
remains = norflaship_write_txfifo(buffer, len);
}
2022-08-15 04:20:27 -05:00
#endif
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
ret = norflash_status_WIP_1_wait(0);
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
if (div) {
// Restore the old div
norflaship_div(div);
}
2022-08-15 04:20:27 -05:00
#endif
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return ret;
2022-08-15 04:20:27 -05:00
}
int norflash_security_register_read(uint32_t start_address, uint8_t *buffer,
uint32_t len) {
uint32_t index = 0;
2022-08-15 04:20:27 -05:00
if (len > NORFLASHIP_RXFIFO_SIZE) {
return 1;
}
2022-08-15 04:20:27 -05:00
norflaship_clear_rxfifo();
2022-08-15 04:20:27 -05:00
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
norflaship_blksize(len);
2022-08-15 04:20:27 -05:00
norflaship_cmd_addr(GD25Q32C_CMD_SECURITY_REGISTER_READ, start_address);
2022-08-15 04:20:27 -05:00
while (1) {
norflaship_rxfifo_empty_wait();
2022-08-15 04:20:27 -05:00
buffer[index] = norflaship_read_rxfifo();
2022-08-15 04:20:27 -05:00
++index;
if (index >= len) {
break;
2022-08-15 04:20:27 -05:00
}
}
2022-08-15 04:20:27 -05:00
norflaship_cmd_done();
2022-08-15 04:20:27 -05:00
return 0;
2022-08-15 04:20:27 -05:00
}
uint32_t norflash_security_register_enable_read(void) {
uint32_t mode;
2022-08-15 04:20:27 -05:00
mode = norflash_op_mode;
2022-08-15 04:20:27 -05:00
norflash_set_mode(0);
2022-08-15 04:20:27 -05:00
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
2022-08-15 04:20:27 -05:00
#endif
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_rdcmd(GD25Q32C_CMD_SECURITY_REGISTER_READ);
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
return mode;
2022-08-15 04:20:27 -05:00
}
void norflash_security_register_disable_read(uint32_t mode) {
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
#if (CHIP_FLASH_CTRL_VER <= 1)
norflash_read(FLASH_NC_BASE, NULL, 1);
2022-08-15 04:20:27 -05:00
#endif
norflaship_clear_rxfifo();
norflaship_busy_wait();
norflaship_rdcmd(GD25Q32C_CMD_STANDARD_READ);
norflaship_busy_wait();
2022-08-15 04:20:27 -05:00
norflash_set_mode(mode);
2022-08-15 04:20:27 -05:00
}
#endif