/*************************************************************************** * * 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" #include "hal_sleep.h" #include "hal_timer.h" #include "hal_trace.h" #include "norflash_gd25q32c.h" #include "plat_addr_map.h" #include "plat_types.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 *)¶m, 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, "\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, "\nstd_read=%u/8 others=%u/8", (ratio.s.std_read + 1), (ratio.s.others + 1)); div = norflaship_get_div(); TRACE(0, "
\ndiv=%u", div); norflash_get_samdly_list(div, &list, &size); TRACE(0, ""); if (list == NULL || size == 0) { TRACE(0, "NONE"); } else { DUMP8("%02X ", list, size); } TRACE(0, ""); 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