Newer
Older
mbed-os / targets / TARGET_NXP / TARGET_MCUXpresso_MCUS / TARGET_MIMXRT105x / TARGET_EVK / TARGET_1050_EVK / flash_api.c
/* 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