/* mbed Microcontroller Library * Copyright (c) 2019 ARM Limited * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "flash_api.h" #include "mbed_toolchain.h" #include "mbed_critical.h" #if DEVICE_FLASH #include "fsl_flexspi.h" #include "fsl_cache.h" #include "flash_defines.h" AT_QUICKACCESS_SECTION_CODE(void flexspi_update_lut_ram(void)); AT_QUICKACCESS_SECTION_CODE(status_t flexspi_nor_write_enable_ram(uint32_t baseAddr)); AT_QUICKACCESS_SECTION_CODE(status_t flexspi_nor_wait_bus_busy_ram(void)); AT_QUICKACCESS_SECTION_CODE(status_t flexspi_nor_flash_erase_sector_ram(uint32_t address)); AT_QUICKACCESS_SECTION_CODE(status_t flexspi_nor_flash_page_program_ram(uint32_t address, const uint32_t *src, uint32_t size)); AT_QUICKACCESS_SECTION_CODE(void flexspi_nor_flash_read_data_ram(uint32_t addr, uint32_t *buffer, uint32_t size)); AT_QUICKACCESS_SECTION_CODE(void *flexspi_memset(void *buf, int c, size_t n)); /** * @brief Set bytes in memory. If put this code in SRAM, Make sure this code * does not call functions in Flash. * * @return pointer to start of buffer */ void *flexspi_memset(void *buf, int c, size_t n) { /* do byte-sized initialization until word-aligned or finished */ unsigned char *d_byte = (unsigned char *)buf; unsigned char c_byte = (unsigned char)c; while (((unsigned int)d_byte) & 0x3) { if (n == 0) { return buf; } *(d_byte++) = c_byte; n--; }; /* do word-sized initialization as long as possible */ unsigned int *d_word = (unsigned int *)d_byte; unsigned int c_word = (unsigned int)(unsigned char)c; c_word |= c_word << 8; c_word |= c_word << 16; while (n >= sizeof(unsigned int)) { *(d_word++) = c_word; n -= sizeof(unsigned int); } /* do byte-sized initialization until finished */ d_byte = (unsigned char *)d_word; while (n > 0) { *(d_byte++) = c_byte; n--; } return buf; } #ifdef HYPERFLASH_BOOT AT_QUICKACCESS_SECTION_CODE(void flexspi_lower_clock_ram(void)); AT_QUICKACCESS_SECTION_CODE(void flexspi_clock_update_ram(void)); void flexspi_update_lut_ram(void) { flexspi_config_t config; flexspi_memset(&config, 0, sizeof(config)); /*Get FLEXSPI default settings and configure the flexspi. */ FLEXSPI_GetDefaultConfig(&config); /*Set AHB buffer size for reading data through AHB bus. */ config.ahbConfig.enableAHBPrefetch = true; /*Allow AHB read start address do not follow the alignment requirement. */ config.ahbConfig.enableReadAddressOpt = true; config.ahbConfig.enableAHBBufferable = true; config.ahbConfig.enableAHBCachable = true; /* enable diff clock and DQS */ config.enableSckBDiffOpt = true; config.rxSampleClock = kFLEXSPI_ReadSampleClkExternalInputFromDqsPad; config.enableCombination = true; FLEXSPI_Init(FLEXSPI, &config); /* Configure flash settings according to serial flash feature. */ FLEXSPI_SetFlashConfig(FLEXSPI, &deviceconfig, kFLEXSPI_PortA1); /* Update LUT table. */ FLEXSPI_UpdateLUT(FLEXSPI, 0, customLUT, CUSTOM_LUT_LENGTH); FLEXSPI_SoftwareReset(FLEXSPI); /* Wait for bus idle. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } } status_t flexspi_nor_write_enable_ram(uint32_t baseAddr) { flexspi_transfer_t flashXfer; status_t status = kStatus_Success; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); /* Write enable */ flashXfer.deviceAddress = baseAddr; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Command; flashXfer.SeqNumber = 2; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); return status; } status_t flexspi_nor_wait_bus_busy_ram(void) { /* Wait status ready. */ bool isBusy = false; uint32_t readValue = 0; status_t status = kStatus_Success; flexspi_transfer_t flashXfer; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); flashXfer.deviceAddress = 0; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Read; flashXfer.SeqNumber = 2; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS; flashXfer.data = &readValue; flashXfer.dataSize = 2; do { status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } if (readValue & 0x8000) { isBusy = false; } else { isBusy = true; } if (readValue & 0x3200) { status = kStatus_Fail; break; } } while (isBusy); return status; } status_t flexspi_nor_flash_erase_sector_ram(uint32_t address) { status_t status = kStatus_Success; flexspi_transfer_t flashXfer; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); /* Write enable */ status = flexspi_nor_write_enable_ram(address); if (status != kStatus_Success) { return status; } flashXfer.deviceAddress = address; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Command; flashXfer.SeqNumber = 4; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy_ram(); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); return status; } void flexspi_lower_clock_ram(void) { unsigned int reg = 0; /* Wait for bus idle. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } FLEXSPI_Enable(FLEXSPI, false); /* Disable FlexSPI clock */ CCM->CCGR6 &= ~CCM_CCGR6_CG5_MASK; /* flexspi clock 66M, DDR mode, internal clock 33M. */ reg = CCM->CSCMR1; reg &= ~CCM_CSCMR1_FLEXSPI_PODF_MASK; reg |= CCM_CSCMR1_FLEXSPI_PODF(3); CCM->CSCMR1 = reg; /* Enable FlexSPI clock */ CCM->CCGR6 |= CCM_CCGR6_CG5_MASK; FLEXSPI_Enable(FLEXSPI, true); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); /* Wait for bus idle. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } } void flexspi_clock_update_ram(void) { /* Program finished, speed the clock to 133M. */ /* Wait for bus idle before change flash configuration. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } FLEXSPI_Enable(FLEXSPI, false); /* Disable FlexSPI clock */ CCM->CCGR6 &= ~CCM_CCGR6_CG5_MASK; /* flexspi clock 260M, DDR mode, internal clock 130M. */ CCM->CSCMR1 &= ~CCM_CSCMR1_FLEXSPI_PODF_MASK; /* Enable FlexSPI clock */ CCM->CCGR6 |= CCM_CCGR6_CG5_MASK; FLEXSPI_Enable(FLEXSPI, true); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); /* Wait for bus idle. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } } status_t flexspi_nor_flash_page_program_ram(uint32_t address, const uint32_t *src, uint32_t size) { status_t status = kStatus_Success; flexspi_transfer_t flashXfer; uint32_t offset = 0; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); flexspi_lower_clock_ram(); while (size > 0) { /* Write enable */ status = flexspi_nor_write_enable_ram(address + offset); if (status != kStatus_Success) { return status; } /* Prepare page program command */ flashXfer.deviceAddress = address + offset; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Write; flashXfer.SeqNumber = 2; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM; flashXfer.data = (uint32_t *)((uint32_t)src + offset); flashXfer.dataSize = BOARD_FLASH_PAGE_SIZE; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy_ram(); if (status != kStatus_Success) { return status; } size -= BOARD_FLASH_PAGE_SIZE; offset += BOARD_FLASH_PAGE_SIZE; } flexspi_clock_update_ram(); return status; } #else AT_QUICKACCESS_SECTION_CODE(status_t flexspi_nor_enable_quad_mode_ram(void)); status_t flexspi_nor_enable_quad_mode_ram(void) { flexspi_transfer_t flashXfer; uint32_t writeValue = FLASH_QUAD_ENABLE; status_t status = kStatus_Success; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); /* Write enable */ status = flexspi_nor_write_enable_ram(0); if (status != kStatus_Success) { return status; } /* Enable quad mode. */ flashXfer.deviceAddress = 0; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Write; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG; flashXfer.data = &writeValue; flashXfer.dataSize = 1; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy_ram(); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); return status; } void flexspi_update_lut_ram(void) { flexspi_config_t config; flexspi_memset(&config, 0, sizeof(config)); /*Get FLEXSPI default settings and configure the flexspi. */ FLEXSPI_GetDefaultConfig(&config); /*Set AHB buffer size for reading data through AHB bus. */ config.ahbConfig.enableAHBPrefetch = true; config.ahbConfig.enableAHBBufferable = true; config.ahbConfig.enableReadAddressOpt = true; config.ahbConfig.enableAHBCachable = true; config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad; FLEXSPI_Init(FLEXSPI, &config); /* Configure flash settings according to serial flash feature. */ FLEXSPI_SetFlashConfig(FLEXSPI, &deviceconfig, kFLEXSPI_PortA1); /* Update LUT table. */ FLEXSPI_UpdateLUT(FLEXSPI, 0, customLUT, CUSTOM_LUT_LENGTH); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); /* Wait for bus idle. */ while (!FLEXSPI_GetBusIdleStatus(FLEXSPI)) { } flexspi_nor_enable_quad_mode_ram(); } status_t flexspi_nor_write_enable_ram(uint32_t baseAddr) { flexspi_transfer_t flashXfer; status_t status = kStatus_Success; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); /* Write enable */ flashXfer.deviceAddress = baseAddr; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Command; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); return status; } status_t flexspi_nor_wait_bus_busy_ram(void) { /* Wait status ready. */ bool isBusy; uint32_t readValue; status_t status = kStatus_Success; flexspi_transfer_t flashXfer; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); flashXfer.deviceAddress = 0; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Read; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READSTATUSREG; flashXfer.data = &readValue; flashXfer.dataSize = 1; do { status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } if (FLASH_BUSY_STATUS_POL) { if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET)) { isBusy = true; } else { isBusy = false; } } else { if (readValue & (1U << FLASH_BUSY_STATUS_OFFSET)) { isBusy = false; } else { isBusy = true; } } } while (isBusy); return status; } status_t flexspi_nor_flash_erase_sector_ram(uint32_t address) { flexspi_transfer_t flashXfer; status_t status = kStatus_Success; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); /* Write enable */ flashXfer.deviceAddress = address; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Command; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_WRITEENABLE; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } flashXfer.deviceAddress = address; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Command; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASESECTOR; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy_ram(); /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); return status; } status_t flexspi_nor_flash_page_program_ram(uint32_t address, const uint32_t *src, uint32_t size) { flexspi_transfer_t flashXfer; status_t status = kStatus_Success; uint32_t offset = 0; flexspi_memset(&flashXfer, 0, sizeof(flashXfer)); while (size > 0) { /* Write enable */ status = flexspi_nor_write_enable_ram(address + offset); if (status != kStatus_Success) { return status; } /* Prepare page program command */ flashXfer.deviceAddress = address + offset; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Write; flashXfer.SeqNumber = 1; flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD; flashXfer.data = (uint32_t *)((uint32_t)src + offset); flashXfer.dataSize = BOARD_FLASH_PAGE_SIZE; status = FLEXSPI_TransferBlocking(FLEXSPI, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy_ram(); if (status != kStatus_Success) { return status; } size -= BOARD_FLASH_PAGE_SIZE; offset += BOARD_FLASH_PAGE_SIZE; } /* Do software reset. */ FLEXSPI_SoftwareReset(FLEXSPI); return status; } #endif void flexspi_nor_flash_read_data_ram(uint32_t addr, uint32_t *buffer, uint32_t size) { memcpy(buffer, (void *)addr, size); } int32_t flash_init(flash_t *obj) { core_util_critical_section_enter(); flexspi_update_lut_ram(); core_util_critical_section_exit(); return 0; } int32_t flash_erase_sector(flash_t *obj, uint32_t address) { status_t status = kStatus_Success; int32_t ret = 0; core_util_critical_section_enter(); status = flexspi_nor_flash_erase_sector_ram(address - FlexSPI_AMBA_BASE); if (status != kStatus_Success) { ret = -1; } else { DCACHE_InvalidateByRange(address, BOARD_FLASH_SECTOR_SIZE); } core_util_critical_section_exit(); return ret; } int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size) { status_t status = kStatus_Success; int32_t ret = 0; core_util_critical_section_enter(); status = flexspi_nor_flash_page_program_ram(address - FlexSPI_AMBA_BASE, (uint32_t *)data, size); if (status != kStatus_Success) { ret = -1; } else { DCACHE_InvalidateByRange(address, size); } core_util_critical_section_exit(); return ret; } int32_t flash_read(flash_t *obj, uint32_t address, uint8_t *data, uint32_t size) { flexspi_nor_flash_read_data_ram(address, (uint32_t *)data, size); return 0; } int32_t flash_free(flash_t *obj) { return 0; } uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address) { uint32_t sectorsize = MBED_FLASH_INVALID_SIZE; uint32_t devicesize = BOARD_FLASHIAP_SIZE; uint32_t startaddr = BOARD_FLASHIAP_START_ADDR; if ((address >= startaddr) && (address < (startaddr + devicesize))) { sectorsize = BOARD_FLASH_SECTOR_SIZE; } return sectorsize; } uint32_t flash_get_page_size(const flash_t *obj) { return BOARD_FLASH_PAGE_SIZE; } uint32_t flash_get_start_address(const flash_t *obj) { return BOARD_FLASHIAP_START_ADDR; } uint32_t flash_get_size(const flash_t *obj) { return BOARD_FLASHIAP_SIZE; } uint8_t flash_get_erase_value(const flash_t *obj) { (void)obj; return 0xFF; } #endif //DEVICE_FLASH