对比新文件 |
| | |
| | | /* |
| | | * 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; |
| | | } |