/*---------------------------------------------------------------------------- * Copyright (c) Fenda Technologies Co., Ltd. 2020. All rights reserved. * * Description: flash drv_mx25u51245g.c * * Author: saimen * * Create: 2021-04-27 *--------------------------------------------------------------------------*/ #include #include #include #include "am_mcu_apollo.h" #include "am_bsp.h" #include "am_util_stdio.h" #include "am_util_delay.h" #include "am_util.h" #include "config_hw.h" #include "drv_mspi.h" #include "drv_mx25u51245g.h" #include "user_flash_ate.h" #include "debug_print.h" #define mx25u512_mspi_module 1 //#define ENABLE_MX25U512_LOG #ifdef ENABLE_MX25U512_LOG #define mx25u512_debug_print(...) am_util_stdio_printf(__VA_ARGS__) #else #define mx25u512_debug_print(...) #endif /***************************************************************************************************/ static volatile bool bDMAComplete = true; static uint32_t g_mx25u512_mspi_buff[32] = {0}; static uint32_t g_mx25u512_mspi_dma_buf[256] = {0}; static mx25u512_info_t mx25u512_info = {0}; static const am_hal_mspi_dev_config_t mx25u512_serial_mspi_config = { .eSpiMode = AM_HAL_MSPI_SPI_MODE_0, .eClockFreq = AM_HAL_MSPI_CLK_48MHZ, //AM_HAL_MSPI_CLK_8MHZ .ui8TurnAround = 8, .eAddrCfg = AM_HAL_MSPI_ADDR_4_BYTE, .eInstrCfg = AM_HAL_MSPI_INSTR_1_BYTE, .eDeviceConfig = AM_HAL_MSPI_FLASH_SERIAL_CE0, //.bSeparateIO = true, .bSendInstr = true, .bSendAddr = true, .bTurnaround = true, #if !defined(AM_PART_APOLLO4) .ui32TCBSize = 0, .pTCB = NULL, .scramblingStartAddr = 0, .scramblingEndAddr = 0, #endif }; static const am_hal_mspi_dev_config_t mx25u512_quad_mspi_config = { .eSpiMode = AM_HAL_MSPI_SPI_MODE_0, .eClockFreq = AM_HAL_MSPI_CLK_48MHZ, .ui8TurnAround = 6, .eAddrCfg = AM_HAL_MSPI_ADDR_4_BYTE, .eInstrCfg = AM_HAL_MSPI_INSTR_1_BYTE, .eDeviceConfig = AM_HAL_MSPI_FLASH_QUAD_CE0, //.bSeparateIO = false, .bSendInstr = true, .bSendAddr = true, .bTurnaround = true, #if defined(AM_PART_APOLLO4) .ui16ReadInstr = 0xEC, .ui16WriteInstr = 0x02, #else .ui8ReadInstr = 0xEC, .ui8WriteInstr = 0x02, #endif #if !defined(AM_PART_APOLLO4) .ui32TCBSize = (sizeof(g_mx25u512_mspi_dma_buf) / sizeof(g_mx25u512_mspi_dma_buf[0])), .pTCB = g_mx25u512_mspi_dma_buf, .scramblingStartAddr = 0, .scramblingEndAddr = 0, #endif .ui16DMATimeLimit = 0, .eDMABoundary = AM_HAL_MSPI_BOUNDARY_NONE, }; #if defined(AM_PART_APOLLO4) static const am_hal_mspi_config_t mx25u512_quad_mspi_tcb_config = { .ui32TCBSize = (sizeof(g_mx25u512_mspi_dma_buf) / sizeof(g_mx25u512_mspi_dma_buf[0])), .pTCB = g_mx25u512_mspi_dma_buf, }; #endif void mx25u512_dma_delay(uint32_t count) { #if defined(AM_PART_APOLLO4) am_util_delay_us(count); #else am_hal_flash_delay(FLASH_CYCLES_US(count)); #endif } void mx25u512_read_write_cb(void *cb_context, uint32_t status) { uint8_t* Complete = (uint8_t *)cb_context; if (status == AM_HAL_STATUS_SUCCESS) { *Complete = true; } mx25u512_debug_print("mx25u512 dma cb done %d\r\n", status); if(mx25u512_info.transfer_cb) { mx25u512_info.transfer_cb(cb_context, status); } } uint32_t mx25u512_enter_deep_power_down(void) { // drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; uint32_t status = AM_HAL_STATUS_SUCCESS; status = mspi_write_cmd(mx25u512_mspi_module, ENTER_DEEP_POWER_DOWN_CMD, NULL, 0); // 因影响lcd驱动的mspi功能,暂时屏蔽 //am_hal_mspi_power_control(p_mspi_dev->pHandle, AM_HAL_SYSCTRL_DEEPSLEEP, true); //am_util_delay_ms(10); mx25u512_debug_print("mx25u512 enter deep power down >>>>> \n"); return status; } uint32_t mx25u512_exit_deep_power_down(void) { drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; uint32_t status = AM_HAL_STATUS_SUCCESS; am_hal_mspi_initialize(mx25u512_mspi_module, &p_mspi_dev->pHandle); // 因影响lcd驱动的mspi功能,暂时屏蔽 //am_hal_mspi_power_control(p_mspi_dev->pHandle, AM_HAL_SYSCTRL_WAKE, true); memcpy(&p_mspi_dev->hal_config, &mx25u512_quad_mspi_config, sizeof(am_hal_mspi_dev_config_t)); #if defined(AM_PART_APOLLO4) memcpy(&p_mspi_dev->tcb_config, &mx25u512_quad_mspi_tcb_config, sizeof(am_hal_mspi_config_t)); #endif status = mspi_mode_switch(mx25u512_mspi_module); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } /* NVIC_EnableIRQ(mspi_interrupts[mx25u512_mspi_module]); am_hal_interrupt_master_enable(); */ status = mspi_write_cmd(mx25u512_mspi_module, RELEASE_DEEP_POWER_DOWN_CMD, NULL, 0); am_util_delay_ms(10); mx25u512_debug_print("mx25u512 release from deep power down >>>>> \n"); return status; } uint32_t mx25u512_read_status_reg(uint8_t *pdata) { uint32_t status = mspi_read_cmd(mx25u512_mspi_module, READ_STATUS_REG_CMD, (uint32_t *)pdata, 1); return status; } uint32_t mx25u512_set_4byte_mode(void) { uint32_t status = mspi_write_cmd(mx25u512_mspi_module, ENTER_4_BYTES_CMD, g_mx25u512_mspi_buff, 0); return status; } uint32_t mx25u512_read_chip_id(uint32_t *chip_id) { uint32_t temp_id = 0; uint32_t status = mspi_read_cmd(mx25u512_mspi_module, QREAD_ID_CMD, &temp_id, 3); *chip_id = ((temp_id & 0xff) << 16) | ((temp_id & 0xff00)) | ((temp_id & 0xff0000) >> 16); return status; } uint32_t mx25u512_reset(void) { uint32_t status = AM_HAL_STATUS_SUCCESS; status = mspi_write_cmd(mx25u512_mspi_module, ENABLE_RESET, g_mx25u512_mspi_buff, 0); if(status != AM_HAL_STATUS_SUCCESS) { return AM_HAL_STATUS_FAIL; } status = mspi_write_cmd(mx25u512_mspi_module, RESET_DEV, g_mx25u512_mspi_buff, 0); if(status != AM_HAL_STATUS_SUCCESS) { return AM_HAL_STATUS_FAIL; } return AM_HAL_STATUS_SUCCESS; } uint32_t mx25u512_qspi_mode_init(void) { drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; uint32_t status = AM_HAL_STATUS_SUCCESS; uint32_t reg_05 = 0; uint32_t reg_15 = 0x2740; mspi_write_cmd(mx25u512_mspi_module, WRITE_ENABLE_CMD, ®_05, 0); mspi_write_cmd(mx25u512_mspi_module, WRITE_STATUS_CONF_REG_CMD, ®_15, 2); mspi_write_cmd(mx25u512_mspi_module, WRITE_DISABLE_CMD, ®_05, 0); mspi_write_cmd(mx25u512_mspi_module, ENTER_4_BYTES_CMD, g_mx25u512_mspi_buff, 0); mspi_write_cmd(mx25u512_mspi_module, ENTER_QPI_MODE_CMD, g_mx25u512_mspi_buff, 0); memset(&p_mspi_dev->hal_config, 0x0, sizeof(am_hal_mspi_dev_config_t)); memcpy(&p_mspi_dev->hal_config, &mx25u512_quad_mspi_config, sizeof(am_hal_mspi_dev_config_t)); #if defined(AM_PART_APOLLO4) memcpy(&p_mspi_dev->tcb_config, &mx25u512_quad_mspi_tcb_config, sizeof(am_hal_mspi_config_t)); #endif status = mspi_mode_switch(mx25u512_mspi_module); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } return AM_HAL_STATUS_SUCCESS; } uint32_t mx25u512_bus_init(void) { drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; p_mspi_dev->module = mx25u512_mspi_module; memset(&p_mspi_dev->hal_config, 0x0, sizeof(am_hal_mspi_dev_config_t)); memcpy(&p_mspi_dev->hal_config, &mx25u512_serial_mspi_config, sizeof(am_hal_mspi_dev_config_t)); #if defined(AM_PART_APOLLO4) memcpy(&p_mspi_dev->tcb_config, &mx25u512_quad_mspi_tcb_config, sizeof(am_hal_mspi_config_t)); #endif if (AM_HAL_STATUS_SUCCESS != mspi_open(mx25u512_mspi_module)) { mx25u512_debug_print("Error - mx25u512 mspi_open failed.\n"); return AM_HAL_STATUS_FAIL; } if (AM_HAL_STATUS_SUCCESS != mx25u512_reset()) { mx25u512_debug_print("Error - mx25u512 reset failed.\n"); return AM_HAL_STATUS_FAIL; } //am_util_delay_ms(100); mx25u512_qspi_mode_init(); NVIC_EnableIRQ(mspi_interrupts[mx25u512_mspi_module]); am_hal_interrupt_master_enable(); // // Return the status. // return AM_HAL_STATUS_SUCCESS; } uint32_t mx25u512_buse_deinit(void) { if (AM_HAL_STATUS_SUCCESS != mspi_close(mx25u512_mspi_module)) { mx25u512_debug_print("Error - mx25u512 mspi_close failed.\n"); return AM_HAL_STATUS_FAIL; } return AM_HAL_STATUS_SUCCESS; } uint32_t mx25u512_erase_sector(uint32_t u32SectorAddr) { uint32_t status = AM_HAL_STATUS_SUCCESS; uint32_t reg_status = 0; bool complete = false; mx25u512_debug_print("flash_erase: 0x%x\r\n", u32SectorAddr); mspi_write_cmd(mx25u512_mspi_module, WRITE_ENABLE_CMD, NULL, 0); mspi_write(mx25u512_mspi_module, 0x20, true, u32SectorAddr, false, NULL, 0); for (uint32_t i = MX25U51245G_MSPI_TIMEOUT; i > 0; i--) { mspi_read_cmd(mx25u512_mspi_module, READ_STATUS_REG_CMD, ®_status, 1); //mx25u512_debug_print("erase ---read reg 1 is %x\n", reg_status); complete = reg_status & 0x01 ? false : true; if (complete) { break; } am_util_delay_us(10); } status = mspi_write_cmd(mx25u512_mspi_module, WRITE_DISABLE_CMD, g_mx25u512_mspi_buff, 0); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } return status; } uint32_t mx25u512_dma_read(uint32_t addr, uint8_t *pdata, uint32_t bytes) { uint32_t status = AM_HAL_STATUS_SUCCESS; bDMAComplete = false; mx25u512_debug_print("flash_read: 0x%x 0x%x %d\r\n", addr,pdata,bytes); status = mspi_read_DMA(mx25u512_mspi_module, addr, (uint32_t *)pdata, bytes, mx25u512_read_write_cb, (void *)&bDMAComplete); //mx25u512_debug_print("mx25u512 mspi_read_DMA bDMAComplete: %d, status: %d\n", bDMAComplete, status); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } // Wait for DMA Complete or Timeout for (uint32_t i = MX25U51245G_MSPI_TIMEOUT; i > 0; i--) //for (uint32_t i = (MX25U51245G_MSPI_TIMEOUT/10); i > 0; i--) { if (bDMAComplete) { break; } // // Call the BOOTROM cycle function to delay for about 1 microsecond. // //am_util_delay_us(10); mx25u512_dma_delay(1); } // Check the status. if (!bDMAComplete) { return AM_HAL_STATUS_FAIL; } return AM_HAL_STATUS_SUCCESS; } //与mx25u512_dma_read相比,不带延迟等待 uint32_t mx25u512_dma_read_ex(uint32_t addr, uint8_t *pdata, uint32_t bytes) { uint32_t status = AM_HAL_STATUS_SUCCESS; bDMAComplete = false; mx25u512_debug_print("flash_read: 0x%x 0x%x %d\r\n", addr,pdata,bytes); status = mspi_read_DMA(mx25u512_mspi_module, addr, (uint32_t *)pdata, bytes, mx25u512_read_write_cb, (void *)&bDMAComplete); //mx25u512_debug_print("mx25u512 mspi_read_DMA bDMAComplete: %d, status: %d\n", bDMAComplete, status); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } return AM_HAL_STATUS_SUCCESS; } uint32_t mx25u512_dma_write(uint32_t addr, uint8_t *pdata, uint32_t bytes) { uint32_t status = AM_HAL_STATUS_SUCCESS; uint32_t curre_write_bytes = 0; uint32_t remaind_bytes = 0; while (bytes > 0) { status = mspi_write_cmd(mx25u512_mspi_module, WRITE_ENABLE_CMD, NULL, 0); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } remaind_bytes = addr % MX25U51245G_PAGE_SIZE; if (remaind_bytes != 0) { remaind_bytes = MX25U51245G_PAGE_SIZE - remaind_bytes; if (bytes >= remaind_bytes) { curre_write_bytes = remaind_bytes; } else { curre_write_bytes = bytes; } } else { if (bytes >= MX25U51245G_PAGE_SIZE) { curre_write_bytes = MX25U51245G_PAGE_SIZE; } else { curre_write_bytes = bytes; } } bDMAComplete = false; status = mspi_write_DMA(mx25u512_mspi_module, addr, (uint32_t *)pdata, curre_write_bytes, mx25u512_read_write_cb, (void *)&bDMAComplete); //mx25u512_debug_print("mx25u512 mspi_write_DMA bDMAComplete: %d, status: %d\n", bDMAComplete, status); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } addr += curre_write_bytes; pdata += curre_write_bytes; bytes -= curre_write_bytes; // Wait for DMA Complete or Timeout for (uint32_t i = MX25U51245G_MSPI_TIMEOUT; i > 0; i--) { if (bDMAComplete) { break; } // // Call the BOOTROM cycle function to delay for about 1 microsecond. // mx25u512_dma_delay(1); } // Check the status. if (!bDMAComplete) { // // Send the command sequence to disable writing. // return AM_HAL_STATUS_FAIL; } else { mx25u512_wait_ready(0, MX25U51245G_MSPI_TIMEOUT); // // Send the command sequence to disable writing. // status = mspi_write_cmd(mx25u512_mspi_module, WRITE_DISABLE_CMD, g_mx25u512_mspi_buff, 0); if (AM_HAL_STATUS_SUCCESS != status) { return AM_HAL_STATUS_FAIL; } } } //mx25u512_debug_print("write ------------------------ status is %d\n", status); return status; } void mx25u512_wait_ready(uint32_t delay_cs,uint32_t time_out) { bool complete = false; uint32_t reg_status = 0; uint32_t i = 0, time_step = 10; if(delay_cs) { time_step = 1000; } i = time_out/time_step; for (; i > 0; i--) { mspi_read_cmd(mx25u512_mspi_module, READ_STATUS_REG_CMD, ®_status, 1); //mx25u512_debug_print("write ---read reg1 is %x\n", reg_status); complete = reg_status & 0x01 ? false : true; if (complete) { break; } if(delay_cs) { mx25u512_info.delay_ms(1); } else { am_util_delay_us(10); } } } //read write config void drv_mx25u512_open(void) { mx25u512_info.lock(); ext_flash_power_on(); am_util_delay_ms(10); mx25u512_bus_init(); mx25u512_info.status = true; mx25u512_info.unlock(); } void drv_mx25u512_close(void) { mx25u512_info.lock(); mx25u512_buse_deinit(); ext_flash_power_off(); mx25u512_info.status = false; mx25u512_info.unlock(); } void drv_mx25u512_info_init(void* delay_ms,void* lock,void* unlock,void* transfer_cb) { mx25u512_info.transfer_cb = transfer_cb; mx25u512_info.lock = lock; mx25u512_info.unlock = unlock; mx25u512_info.delay_ms = delay_ms; } void drv_mx25u512_enter_power_down(void) { mx25u512_info.lock(); mx25u512_enter_deep_power_down(); mx25u512_info.unlock(); } void drv_mx25u512_release_power_down(void) { mx25u512_info.lock(); mx25u512_exit_deep_power_down(); mx25u512_info.unlock(); } uint32_t drv_mx25u512_read_id(void) { uint32_t temp_id = 0; mx25u512_info.lock(); mx25u512_read_chip_id(&temp_id); mx25u512_info.unlock(); return temp_id; } uint32_t drv_mx25u512_read_status(void) { return mx25u512_info.status; } extern uint32_t user_mspi_status_get(uint32_t module); //read,write,erase flash uint32_t drv_mx25u512_chip_erase(void) { uint32_t status; mx25u512_info.lock(); while(user_mspi_status_get(mx25u512_mspi_module)) { mx25u512_info.delay_ms(1); } status = mspi_write_cmd(mx25u512_mspi_module, WRITE_ENABLE_CMD, NULL, 0); if(status == AM_HAL_STATUS_SUCCESS) { status = mspi_write(mx25u512_mspi_module, CHIP_ERASE_CMD, false, NULL, false, NULL, 0); } mx25u512_wait_ready(1,MX25U51245G_MSPI_CHIP_ERASE_TIMEOUT); if(status == AM_HAL_STATUS_SUCCESS) { status = mspi_write_cmd(mx25u512_mspi_module, WRITE_DISABLE_CMD, g_mx25u512_mspi_buff, 0); } mx25u512_info.unlock(); return status; } uint32_t drv_mx25u512_sector_erase(uint32_t addr) { uint32_t status; mx25u512_info.lock(); while(user_mspi_status_get(mx25u512_mspi_module)) { mx25u512_info.delay_ms(1); } status = mx25u512_erase_sector(addr); mx25u512_info.unlock(); return status; } uint32_t drv_mx25u512_smart_erase(uint32_t begin_addr,uint32_t end_addr) { uint32_t status; uint32_t index_sector,begin_sector,end_sector; if(begin_sector>end_sector || begin_addr>=MX25U51245G_TOTAL_SIZE || end_addr>=MX25U51245G_TOTAL_SIZE) { return AM_HAL_STATUS_FAIL; } mx25u512_info.lock(); while(user_mspi_status_get(mx25u512_mspi_module)) { mx25u512_info.delay_ms(1); } status = mspi_write_cmd(mx25u512_mspi_module, WRITE_ENABLE_CMD, NULL, 0); index_sector = begin_sector; while(end_sector>=index_sector) { if(index_sector%16==0) { if(end_sector-index_sector>=16) { status = mspi_write(mx25u512_mspi_module, BLOCK_ERASE_CMD, true, (index_sector<<12), false, NULL, 0); mx25u512_wait_ready(1,MX25U51245G_MSPI_TIMEOUT); index_sector += 16; } else { status = mspi_write(mx25u512_mspi_module, SECTOR_ERASE_CMD, true, (index_sector<<12), false, NULL, 0); mx25u512_wait_ready(1,MX25U51245G_MSPI_TIMEOUT); index_sector++; } } else { status = mspi_write(mx25u512_mspi_module, SECTOR_ERASE_CMD, true, (index_sector<<12), false, NULL, 0); mx25u512_wait_ready(1,MX25U51245G_MSPI_TIMEOUT); index_sector++; } } status = mspi_write_cmd(mx25u512_mspi_module, WRITE_DISABLE_CMD, g_mx25u512_mspi_buff, 0); mx25u512_info.unlock(); return status; } uint32_t drv_mx25u512_read_nonblocking(uint8_t *pdata,uint32_t addr,uint32_t readlen) { uint32_t status; mx25u512_info.lock(); while(user_mspi_status_get(mx25u512_mspi_module)) { mx25u512_info.delay_ms(1); } status = mx25u512_dma_read(addr, pdata, readlen); mx25u512_info.unlock(); return status; } //与drv_mx25u512_read_nonblocking相比,不带延迟等待 uint32_t drv_mx25u512_read_nonblocking_ex(uint8_t *pdata,uint32_t addr,uint32_t readlen) { uint32_t status; mx25u512_info.lock(); #if 0 status = mx25u512_dma_read_ex(addr, pdata, readlen); #else bDMAComplete = false; mx25u512_debug_print("flash_read: 0x%x 0x%x %d\r\n", addr, pdata, readlen); #if 0 status = mspi_read_DMA(mx25u512_mspi_module, addr, (uint32_t *)pdata, readlen, mx25u512_read_write_cb, (void *)&bDMAComplete); #else drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; am_hal_mspi_dma_transfer_t Transaction = {0}; // // Create the transaction. // Transaction.eDirection = AM_HAL_MSPI_RX; Transaction.ui32DeviceAddress = addr; // Clear the CQ stimulus. // Transaction.ui32PauseCondition = 0; Transaction.ui32SRAMAddress = (uint32_t)pdata; // Clear the post-processing // Transaction.ui32StatusSetClr = 0; Transaction.ui32TransferCount = readlen; Transaction.ui8Priority = 1; status = am_hal_mspi_nonblocking_transfer(p_mspi_dev->pHandle, &Transaction, AM_HAL_MSPI_TRANS_DMA, mx25u512_read_write_cb, (void *)&bDMAComplete); #endif #endif mx25u512_info.unlock(); return status; } uint32_t drv_mx25u512_write_nonblocking(uint8_t *pdata,uint32_t addr,uint32_t writelen) { uint32_t status; mx25u512_info.lock(); while(user_mspi_status_get(mx25u512_mspi_module)) { mx25u512_info.delay_ms(1); } status = mx25u512_dma_write(addr, pdata, writelen); mx25u512_info.unlock(); return status; } void drv_mx25u512_maped(void) { drv_mspi_t *p_mspi_dev = &g_mspi_dev[mx25u512_mspi_module]; uint32_t status; status = am_hal_mspi_control(p_mspi_dev->pHandle, AM_HAL_MSPI_REQ_XIP_EN, NULL); if(AM_HAL_STATUS_SUCCESS != status) { mx25u512_debug_print("Failed to put the MSPI mx25u512 into XIP mode! ret:%d\r\n", ui32Status); return; } }