circuitpython/ports/mimxrt/hal/flexspi_hyper_flash.c
Philipp Ebensberger a3f3b9045c mimxrt/flash: Separate low level driver code from flash object.
Separate low level flash access from mimxrt flash driver object.  Allows
better abstraction from hardware for testing and reuse in other areas (e.g.
bootloader).

Signed-off-by: Philipp Ebensberger <philipp.ebensberger@3bricks-software.de>
2023-05-09 15:18:36 +10:00

254 lines
8.1 KiB
C

/*
* 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;
}