From cc432b761c884a0bd8e9d83db0a4e26109fc08b1 Mon Sep 17 00:00:00 2001
From: chen <15335560115@163.com>
Date: 星期五, 08 十一月 2024 15:35:38 +0800
Subject: [PATCH] 安邦手环GPS删除部分无用数据和修改4G波特率9600出厂测试固件

---
 keil/include/drivers/mk_flash.c | 1326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,326 insertions(+), 0 deletions(-)

diff --git a/keil/include/drivers/mk_flash.c b/keil/include/drivers/mk_flash.c
new file mode 100644
index 0000000..5b6c37b
--- /dev/null
+++ b/keil/include/drivers/mk_flash.c
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and
+ * its subsidiaries and affiliates (collectly called MKSEMI).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form, except as embedded into an MKSEMI
+ *    integrated circuit in a product or a software update for such product,
+ *    must reproduce the above copyright notice, this list of conditions and
+ *    the following disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * 3. Neither the name of MKSEMI nor the names of its contributors may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ *    MKSEMI integrated circuit.
+ *
+ * 5. Any software provided in binary form under this license must not be
+ *    reverse engineered, decompiled, modified and/or disassembled.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mk_flash.h"
+#include "mk_clock.h"
+#include "mk_reset.h"
+#include "mk_trace.h"
+#include "mk_misc.h"
+
+#if FLASH_DMA_MODE_EN
+static void flash_dma_callback(void *ch, uint32_t err_code);
+static void flash_dma_write_nbytes_callback(void *ch, uint32_t err_code);
+#endif
+
+struct FLASH_HANDLE_T flash_handle[FLASH_MAX_NUM] = {
+    {
+        .base = FLASH_CTRL,
+        .irq = FLASH_CTRL_IRQn,
+        .dma_wr_ch = DMA_CH2,
+        .dma_rd_ch = DMA_CH3,
+        .config =
+        {
+            .timeout = 0xFFFFU,
+            .cs_high_time = 0xFU,
+            .dual_mode = false,
+            .prefetch_dis = false,
+            .cache_prefetch_dis = false,
+            .feedback_clk_sel = false,
+            .spi_sck_high = false,
+            .spi_falling_edge_active = true,
+            .dma_en = false,
+            .int_en = false,
+            .start_addr = FLASH_BASE,
+            .total_size = FLASH_SIZE,
+            .block_size = FLASH_BLOCK_SIZE,
+            .sector_size = FLASH_SECTOR_SIZE,
+            .page_size = FLASH_PAGE_SIZE,
+        },
+        .callback = NULL,
+        .flash_type = 0,
+        .wrsr_type = 0,    // MXIC automotive flash type need set to 1
+        .work_mode = 0,    // high performance mode
+        .promote_fclk = 0, // promote flash controller clock if 1
+#ifndef XIP_EN
+        .is_open = 0,
+#else
+        .is_open = 1, // Flash has been opened by default in XIP mode
+#endif
+    },
+};
+
+static const struct FLASH_CMD_T flash_cmd[FLASH_MAX_NUM][FLASH_OPCODE_NUM] = {
+    {
+        // QUAD
+        //{false, FLASH_DATA_IN, FLASH_CMD_DATA_QUAD_DUAL, FLASH_CMD_OP_ADDR_3B, FLASH_SECTOR_SIZE, 1, 0x6B},
+        {false, FLASH_DATA_IN, FLASH_CMD_OP_SERIAL, FLASH_CMD_OP_ADDR_3B, FLASH_SECTOR_SIZE, 3, 0xEB},
+        {false, FLASH_DATA_OUT, FLASH_CMD_DATA_QUAD_DUAL, FLASH_CMD_OP_ADDR_3B, FLASH_PAGE_SIZE, 0, 0x32},
+        // DUAL
+        //{false, FLASH_DATA_IN, FLASH_CMD_DATA_QUAD_DUAL, FLASH_CMD_OP_ADDR_3B, FLASH_SECTOR_SIZE, 1, 0x3B},
+        //{false, FLASH_DATA_IN, FLASH_CMD_OP_SERIAL, FLASH_CMD_OP_ADDR_3B, FLASH_SECTOR_SIZE, 1, 0xBB},
+        //{false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ADDR_3B, FLASH_PAGE_SIZE, 0, 0x02},
+        // STD
+        //{false, FLASH_DATA_IN, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ADDR_3B, FLASH_SECTOR_SIZE, 0, 0x03},
+        //{false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ADDR_3B, FLASH_PAGE_SIZE, 0, 0x02},
+        {false, FLASH_DATA_IN, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 1, 0, 0x05},
+        {false, FLASH_DATA_IN, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 1, 0, 0x35},
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ADDR_3B, 0, 0, 0x20},
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ADDR_3B, 0, 0, 0xD8},
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 0, 0, 0x06},
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 2, 0, 0x01},
+        {false, FLASH_DATA_IN, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 3, 0, 0x9F},
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 0, 0, 0xB9}, // Deep power down
+        {false, FLASH_DATA_OUT, FLASH_CMD_ALL_SERIAL, FLASH_CMD_OP_ONLY, 0, 0, 0xAB}, // Release from Deep power down
+    },
+};
+
+static void flash_wait_status(enum FLASH_DEV_T id, uint32_t mask, uint32_t status)
+{
+    uint32_t start = sys_timer_get();
+    while ((flash_handle[id].base->STATUS & mask) == status)
+    {
+        if (sys_timer_get() - start > flash_handle[id].config.timeout)
+        {
+            break;
+        }
+    }
+}
+
+static void flash_reset_cmd(enum FLASH_DEV_T id)
+{
+    flash_handle[id].base->STATUS = FLASH_STATUS_RESET_MSK;
+    /* Wait for the RESET flag cleared by HW */
+    flash_wait_status(id, FLASH_STATUS_RESET_MSK, FLASH_STATUS_RESET_MSK);
+}
+
+static void flash_write_cmd(enum FLASH_DEV_T id, enum FLASH_OPCODE_T opcode)
+{
+    /* Wait for the CMD and MCINT flag all be 0 */
+    flash_wait_status(id, FLASH_STATUS_MCINIT_MSK, FLASH_STATUS_MCINIT_MSK);
+    flash_wait_status(id, FLASH_STATUS_CMD_MSK, FLASH_STATUS_CMD_MSK);
+
+    uint8_t op_code = flash_cmd[id][opcode].opcode;
+    enum FLASH_CMD_FORMAT_T cmd_format = flash_cmd[id][opcode].format;
+    uint16_t data_len = flash_cmd[id][opcode].data_len;
+    if (flash_handle[id].flash_type == 1)
+    {
+        if (opcode == FLASH_OPCODE_PROGRAM_PAGE)
+        {
+            op_code = 0x38;
+            cmd_format = FLASH_CMD_OP_SERIAL;
+        }
+
+        if ((flash_handle[id].wrsr_type == 1) && (opcode == FLASH_OPCODE_WRITE_REGISTER))
+        {
+            data_len = 3;
+        }
+    }
+
+    flash_handle[id].base->CMD = FLASH_CMD_DATALEN(data_len) | FLASH_CMD_POLL(flash_cmd[id][opcode].polling_mode) |
+                                 FLASH_CMD_DOUT(flash_cmd[id][opcode].data_dir) | FLASH_CMD_INTLEN(flash_cmd[id][opcode].dummy_len) |
+                                 FLASH_CMD_FIELDFORM(cmd_format) | FLASH_CMD_FRAMEFORM(flash_cmd[id][opcode].type) | FLASH_CMD_OPCODE(op_code);
+
+    if (data_len && (flash_cmd[id][opcode].data_dir == FLASH_DATA_OUT))
+    {
+        /* Wait for the command written */
+        flash_wait_status(id, FLASH_STATUS_CMD_MSK, 0U);
+    }
+}
+
+#if FLASH_WRITE_EN
+static void flash_write_variable_len_cmd(enum FLASH_DEV_T id, const uint32_t len)
+{
+    /* Wait for the CMD and MCINT flag all be 0 */
+    flash_wait_status(id, FLASH_STATUS_MCINIT_MSK, FLASH_STATUS_MCINIT_MSK);
+    flash_wait_status(id, FLASH_STATUS_CMD_MSK, FLASH_STATUS_CMD_MSK);
+
+    uint8_t op_code = flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].opcode;
+    enum FLASH_CMD_FORMAT_T cmd_format = flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].format;
+    if (flash_handle[id].flash_type == 1)
+    {
+        op_code = 0x38;
+        cmd_format = FLASH_CMD_OP_SERIAL;
+    }
+
+    flash_handle[id].base->CMD = FLASH_CMD_DATALEN(len) | FLASH_CMD_POLL(flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].polling_mode) |
+                                 FLASH_CMD_DOUT(flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].data_dir) |
+                                 FLASH_CMD_INTLEN(flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].dummy_len) | FLASH_CMD_FIELDFORM(cmd_format) |
+                                 FLASH_CMD_FRAMEFORM(flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].type) | FLASH_CMD_OPCODE(op_code);
+
+    if (len && (flash_cmd[id][FLASH_OPCODE_PROGRAM_PAGE].data_dir == FLASH_DATA_OUT))
+    {
+        /* Wait for the command written */
+        flash_wait_status(id, FLASH_STATUS_CMD_MSK, 0U);
+    }
+}
+#endif
+
+static void flash_write_mem_cmd(enum FLASH_DEV_T id, enum FLASH_OPCODE_T opcode)
+{
+    /* Wait for the CMD and MCINT flag all be 0 */
+    flash_wait_status(id, FLASH_STATUS_MCINIT_MSK, FLASH_STATUS_MCINIT_MSK);
+    flash_wait_status(id, FLASH_STATUS_CMD_MSK, FLASH_STATUS_CMD_MSK);
+
+    flash_handle[id].base->MCMD = FLASH_MCMD_POLL(0U) | FLASH_MCMD_DOUT(0U) | FLASH_MCMD_INTLEN(flash_cmd[id][opcode].dummy_len) |
+                                  FLASH_MCMD_FIELDFORM(flash_cmd[id][opcode].format) | FLASH_MCMD_FRAMEFORM(flash_cmd[id][opcode].type) |
+                                  FLASH_MCMD_OPCODE(flash_cmd[id][opcode].opcode);
+}
+
+static uint8_t flash_read_status(enum FLASH_DEV_T id)
+{
+    flash_write_cmd(id, FLASH_OPCODE_GET_STATUS);
+    flash_wait_status(id, FLASH_STATUS_INTRQ_MSK, 0U);
+    uint8_t val = flash_handle[id].base->DATA;
+    return (val);
+}
+
+static uint8_t flash_read_status1(enum FLASH_DEV_T id)
+{
+    flash_write_cmd(id, FLASH_OPCODE_GET_STATUS1);
+    flash_wait_status(id, FLASH_STATUS_INTRQ_MSK, 0U);
+    uint8_t val1 = flash_handle[id].base->DATA;
+    return (val1);
+}
+
+static void flash_wait_done(enum FLASH_DEV_T id, uint32_t timeout)
+{
+    uint32_t start = sys_timer_get();
+    /* Check WIP bit */
+    while (flash_read_status(id) & 0x01)
+    {
+        delay_us(100);
+        if (sys_timer_get() - start > timeout)
+        {
+            break;
+        }
+    }
+}
+
+static void flash_write_quad_mode(enum FLASH_DEV_T id, uint8_t qe_bit)
+{
+    // Write enable
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+    // Set write register command
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_REGISTER);
+
+    // Enable Quad mode
+    if (flash_handle[id].flash_type == 1)
+    {
+        flash_handle[id].base->DATA = qe_bit ? 0x40 : 0x00;
+        flash_handle[id].base->DATA = 0x00;
+
+        if (flash_handle[id].wrsr_type == 1)
+        {
+            // high performance mode
+            flash_handle[id].base->DATA = flash_handle[id].work_mode == 0 ? 0x02 : 0;
+        }
+    }
+    else
+    {
+        flash_handle[id].base->DATA = 0x00;
+        flash_handle[id].base->DATA = qe_bit ? 0x02 : 0x00;
+    }
+    flash_wait_done(id, FLASH_OP_WRITE_STATUS_TIMEOUT);
+}
+
+static int flash_state_update(enum FLASH_DEV_T id, enum FLASH_STATE_T new_state)
+{
+    int ret = DRV_OK;
+    uint32_t lock = int_lock();
+
+    // update state
+    switch (flash_handle[id].state)
+    {
+    case FLASH_STATE_READY:
+        flash_handle[id].state = new_state;
+        break;
+    case FLASH_STATE_BUSY_READ:
+    case FLASH_STATE_BUSY_ERASE:
+    case FLASH_STATE_BUSY_WRITE:
+        ret = DRV_BUSY;
+        break;
+    case FLASH_STATE_RESET:
+    case FLASH_STATE_TIMEOUT:
+    case FLASH_STATE_ERROR:
+        ret = DRV_ERROR;
+        break;
+    }
+    int_unlock(lock);
+
+    return ret;
+}
+
+int flash_open(enum FLASH_DEV_T id, struct FLASH_CFG_T *config)
+{
+    if (id >= FLASH_MAX_NUM)
+    {
+        return DRV_ERROR;
+    }
+
+    if (flash_handle[id].is_open)
+    {
+        return DRV_OK;
+    }
+
+#if BOR_EN
+    bor_open(NULL);
+#endif
+
+    if (config)
+    {
+        memcpy(&flash_handle[id].config, config, sizeof(struct FLASH_CFG_T));
+    }
+    flash_handle[id].code = 0;
+
+    // enable flash controller clock
+    clock_enable(CLOCK_FLASH_CTRL);
+    reset_module(RESET_MODULE_FLASH_CTRL);
+
+    // reset the command register
+    flash_reset_cmd(id);
+
+    // set time delay parameter
+    flash_handle[id].base->CTRL = FLASH_CTRL_TIMEOUT(flash_handle[id].config.timeout) | FLASH_CTRL_CSHIGH(flash_handle[id].config.cs_high_time) |
+                                  FLASH_CTRL_D_PRFTCH_DIS(flash_handle[id].config.prefetch_dis) | FLASH_CTRL_MODE3(flash_handle[id].config.spi_sck_high) |
+                                  FLASH_CTRL_PRFTCH_DIS(flash_handle[id].config.cache_prefetch_dis) | FLASH_CTRL_DUAL(flash_handle[id].config.dual_mode) |
+                                  FLASH_CTRL_RFCLK(flash_handle[id].config.spi_falling_edge_active) |
+                                  FLASH_CTRL_FBCLK(flash_handle[id].config.feedback_clk_sel);
+
+    // Release power down
+    flash_write_cmd(id, FLASH_OPCODE_RELEASE_POWER_DOWN);
+    delay_us(50);
+
+    // Read Identification
+    uint32_t jedec_id = 0;
+    flash_write_cmd(id, FLASH_OPCODE_RDID);
+    flash_wait_status(id, FLASH_STATUS_INTRQ_MSK, 0U);
+
+    jedec_id = flash_handle[id].base->DATA;
+    if (jedec_id == 0xC2)
+    {
+        flash_handle[id].flash_type = 1;
+    }
+    else
+    {
+        flash_handle[id].flash_type = 0;
+    }
+    jedec_id = (jedec_id << 8) + flash_handle[id].base->DATA;
+    jedec_id = (jedec_id << 8) + flash_handle[id].base->DATA;
+    LOG_INFO(TRACE_MODULE_DRIVER, "jedec_id %x\r\n", jedec_id);
+
+    // Check Quad enable bit, its a non-volatile bit
+    uint8_t qe_bit = 0;
+    if (flash_handle[id].flash_type == 1)
+    {
+        // write quad mode anyway
+        flash_write_quad_mode(id, !flash_handle[id].config.dual_mode);
+    }
+    else
+    {
+        qe_bit = (flash_read_status1(id) >> 1) & 0x1;
+
+        // write quad mode if needed
+        if (((flash_handle[id].config.dual_mode == false) && (qe_bit == 0)) || ((flash_handle[id].config.dual_mode == true) && (qe_bit == 1)))
+        {
+            flash_write_quad_mode(id, !qe_bit);
+        }
+    }
+
+    if (flash_handle[id].promote_fclk == 1)
+    {
+        clock_set_divider(CLOCK_FLASH_CTRL_DIV, CLOCK_DIVIDED_BY_1);
+    }
+
+    // setup memory command
+    flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+#if FLASH_INT_MODE_EN
+    // enable IRQ
+    if (flash_handle[id].config.int_en)
+    {
+        NVIC_SetPriority(flash_handle[id].irq, IRQ_PRIORITY_NORMAL);
+        NVIC_ClearPendingIRQ(flash_handle[id].irq);
+        NVIC_EnableIRQ(flash_handle[id].irq);
+    }
+#endif
+
+    flash_handle[id].state = FLASH_STATE_READY;
+    flash_handle[id].is_open = 1;
+
+    return DRV_OK;
+}
+
+int flash_close(enum FLASH_DEV_T id)
+{
+    if (id >= FLASH_MAX_NUM)
+    {
+        return DRV_ERROR;
+    }
+
+    // If the XIP_EN macro is defined, the flash close operation is ignored
+#ifndef XIP_EN
+    if (!flash_handle[id].is_open)
+    {
+        return DRV_DEV_UNAVAILABLE;
+    }
+
+    // power down
+    flash_write_cmd(id, FLASH_OPCODE_POWER_DOWN);
+    delay_us(15);
+
+    // disable flash controller clock or power
+    clock_disable(CLOCK_FLASH_CTRL);
+
+    flash_handle[id].state = FLASH_STATE_RESET;
+    flash_handle[id].is_open = 0;
+
+#if BOR_EN
+    bor_close();
+#endif
+#endif
+
+    return DRV_OK;
+}
+
+int flash_open_for_xip(enum FLASH_DEV_T id)
+{
+    flash_handle[id].code = 0;
+
+    // enable flash controller clock
+    SYSCON->SYS_CMU |= (1U << CLOCK_FLASH_CTRL);
+    SYSCON->SYS_RMU &= ~(1U << RESET_MODULE_FLASH_CTRL);
+    SYSCON->SYS_RMU |= (1U << RESET_MODULE_FLASH_CTRL);
+
+    // reset the command register
+    flash_handle[id].base->STATUS = FLASH_STATUS_RESET_MSK;
+
+    /* Wait for the RESET flag cleared by HW */
+    uint8_t start = 0;
+    while ((flash_handle[id].base->STATUS & FLASH_STATUS_RESET_MSK) == FLASH_STATUS_RESET_MSK)
+    {
+        delay_us(10);
+        start++;
+        if (start > 100)
+        {
+            break;
+        }
+    }
+
+    // set time delay parameter
+    flash_handle[id].base->CTRL = FLASH_CTRL_TIMEOUT(flash_handle[id].config.timeout) | FLASH_CTRL_CSHIGH(flash_handle[id].config.cs_high_time) |
+                                  FLASH_CTRL_D_PRFTCH_DIS(flash_handle[id].config.prefetch_dis) | FLASH_CTRL_MODE3(flash_handle[id].config.spi_sck_high) |
+                                  FLASH_CTRL_PRFTCH_DIS(flash_handle[id].config.cache_prefetch_dis) | FLASH_CTRL_DUAL(flash_handle[id].config.dual_mode) |
+                                  FLASH_CTRL_RFCLK(flash_handle[id].config.spi_falling_edge_active) |
+                                  FLASH_CTRL_FBCLK(flash_handle[id].config.feedback_clk_sel);
+
+    /* Wait for the CMD and MCINT flag all be 0 */
+    start = 0;
+    while ((flash_handle[id].base->STATUS & (FLASH_STATUS_MCINIT_MSK | FLASH_STATUS_CMD_MSK)) == (FLASH_STATUS_MCINIT_MSK | FLASH_STATUS_CMD_MSK))
+    {
+        delay_us(10);
+        start++;
+        if (start > 100)
+        {
+            break;
+        }
+    }
+
+    // setup memory command
+    flash_handle[id].base->MCMD = FLASH_MCMD_POLL(0U) | FLASH_MCMD_DOUT(0U) | FLASH_MCMD_INTLEN(3) | FLASH_MCMD_FIELDFORM(FLASH_CMD_OP_SERIAL) |
+                                  FLASH_MCMD_FRAMEFORM(FLASH_CMD_OP_ADDR_3B) | FLASH_MCMD_OPCODE(0xEB);
+
+    flash_handle[id].state = FLASH_STATE_READY;
+    flash_handle[id].is_open = 1;
+
+    return DRV_OK;
+}
+
+void flash_power_up(enum FLASH_DEV_T id)
+{
+    if (flash_handle[id].is_open)
+    {
+        // Release power down
+        flash_handle[id].base->CMD = FLASH_CMD_DATALEN(0) | FLASH_CMD_POLL(false) | FLASH_CMD_DOUT(FLASH_DATA_OUT) | FLASH_CMD_INTLEN(0) |
+                                     FLASH_CMD_FIELDFORM(FLASH_CMD_ALL_SERIAL) | FLASH_CMD_FRAMEFORM(FLASH_CMD_OP_ONLY) | FLASH_CMD_OPCODE(0xAB);
+
+        delay_us(30);
+        // flash_write_cmd(id, FLASH_OPCODE_RELEASE_POWER_DOWN);
+    }
+}
+
+void flash_power_down(enum FLASH_DEV_T id)
+{
+    if (flash_handle[id].is_open)
+    {
+        // Power down
+        flash_handle[id].base->CMD = FLASH_CMD_DATALEN(0) | FLASH_CMD_POLL(false) | FLASH_CMD_DOUT(FLASH_DATA_OUT) | FLASH_CMD_INTLEN(0) |
+                                     FLASH_CMD_FIELDFORM(FLASH_CMD_ALL_SERIAL) | FLASH_CMD_FRAMEFORM(FLASH_CMD_OP_ONLY) | FLASH_CMD_OPCODE(0xB9);
+
+        // flash_write_cmd(id, FLASH_OPCODE_POWER_DOWN);
+        delay_us(15);
+    }
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
+#if FLASH_WRITE_EN
+static int flash_page_write_nbytes(enum FLASH_DEV_T id, uint32_t addr, const uint8_t *buf, uint32_t len)
+{
+    // Write enable
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+    // Set address
+    flash_handle[id].base->ADDR = addr;
+
+    // program page
+    flash_write_variable_len_cmd(id, len);
+
+    for (uint32_t i = 0; i < len; i++)
+    {
+        flash_handle[id].base->DATA = buf[i];
+    }
+
+    flash_wait_done(id, FLASH_OP_WRITE_PAGE_TIMEOUT);
+
+    return DRV_OK;
+}
+
+static int flash_page_write(enum FLASH_DEV_T id, uint32_t page, const uint8_t *buf)
+{
+    LOG_INFO(TRACE_MODULE_DRIVER, "flash_page_write %d\r\n", page);
+
+    // Write enable
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+    // Set address
+    flash_handle[id].base->ADDR = flash_handle[id].config.start_addr + (page * flash_handle[id].config.page_size);
+
+    // program page
+    flash_write_cmd(id, FLASH_OPCODE_PROGRAM_PAGE);
+
+    for (uint32_t i = 0; i < flash_handle[id].config.page_size; i++)
+    {
+        flash_handle[id].base->DATA = buf[i];
+    }
+
+    flash_wait_done(id, FLASH_OP_WRITE_PAGE_TIMEOUT);
+
+    return DRV_OK;
+}
+
+int flash_sector_erase(enum FLASH_DEV_T id, uint32_t sector_idx)
+{
+    // LOG_INFO(TRACE_MODULE_DRIVER, "flash_sector_erase %d\r\n", sector_idx);
+    ASSERT(sector_idx < (flash_handle[id].config.total_size / flash_handle[id].config.sector_size), "sector idx is over range\r\n");
+
+    int ret = flash_state_update(id, FLASH_STATE_BUSY_ERASE);
+    if (ret != DRV_OK)
+    {
+        return ret;
+    }
+
+    // Reset the flash controller to switch to command mode
+    flash_reset_cmd(id);
+
+    // Write enable
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+    // Set address
+    flash_handle[id].base->ADDR = flash_handle[id].config.start_addr + (sector_idx * flash_handle[id].config.sector_size);
+
+#if FLASH_INT_MODE_EN
+    if (flash_handle[id].config.int_en)
+    {
+        // clear interrupt
+        flash_handle[id].base->STATUS |= FLASH_STATUS_INTRQ_MSK;
+
+        // enable INT
+        flash_handle[id].base->CTRL |= FLASH_CTRL_INTEN_MSK;
+
+        flash_handle[id].code = FLASH_OP_ERASE;
+    }
+#endif
+
+    // Erase sector
+    flash_write_cmd(id, FLASH_OPCODE_ERASE_SECTOR);
+
+    if (flash_handle[id].config.int_en == 0)
+    {
+        flash_wait_done(id, FLASH_OP_ERASE_SECTOR_TIMEOUT);
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        flash_handle[id].state = FLASH_STATE_READY;
+    }
+
+    return DRV_OK;
+}
+
+int flash_block_erase(enum FLASH_DEV_T id, uint32_t block_idx)
+{
+    // LOG_INFO(TRACE_MODULE_DRIVER, "flash_sector_erase %d\r\n", block_idx);
+    ASSERT(block_idx < (flash_handle[id].config.total_size / flash_handle[id].config.block_size), "block idx is over range\r\n");
+
+    int ret = flash_state_update(id, FLASH_STATE_BUSY_ERASE);
+    if (ret != DRV_OK)
+    {
+        return ret;
+    }
+
+    // Reset the flash controller to switch to command mode
+    flash_reset_cmd(id);
+
+    // Write enable
+    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+    // Set address
+    flash_handle[id].base->ADDR = flash_handle[id].config.start_addr + (block_idx * flash_handle[id].config.block_size);
+
+#if FLASH_INT_MODE_EN
+    if (flash_handle[id].config.int_en)
+    {
+        // clear interrupt
+        flash_handle[id].base->STATUS |= FLASH_STATUS_INTRQ_MSK;
+
+        // enable INT
+        flash_handle[id].base->CTRL |= FLASH_CTRL_INTEN_MSK;
+
+        flash_handle[id].code = FLASH_OP_ERASE;
+    }
+#endif
+
+    // Erase block
+    flash_write_cmd(id, FLASH_OPCODE_ERASE_BLOCK);
+
+    if (flash_handle[id].config.int_en == 0)
+    {
+        flash_wait_done(id, FLASH_OP_ERASE_BLOCK_TIMEOUT);
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        flash_handle[id].state = FLASH_STATE_READY;
+    }
+    return DRV_OK;
+}
+
+// limitation: start address and len should alagin with sector size
+int flash_erase(enum FLASH_DEV_T id, uint32_t start_addr, uint32_t len)
+{
+    if ((start_addr < FLASH_BASE) || (start_addr > FLASH_BASE + FLASH_SIZE - len) || (len == 0))
+    {
+        return DRV_ERROR;
+    }
+
+    if ((start_addr % flash_handle[id].config.sector_size) || (len % flash_handle[id].config.sector_size))
+    {
+        return DRV_ERROR;
+    }
+
+    uint32_t offset_addr = start_addr - flash_handle[id].config.start_addr;
+    uint32_t sector_start = offset_addr / flash_handle[id].config.sector_size;
+    uint32_t sector_end = (offset_addr + len) / flash_handle[id].config.sector_size;
+    uint32_t sectors_per_block = flash_handle[id].config.block_size / flash_handle[id].config.sector_size;
+
+    if ((sector_end - sector_start) < sectors_per_block)
+    {
+        for (uint32_t i = sector_start; i < sector_end; i++)
+        {
+            flash_sector_erase(id, i);
+            while (flash_check_busy(id))
+            {
+            }
+        }
+    }
+    else
+    {
+        uint32_t block_start = offset_addr / flash_handle[id].config.block_size;
+        uint32_t block_end = (offset_addr + len) / flash_handle[id].config.block_size;
+
+        if (offset_addr % flash_handle[id].config.block_size)
+        {
+            block_start += 1;
+        }
+        uint32_t sector_tail_num = ((offset_addr + len) % flash_handle[id].config.block_size) / flash_handle[id].config.sector_size;
+
+        for (uint32_t i = sector_start; i < block_start * sectors_per_block; i++)
+        {
+            flash_sector_erase(id, i);
+            while (flash_check_busy(id))
+            {
+            }
+        }
+
+        for (uint32_t i = block_start; i < block_end; i++)
+        {
+            flash_block_erase(id, i);
+            while (flash_check_busy(id))
+            {
+            }
+        }
+
+        uint32_t sector_tail_start = block_end * sectors_per_block;
+        for (uint32_t i = 0; i < sector_tail_num; i++)
+        {
+            flash_sector_erase(id, sector_tail_start + i);
+            while (flash_check_busy(id))
+            {
+            }
+        }
+    }
+
+    return DRV_OK;
+}
+
+static void flash_init_write_nbytes_cfg(enum FLASH_DEV_T id, const uint32_t dest_addr, const uint8_t *src_addr, const uint32_t write_len)
+{
+    flash_handle[id].wr_nbyte_cfg.src_addr = src_addr;
+    flash_handle[id].wr_nbyte_cfg.src_buf_pos = 0;
+    flash_handle[id].wr_nbyte_cfg.dest_tmp_addr = dest_addr;
+    flash_handle[id].wr_nbyte_cfg.write_start_addr = dest_addr;
+    flash_handle[id].wr_nbyte_cfg.write_end_addr = dest_addr + write_len - 0x01U;
+    flash_handle[id].wr_nbyte_cfg.offset_addr = dest_addr - flash_handle[id].config.start_addr;
+    flash_handle[id].wr_nbyte_cfg.page_start = flash_handle[id].wr_nbyte_cfg.offset_addr / flash_handle[id].config.page_size;
+    flash_handle[id].wr_nbyte_cfg.page_end = (flash_handle[id].wr_nbyte_cfg.offset_addr + write_len - 0x01U) / flash_handle[id].config.page_size;
+    flash_handle[id].wr_nbyte_cfg.page_count = flash_handle[id].wr_nbyte_cfg.page_end - flash_handle[id].wr_nbyte_cfg.page_start;
+    flash_handle[id].wr_nbyte_cfg.write_num = flash_handle[id].wr_nbyte_cfg.page_count + 0x01U;
+    flash_handle[id].wr_nbyte_cfg.write_count = 0x0U;
+
+    if (0 == flash_handle[id].wr_nbyte_cfg.page_count)
+    {
+        flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len = write_len;
+        flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len = 0x0U;
+    }
+    else
+    {
+        flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len =
+            flash_handle[id].config.page_size -
+            (dest_addr - (flash_handle[id].wr_nbyte_cfg.page_start * flash_handle[id].config.page_size + flash_handle[id].config.start_addr));
+        flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len =
+            flash_handle[id].wr_nbyte_cfg.write_end_addr -
+            (flash_handle[id].wr_nbyte_cfg.page_end * flash_handle[id].config.page_size + flash_handle[id].config.start_addr) + 0x01U;
+    }
+}
+
+int flash_write_nbytes(enum FLASH_DEV_T id, uint32_t start_addr, const uint8_t *buf, uint32_t len)
+{
+#define WRITE_ONE_PAGES 0x00U
+#define WRITE_TWO_PAGES 0x01U
+
+    if ((start_addr < FLASH_BASE) || (start_addr > FLASH_BASE + FLASH_SIZE - len) || (len == 0))
+    {
+        return DRV_ERROR;
+    }
+
+    int ret = flash_state_update(id, FLASH_STATE_BUSY_WRITE);
+    if (ret != DRV_OK)
+    {
+        return ret;
+    }
+
+    flash_init_write_nbytes_cfg(id, start_addr, buf, len);
+
+    // Reset the flash controller to switch to command mode
+    flash_reset_cmd(id);
+    if (flash_handle[id].config.dma_en)
+    {
+#if FLASH_DMA_MODE_EN
+        struct DMA_CH_CFG_T flash_wr_dma_cfg = {
+            .fifo_th = DMA_FIFO_TH_1,
+            .src_burst_size = DMA_SRC_BURST_SIZE_1,
+            .src_width = DMA_WIDTH_1B,
+            .dst_width = DMA_WIDTH_1B,
+            .src_addr_ctrl = DMA_ADDR_INC,
+            .dst_addr_ctrl = DMA_ADDR_FIXED,
+            .src_req_sel = DMA_REQ_MEM,
+            .dst_req_sel = DMA_REQ_FLASH,
+        };
+        // enable DMA
+        flash_handle[id].base->CTRL |= FLASH_CTRL_DMAEN_MSK;
+        dma_open(flash_handle[id].dma_wr_ch, &flash_wr_dma_cfg);
+
+        flash_handle[id].dma_op.start_addr = flash_handle[id].wr_nbyte_cfg.dest_tmp_addr;
+        flash_handle[id].dma_op.buf = flash_handle[id].wr_nbyte_cfg.src_addr;
+        flash_handle[id].dma_op.len = len;
+        flash_handle[id].dma_op.count = 0;
+        flash_handle[id].code = FLASH_OP_WRITE;
+
+        flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+        flash_handle[id].wr_nbyte_cfg.dest_tmp_addr += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+        flash_handle[id].wr_nbyte_cfg.write_count += 0x01U;
+
+        if (0x01U == flash_handle[id].wr_nbyte_cfg.write_count)
+        {
+            // Write enable
+            flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+            // Set address
+            flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+
+            // program n bytes
+            flash_write_variable_len_cmd(id, flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len);
+
+            dma_transfer(flash_handle[id].dma_wr_ch, (uint8_t *)flash_handle[id].dma_op.buf, (uint8_t *)&flash_handle[id].base->DATA,
+                         flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len, flash_dma_write_nbytes_callback);
+        }
+#endif
+    }
+    else
+    {
+        for (flash_handle[id].wr_nbyte_cfg.write_count = 0x01U; flash_handle[id].wr_nbyte_cfg.write_count <= flash_handle[id].wr_nbyte_cfg.write_num;
+                flash_handle[id].wr_nbyte_cfg.write_count++)
+        {
+            switch (flash_handle[id].wr_nbyte_cfg.page_count)
+            {
+            case WRITE_ONE_PAGES:
+                flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                        flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len);
+                break;
+
+            case WRITE_TWO_PAGES:
+                if (0x01U == flash_handle[id].wr_nbyte_cfg.write_count)
+                {
+                    flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                            flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len);
+                    flash_handle[id].wr_nbyte_cfg.dest_tmp_addr += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+                    flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+                }
+                else if (flash_handle[id].wr_nbyte_cfg.write_count == flash_handle[id].wr_nbyte_cfg.write_num)
+                {
+                    flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                            flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len);
+                }
+                break;
+
+            default:
+                if (0x01U == flash_handle[id].wr_nbyte_cfg.write_count)
+                {
+                    flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                            flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len);
+                    flash_handle[id].wr_nbyte_cfg.dest_tmp_addr += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+                    flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].wr_nbyte_cfg.start_page_inc_data_len;
+                }
+                else if (flash_handle[id].wr_nbyte_cfg.write_count == flash_handle[id].wr_nbyte_cfg.write_num)
+                {
+                    flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                            flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len);
+                }
+                else
+                {
+                    flash_page_write_nbytes(id, flash_handle[id].wr_nbyte_cfg.dest_tmp_addr, &buf[flash_handle[id].wr_nbyte_cfg.src_buf_pos],
+                                            flash_handle[id].config.page_size);
+                    flash_handle[id].wr_nbyte_cfg.dest_tmp_addr += flash_handle[id].config.page_size;
+                    flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].config.page_size;
+                }
+                break;
+            }
+        }
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        flash_handle[id].state = FLASH_STATE_READY;
+    }
+
+    return DRV_OK;
+}
+
+// limitation: start address and len should alagin with page size
+int flash_write(enum FLASH_DEV_T id, uint32_t start_addr, const uint8_t *buf, uint32_t len)
+{
+    if ((start_addr < FLASH_BASE) || (start_addr > FLASH_BASE + FLASH_SIZE - len) || (len == 0))
+    {
+        return DRV_ERROR;
+    }
+
+    if ((start_addr % flash_handle[id].config.page_size) || (len % flash_handle[id].config.page_size))
+    {
+        return DRV_ERROR;
+    }
+
+    int ret = flash_state_update(id, FLASH_STATE_BUSY_WRITE);
+    if (ret != DRV_OK)
+    {
+        return ret;
+    }
+
+    uint32_t offset_addr = start_addr - flash_handle[id].config.start_addr;
+    uint32_t page_start = offset_addr / flash_handle[id].config.page_size;
+    uint32_t page_end = (offset_addr + len) / flash_handle[id].config.page_size;
+
+    // Reset the flash controller to switch to command mode
+    flash_reset_cmd(id);
+
+    if (flash_handle[id].config.dma_en)
+    {
+#if FLASH_DMA_MODE_EN
+        // enable DMA
+        flash_handle[id].base->CTRL |= FLASH_CTRL_DMAEN_MSK;
+
+        flash_handle[id].dma_op.start_addr = start_addr;
+        flash_handle[id].dma_op.buf = buf;
+        flash_handle[id].dma_op.len = len;
+        flash_handle[id].dma_op.count = 0;
+        flash_handle[id].code = FLASH_OP_WRITE;
+
+        // Write enable
+        flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+        // Set address
+        flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+
+        // program page
+        flash_write_cmd(id, FLASH_OPCODE_PROGRAM_PAGE);
+
+        struct DMA_CH_CFG_T flash_wr_dma_cfg = {
+            .fifo_th = DMA_FIFO_TH_4,
+            .src_burst_size = DMA_SRC_BURST_SIZE_256,
+            .src_width = DMA_WIDTH_4B,
+            .dst_width = DMA_WIDTH_4B,
+            .src_addr_ctrl = DMA_ADDR_INC,
+            .dst_addr_ctrl = DMA_ADDR_FIXED,
+            .src_req_sel = DMA_REQ_MEM,
+            .dst_req_sel = DMA_REQ_FLASH,
+        };
+
+        dma_open(flash_handle[id].dma_wr_ch, &flash_wr_dma_cfg);
+        dma_transfer(flash_handle[id].dma_wr_ch, (uint8_t *)flash_handle[id].dma_op.buf, (uint8_t *)&flash_handle[id].base->DATA,
+                     flash_handle[id].config.page_size / 4, flash_dma_callback);
+#endif
+    }
+    else
+    {
+        for (uint32_t i = page_start; i < page_end; i++)
+        {
+            flash_page_write(id, i, buf + (i - page_start) * flash_handle[id].config.page_size);
+        }
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        flash_handle[id].state = FLASH_STATE_READY;
+    }
+    return DRV_OK;
+}
+#else
+__WEAK int flash_erase(enum FLASH_DEV_T id, uint32_t start_addr, uint32_t len)
+{
+    return DRV_ERROR;
+}
+
+__WEAK int flash_write_nbytes(enum FLASH_DEV_T id, uint32_t start_addr, const uint8_t *buf, uint32_t len)
+{
+    return DRV_ERROR;
+}
+#endif
+
+// limitation: for DMA read, the length should be the multiple of 4
+int flash_read(enum FLASH_DEV_T id, uint32_t start_addr, uint8_t *buf, uint32_t len)
+{
+    ASSERT(((flash_handle[id].config.dma_en) ? ((len & 0x3) == 0) : 1), "The DMA read length should be the multiple of 4\r\n");
+
+    if ((start_addr < FLASH_BASE) || (start_addr > FLASH_BASE + FLASH_SIZE - len) || (len == 0))
+    {
+        return DRV_ERROR;
+    }
+
+    int ret = flash_state_update(id, FLASH_STATE_BUSY_READ);
+    if (ret != DRV_OK)
+    {
+        return ret;
+    }
+
+    // Reset the flash controller to switch to command mode
+    flash_reset_cmd(id);
+
+    if (flash_handle[id].config.dma_en)
+    {
+#if FLASH_DMA_MODE_EN
+        // enable DMA
+        flash_handle[id].base->CTRL |= FLASH_CTRL_DMAEN_MSK;
+
+        flash_handle[id].dma_op.start_addr = start_addr;
+        flash_handle[id].dma_op.buf = buf;
+        flash_handle[id].dma_op.len = len;
+        flash_handle[id].dma_op.count = 0;
+        flash_handle[id].code = FLASH_OP_READ;
+
+        // Write enable
+        flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+        // Set address
+        flash_handle[id].base->ADDR = start_addr;
+
+        // read data bytes
+        flash_write_cmd(id, FLASH_OPCODE_READ);
+
+        struct DMA_CH_CFG_T flash_rd_dma_cfg = {
+            .fifo_th = DMA_FIFO_TH_4,
+            .src_burst_size = DMA_SRC_BURST_SIZE_4,
+            .src_width = DMA_WIDTH_4B,
+            .dst_width = DMA_WIDTH_4B,
+            .src_addr_ctrl = DMA_ADDR_FIXED,
+            .dst_addr_ctrl = DMA_ADDR_INC,
+            .src_req_sel = DMA_REQ_FLASH,
+            .dst_req_sel = DMA_REQ_MEM,
+        };
+
+        if (len > flash_handle[id].config.sector_size)
+        {
+            len = flash_handle[id].config.sector_size;
+        }
+        dma_open(flash_handle[id].dma_rd_ch, &flash_rd_dma_cfg);
+        dma_transfer(flash_handle[id].dma_rd_ch, (uint8_t *)&flash_handle[id].base->DATA, buf, len / 4, flash_dma_callback);
+        // update parameters for next transfer
+        flash_handle[id].dma_op.count += len;
+        flash_handle[id].dma_op.buf += len;
+        flash_handle[id].dma_op.start_addr += len;
+#endif
+    }
+    else
+    {
+        uint32_t sector_num = len / flash_handle[id].config.sector_size;
+        uint32_t sector_tail = len % flash_handle[id].config.sector_size;
+        for (uint32_t i = 0; i < sector_num; i++)
+        {
+            // Write enable
+            flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+            // Set address
+            flash_handle[id].base->ADDR = start_addr + i * flash_handle[id].config.sector_size;
+
+            // read data bytes
+            flash_write_cmd(id, FLASH_OPCODE_READ);
+
+            for (uint32_t j = 0; j < flash_handle[id].config.sector_size; j++)
+            {
+                buf[i * flash_handle[id].config.sector_size + j] = flash_handle[id].base->DATA;
+            }
+        }
+        if (sector_tail)
+        {
+            // Write enable
+            flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+            // Set address
+            flash_handle[id].base->ADDR = start_addr + sector_num * flash_handle[id].config.sector_size;
+
+            // read data bytes
+            flash_write_cmd(id, FLASH_OPCODE_READ);
+
+            for (uint32_t j = 0; j < sector_tail; j++)
+            {
+                buf[sector_num * flash_handle[id].config.sector_size + j] = flash_handle[id].base->DATA;
+            }
+        }
+
+        // Reset the flash controller to switch to command mode
+        flash_reset_cmd(id);
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        flash_handle[id].state = FLASH_STATE_READY;
+    }
+
+    return DRV_OK;
+}
+
+#if FLASH_DMA_MODE_EN
+// flash dma write flash operation done goes to here
+static void flash_dma_write_nbytes_callback(void *ch, uint32_t err_code)
+{
+    enum FLASH_DEV_T id = FLASH_ID0;
+    uint8_t ch_num = *(uint8_t *)ch;
+    uint8_t done = 0;
+
+    if (ch_num == flash_handle[id].dma_wr_ch)
+    {
+        if (err_code == DMA_INT_TYPE_DONE)
+        {
+            flash_handle[id].dma_op.buf = flash_handle[id].wr_nbyte_cfg.src_addr + flash_handle[id].wr_nbyte_cfg.src_buf_pos;
+            flash_handle[id].dma_op.start_addr = flash_handle[id].wr_nbyte_cfg.dest_tmp_addr;
+            flash_handle[id].dma_op.count = flash_handle[id].wr_nbyte_cfg.src_buf_pos;
+
+            flash_handle[id].wr_nbyte_cfg.write_count += 0x01U;
+            if (flash_handle[id].dma_op.len <= flash_handle[id].dma_op.count)
+            {
+                done = 1;
+            }
+            else
+            {
+                // wait until the flash writing operation is complete
+                delay_us(350);
+                if ((flash_handle[id].wr_nbyte_cfg.write_count == flash_handle[id].wr_nbyte_cfg.write_num) &&
+                        (flash_handle[id].wr_nbyte_cfg.write_count > 0x01U))
+                {
+                    flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].config.page_size;
+                    // Write enable
+                    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+                    // Set address
+                    flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+                    // program n bytes
+                    flash_write_variable_len_cmd(id, flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len);
+                    dma_transfer(flash_handle[id].dma_wr_ch, (uint8_t *)flash_handle[id].dma_op.buf, (uint8_t *)&flash_handle[id].base->DATA,
+                                 flash_handle[id].wr_nbyte_cfg.end_page_inc_data_len, flash_dma_write_nbytes_callback);
+                }
+                else if ((flash_handle[id].wr_nbyte_cfg.write_count < flash_handle[id].wr_nbyte_cfg.write_num) &&
+                         (flash_handle[id].wr_nbyte_cfg.write_count > 0x01U))
+                {
+                    flash_handle[id].wr_nbyte_cfg.src_buf_pos += flash_handle[id].config.page_size;
+                    flash_handle[id].wr_nbyte_cfg.dest_tmp_addr += flash_handle[id].config.page_size;
+
+                    // Write enable
+                    flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+                    // Set address
+                    flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+                    // program n bytes
+                    flash_write_variable_len_cmd(id, flash_handle[id].config.page_size);
+                    dma_transfer(flash_handle[id].dma_wr_ch, (uint8_t *)flash_handle[id].dma_op.buf, (uint8_t *)&flash_handle[id].base->DATA,
+                                 flash_handle[id].config.page_size, flash_dma_write_nbytes_callback);
+                }
+            }
+        }
+        else
+        {
+            done = 1;
+        }
+    }
+    else
+    {
+        ASSERT(0, "Unexpected dma channel\r\n");
+    }
+
+    if (done)
+    {
+        // disable DMA
+        flash_handle[id].base->CTRL &= ~FLASH_CTRL_DMAEN_MSK;
+
+        flash_handle[id].code = (err_code << 16) | (1 << 8) | flash_handle[id].code;
+        // TODO: need to check operation done
+    }
+}
+
+// flash dma operation done goes to here
+static void flash_dma_callback(void *ch, uint32_t err_code)
+{
+    enum FLASH_DEV_T id = FLASH_ID0;
+    uint8_t ch_num = *(uint8_t *)ch;
+    uint8_t done = 0;
+    if (ch_num == flash_handle[id].dma_rd_ch)
+    {
+        if (err_code == DMA_INT_TYPE_DONE)
+        {
+            if (flash_handle[id].dma_op.len <= flash_handle[id].dma_op.count)
+            {
+                // Reset the flash controller to switch to command mode
+                flash_reset_cmd(id);
+                done = 1;
+            }
+            else
+            {
+                // Write enable
+                flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+                // Set address
+                flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+
+                // read data bytes
+                flash_write_cmd(id, FLASH_OPCODE_READ);
+
+                uint32_t len = flash_handle[id].dma_op.len - flash_handle[id].dma_op.count;
+                if (len > flash_handle[id].config.sector_size)
+                {
+                    len = flash_handle[id].config.sector_size;
+                }
+
+                dma_transfer(flash_handle[id].dma_rd_ch, (uint8_t *)&flash_handle[id].base->DATA, (uint8_t *)flash_handle[id].dma_op.buf, len / 4,
+                             flash_dma_callback);
+                // update parameters for next transfer
+                flash_handle[id].dma_op.count += len;
+                flash_handle[id].dma_op.buf += len;
+                flash_handle[id].dma_op.start_addr += len;
+            }
+        }
+        else
+        {
+            // Reset the flash controller to switch to command mode
+            flash_reset_cmd(id);
+            done = 1;
+        }
+    }
+    else if (ch_num == flash_handle[id].dma_wr_ch)
+    {
+        if (err_code == DMA_INT_TYPE_DONE)
+        {
+            flash_handle[id].dma_op.start_addr += flash_handle[id].config.page_size;
+            flash_handle[id].dma_op.buf += flash_handle[id].config.page_size;
+            flash_handle[id].dma_op.count += flash_handle[id].config.page_size;
+            if (flash_handle[id].dma_op.len <= flash_handle[id].dma_op.count)
+            {
+                done = 1;
+            }
+            else
+            {
+                // Write enable
+                flash_write_cmd(id, FLASH_OPCODE_WRITE_ENABLE);
+
+                // Set address
+                flash_handle[id].base->ADDR = flash_handle[id].dma_op.start_addr;
+
+                // program page
+                flash_write_cmd(id, FLASH_OPCODE_PROGRAM_PAGE);
+
+                dma_transfer(flash_handle[id].dma_wr_ch, (uint8_t *)flash_handle[id].dma_op.buf, (uint8_t *)&flash_handle[id].base->DATA,
+                             flash_handle[id].config.page_size / 4, flash_dma_callback);
+            }
+        }
+        else
+        {
+            done = 1;
+        }
+    }
+    else
+    {
+        ASSERT(0, "Unexpected dma channel\r\n");
+    }
+
+    if (done)
+    {
+        // disable DMA
+        flash_handle[id].base->CTRL &= ~FLASH_CTRL_DMAEN_MSK;
+
+        flash_handle[id].code = (err_code << 16) | (1 << 8) | flash_handle[id].code;
+        // TODO: need to check operation done
+    }
+}
+#endif
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+void FLASH_CTRL_IRQHandler(void)
+{
+#if FLASH_INT_MODE_EN
+    enum FLASH_DEV_T id = FLASH_ID0;
+    // clear interrupt
+    flash_handle[id].base->STATUS |= FLASH_STATUS_INTRQ_MSK;
+
+    if (flash_handle[id].state == FLASH_STATE_BUSY_ERASE)
+    {
+        // disable INT
+        flash_handle[id].base->CTRL &= ~FLASH_CTRL_INTEN_MSK;
+
+        flash_handle[id].code = (2 << 8) | flash_handle[id].code;
+        // TODO: need to check operation done
+    }
+#endif
+}
+
+// for interrupt enable or DMA enable mode to check operation done
+// this function should be excuted in command mode
+bool flash_check_busy(enum FLASH_DEV_T id)
+{
+    uint8_t busy = 0;
+    uint8_t op_code = flash_handle[id].code & 0xff;
+    uint8_t int_flag = (flash_handle[id].code >> 8) & 0xff;
+    uint8_t err_code = (flash_handle[id].code >> 16) & 0xff;
+
+    if ((op_code == FLASH_OP_ERASE) || (op_code == FLASH_OP_WRITE))
+    {
+        if (int_flag == 0)
+        {
+            busy = 1;
+        }
+        else
+        {
+            // WIP
+            busy = flash_read_status(id) & 0x1;
+        }
+    }
+    else if (op_code == FLASH_OP_READ)
+    {
+        if (int_flag == 0)
+        {
+            busy = 1;
+        }
+    }
+    else
+    {
+        return busy;
+    }
+
+    if (busy == 0)
+    {
+        // clear op code
+        flash_handle[id].code = 0;
+
+        // setup memory command
+        flash_write_mem_cmd(id, FLASH_OPCODE_READ);
+
+        // update state
+        if ((err_code == DMA_INT_TYPE_DONE) || (err_code == 0))
+        {
+            flash_handle[id].state = FLASH_STATE_READY;
+        }
+        else
+        {
+            flash_handle[id].state = FLASH_STATE_ERROR;
+        }
+
+        if (flash_handle[id].callback)
+        {
+            flash_handle[id].callback(&id, op_code);
+        }
+    }
+
+    return busy;
+}

--
Gitblit v1.9.3