/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_clock.h" #include "flexspi_hyper_flash.h" // Copy of a few (pseudo-)functions from fsl_clock.h, which were nor reliably // inlined when they should be. That caused DEBUG mode to fail. // It does not increase the code size, since they were supposed to be inline. __attribute__((always_inline)) static inline void clock_set_div(clock_div_t divider, uint32_t value) { uint32_t busyShift; busyShift = CCM_TUPLE_BUSY_SHIFT(divider); CCM_TUPLE_REG(CCM, divider) = (CCM_TUPLE_REG(CCM, divider) & (~CCM_TUPLE_MASK(divider))) | (((uint32_t)((value) << CCM_TUPLE_SHIFT(divider))) & CCM_TUPLE_MASK(divider)); /* Clock switch need Handshake? */ if (CCM_NO_BUSY_WAIT != busyShift) { /* Wait until CCM internal handshake finish. */ while (CCM->CDHIPR & (1U << busyShift)) { } } } __attribute__((always_inline)) static inline void clock_control_gate(clock_ip_name_t name, clock_gate_value_t value) { uint32_t index = ((uint32_t)name) >> 8U; uint32_t shift = ((uint32_t)name) & 0x1FU; volatile uint32_t *reg; reg = ((volatile uint32_t *)&CCM->CCGR0) + index; *reg = ((*reg) & ~(3U << shift)) | (((uint32_t)value) << shift); } __attribute__((always_inline)) static inline void clock_enable_clock(clock_ip_name_t name) { clock_control_gate(name, kCLOCK_ClockNeededRunWait); } __attribute__((always_inline)) static inline void clock_disable_clock(clock_ip_name_t name) { clock_control_gate(name, kCLOCK_ClockNotNeeded); } #define DIV_PAGE_PGM 4 #define DIV_ERASE_PGM 4 #define DIV_READ 0 static void SetFlexSPIDiv(uint32_t div) __attribute__((section(".ram_functions"))); static void SetFlexSPIDiv(uint32_t div) { FLEXSPI_Enable(BOARD_FLEX_SPI, false); clock_disable_clock(kCLOCK_FlexSpi); clock_set_div(kCLOCK_FlexspiDiv, div); /* flexspi clock 332M, DDR mode, internal clock 166M. */ clock_enable_clock(kCLOCK_FlexSpi); FLEXSPI_Enable(BOARD_FLEX_SPI, true); } status_t flexspi_nor_hyperbus_read(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) __attribute__((section(".ram_functions"))); status_t flexspi_nor_hyperbus_read(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) { flexspi_transfer_t flashXfer; status_t status; flashXfer.deviceAddress = addr * 2; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Read; flashXfer.SeqNumber = 1; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA; flashXfer.data = buffer; flashXfer.dataSize = bytes; status = FLEXSPI_TransferBlocking(base, &flashXfer); return status; } status_t flexspi_nor_hyperbus_write(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) __attribute__((section(".ram_functions"))); status_t flexspi_nor_hyperbus_write(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) { flexspi_transfer_t flashXfer; status_t status; flashXfer.deviceAddress = addr * 2; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Write; flashXfer.SeqNumber = 1; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA; flashXfer.data = buffer; flashXfer.dataSize = bytes; status = FLEXSPI_TransferBlocking(base, &flashXfer); return status; } status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr) __attribute__((section(".ram_functions"))); status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr) { flexspi_transfer_t flashXfer; status_t status; /* 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(base, &flashXfer); return status; } status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base) { /* Wait status ready. */ bool isBusy; uint32_t readValue; status_t status; flexspi_transfer_t 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(base, &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(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { status_t status; flexspi_transfer_t flashXfer; /* Write enable */ status = flexspi_nor_write_enable(base, 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(base, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy(base); return status; } status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) { return flexspi_nor_flash_erase_sector(base, address); // HyperFlash does not support block erase! } status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) { status_t status; flexspi_transfer_t flashXfer; /* Speed down flexspi clock */ SetFlexSPIDiv(DIV_PAGE_PGM); /* Write enable */ status = flexspi_nor_write_enable(base, address); if (status != kStatus_Success) { return status; } /* Prepare page program command */ flashXfer.deviceAddress = address; flashXfer.port = kFLEXSPI_PortA1; flashXfer.cmdType = kFLEXSPI_Write; flashXfer.SeqNumber = 2; flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM; flashXfer.data = (uint32_t *)src; flashXfer.dataSize = size; status = FLEXSPI_TransferBlocking(base, &flashXfer); if (status != kStatus_Success) { return status; } status = flexspi_nor_wait_bus_busy(base); SetFlexSPIDiv(DIV_READ); return status; } status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base) { /* * Read ID-CFI Parameters */ // CFI Entry status_t status; uint32_t buffer[2]; uint8_t data[4] = {0x00, 0x98}; status = flexspi_nor_hyperbus_write(base, 0x555, (uint32_t *)data, 2); if (status != kStatus_Success) { return status; } // ID-CFI Read // Read Query Unique ASCII String status = flexspi_nor_hyperbus_read(base, 0x10, &buffer[0], sizeof(buffer)); if (status != kStatus_Success) { return status; } buffer[1] &= 0xFFFF; // Check that the data read out is unicode "QRY" in big-endian order if ((buffer[0] != 0x52005100) || (buffer[1] != 0x5900)) { status = kStatus_Fail; return status; } // ASO Exit 0xF000 data[1] = 0xF0; status = flexspi_nor_hyperbus_write(base, 0x0, (uint32_t *)data, 2); if (status != kStatus_Success) { return status; } return status; }