From e862d9437b059e27c2a8d5b7bb210b1fc3a040bf Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Wed, 27 Mar 2024 14:27:35 +0100 Subject: [PATCH] add stm32 low level drivers --- .../lx_stm32_nand_simulator_driver.c | 355 +++++++ .../lx_stm32_nor_simulator_driver.c | 125 +++ common/stm32_drivers/lx_stm32_ospi_driver.c | 245 +++++ common/stm32_drivers/lx_stm32_qspi_driver.c | 244 +++++ .../template/lx_stm32_nand_driver.c | 230 +++++ .../template/lx_stm32_nand_driver.h | 60 ++ .../template/lx_stm32_nand_simulator_driver.h | 73 ++ .../template/lx_stm32_nor_driver.c | 151 +++ .../template/lx_stm32_nor_driver.h | 60 ++ .../template/lx_stm32_nor_simulator_driver.h | 60 ++ .../template/lx_stm32_ospi_driver.h | 129 +++ .../template/lx_stm32_ospi_driver_glue.c | 906 ++++++++++++++++++ .../template/lx_stm32_qspi_driver.h | 138 +++ .../template/lx_stm32_qspi_driver_glue.c | 155 +++ 14 files changed, 2931 insertions(+) create mode 100644 common/stm32_drivers/lx_stm32_nand_simulator_driver.c create mode 100644 common/stm32_drivers/lx_stm32_nor_simulator_driver.c create mode 100644 common/stm32_drivers/lx_stm32_ospi_driver.c create mode 100644 common/stm32_drivers/lx_stm32_qspi_driver.c create mode 100644 common/stm32_drivers/template/lx_stm32_nand_driver.c create mode 100644 common/stm32_drivers/template/lx_stm32_nand_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_nand_simulator_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_nor_driver.c create mode 100644 common/stm32_drivers/template/lx_stm32_nor_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_nor_simulator_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_ospi_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_ospi_driver_glue.c create mode 100644 common/stm32_drivers/template/lx_stm32_qspi_driver.h create mode 100644 common/stm32_drivers/template/lx_stm32_qspi_driver_glue.c diff --git a/common/stm32_drivers/lx_stm32_nand_simulator_driver.c b/common/stm32_drivers/lx_stm32_nand_simulator_driver.c new file mode 100644 index 0000000..5480ccc --- /dev/null +++ b/common/stm32_drivers/lx_stm32_nand_simulator_driver.c @@ -0,0 +1,355 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* Include necessary files. */ + +#include "lx_stm32_nand_simulator_driver.h" + +/* Define constants for the NAND flash simulation. */ + + +/* Definition of the spare area is relative to the page size of the NAND part and perhaps manufactures of the NAND part. + Here are some common definitions: + + 256 Byte page + + Bytes Meaning + + 0,1,2 ECC bytes + 3,4,6,7 Extra + 5 Bad block flag + + 512 Byte page + + Bytes Meaning + + 0,1,2,3,6,7 ECC bytes + 8-15 Extra + 5 Bad block flag + + 2048 Byte page + + Bytes Meaning + + 0 Bad block flag + 2-39 Extra + 40-63 ECC bytes +*/ + +typedef struct PHYSICAL_PAGE_STRUCT +{ + unsigned long memory[WORDS_PER_PHYSICAL_PAGE]; + unsigned char spare[SPARE_BYTES_PER_PAGE]; +} PHYSICAL_PAGE; + +typedef struct NAND_BLOCK_DIAG_STRUCT +{ + unsigned long erases; + unsigned long page_writes[PHYSICAL_PAGES_PER_BLOCK]; + unsigned long max_page_writes[PHYSICAL_PAGES_PER_BLOCK]; +} NAND_BLOCK_DIAG; + + + +typedef struct NAND_FLASH_BLOCK_STRUCT +{ + PHYSICAL_PAGE physical_pages[PHYSICAL_PAGES_PER_BLOCK]; +} NAND_FLASH_BLOCK; + +NAND_FLASH_BLOCK nand_memory_area[TOTAL_BLOCKS]; + +NAND_BLOCK_DIAG nand_block_diag[TOTAL_BLOCKS]; + +/* Define NAND flash buffer for LevelX. */ + +ULONG nand_flash_simulator_buffer[WORDS_PER_PHYSICAL_PAGE]; +ULONG *nand_flash_memory; + +UINT lx_nand_simulator_initialize(LX_NAND_FLASH *nand_flash); +UINT lx_nand_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words); +UINT lx_nand_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words); +UINT lx_nand_simulator_block_erase(ULONG block, ULONG erase_count); +UINT lx_nand_simulator_block_erased_verify(ULONG block); +UINT lx_nand_simulator_page_erased_verify(ULONG block, ULONG page); +UINT lx_nand_simulator_erase_all(VOID); +UINT lx_nand_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte); +UINT lx_nand_simulator_block_status_set(ULONG block, UCHAR bad_block_byte); +UINT lx_nand_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size); +UINT lx_nand_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size); +UINT lx_nand_simulator_system_error(UINT error_code, ULONG block, ULONG page); + + +UINT lx_stm32_nand_simulator_initialize(LX_NAND_FLASH *nand_flash) +{ + + /* Setup the buffer pointer. */ + nand_flash_memory = (ULONG *) &nand_memory_area[0]; + + /* Setup geometry of the NAND flash. */ + nand_flash -> lx_nand_flash_total_blocks = TOTAL_BLOCKS; + nand_flash -> lx_nand_flash_pages_per_block = PHYSICAL_PAGES_PER_BLOCK; + nand_flash -> lx_nand_flash_bytes_per_page = BYTES_PER_PHYSICAL_PAGE; + + /* Setup function pointers for the NAND flash services. */ + nand_flash -> lx_nand_flash_driver_read = lx_nand_simulator_read; + nand_flash -> lx_nand_flash_driver_write = lx_nand_simulator_write; + nand_flash -> lx_nand_flash_driver_block_erase = lx_nand_simulator_block_erase; + nand_flash -> lx_nand_flash_driver_block_erased_verify = lx_nand_simulator_block_erased_verify; + nand_flash -> lx_nand_flash_driver_page_erased_verify = lx_nand_simulator_page_erased_verify; + nand_flash -> lx_nand_flash_driver_block_status_get = lx_nand_simulator_block_status_get; + nand_flash -> lx_nand_flash_driver_block_status_set = lx_nand_simulator_block_status_set; + nand_flash -> lx_nand_flash_driver_extra_bytes_get = lx_nand_simulator_extra_bytes_get; + nand_flash -> lx_nand_flash_driver_extra_bytes_set = lx_nand_simulator_extra_bytes_set; + nand_flash -> lx_nand_flash_driver_system_error = lx_nand_simulator_system_error; + + /* Setup local buffer for NAND flash operation. This buffer must be the page size of the NAND flash memory. */ + nand_flash -> lx_nand_flash_page_buffer = &nand_flash_simulator_buffer[0]; + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words) +{ + +ULONG *flash_address; + + /* Pickup the flash address. */ + flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]); + + /* Loop to read flash. */ + while (words--) + { + /* Copy word. */ + *destination++ = *flash_address++; + } + + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words) +{ + +ULONG *flash_address; + + /* Increment the diag info. */ + nand_block_diag[block].page_writes[page]++; + if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page]) + nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page]; + + /* Pickup the flash address. */ + flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]); + + /* Loop to write flash. */ + while (words--) + { + + /* Can the word be written? We can clear new bits, but just can't unclear + in a NAND device. */ + if ((*source & *flash_address) != *source) + return(LX_INVALID_WRITE); + + /* Copy word. */ + *flash_address++ = *source++; + } + + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_block_erase(ULONG block, ULONG erase_count) +{ + +ULONG *pointer; +ULONG words; +UINT i; + + LX_PARAMETER_NOT_USED(erase_count); + + /* Increment the diag info. */ + nand_block_diag[block].erases++; + for (i = 0; i < PHYSICAL_PAGES_PER_BLOCK;i++) + nand_block_diag[block].page_writes[i] = 0; + + /* Setup pointer. */ + pointer = (ULONG *) &nand_memory_area[block]; + + /* Loop to erase block. */ + words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_erase_all(VOID) +{ + +ULONG *pointer; +ULONG words; +UINT i, j; + + /* Increment the diag info. */ + for (i = 0; i < TOTAL_BLOCKS; i++) + { + nand_block_diag[i].erases = 0; + for (j = 0; j < PHYSICAL_PAGES_PER_BLOCK;j++) + nand_block_diag[i].page_writes[j] = 0; + } + + /* Setup pointer. */ + pointer = (ULONG *) &nand_memory_area[0]; + + /* Loop to erase block. */ + words = (ULONG)(sizeof(nand_memory_area))/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_block_erased_verify(ULONG block) +{ + +ULONG *word_ptr; +ULONG words; + + /* Determine if the block is completely erased. */ + + /* Pickup the pointer to the first word of the block. */ + word_ptr = (ULONG *) &nand_memory_area[block]; + + /* Calculate the number of words in a block. */ + words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG); + + /* Loop to check if the block is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_page_erased_verify(ULONG block, ULONG page) +{ + +ULONG *word_ptr; +ULONG words; + + /* Determine if the block is completely erased. */ + + /* Pickup the pointer to the first word of the block's page. */ + word_ptr = (ULONG *) &nand_memory_area[block].physical_pages[page]; + + /* Calculate the number of words in a block. */ + words = WORDS_PER_PHYSICAL_PAGE; + + /* Loop to check if the page is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte) +{ + + /* Pickup the bad block byte and return it. */ + *bad_block_byte = nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION]; + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_block_status_set(ULONG block, UCHAR bad_block_byte) +{ + + /* Set the bad block byte. */ + nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION] = bad_block_byte; + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT lx_nand_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size) +{ + +UCHAR *source; + + /* Setup source pointer in the spare area. */ + source = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]); + + /* Loop to return the extra bytes requested. */ + while (size--) + { + + /* Retrieve an extra byte from the spare area. */ + *destination++ = *source++; + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size) +{ + +UCHAR *destination; + + /* Increment the diag info. */ + nand_block_diag[block].page_writes[page]++; + if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page]) + nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page]; + + /* Setup destination pointer in the spare area. */ + destination = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]); + + /* Loop to set the extra bytes. */ + while (size--) + { + + /* Set an extra byte in the spare area. */ + *destination++ = *source++; + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT lx_nand_simulator_system_error(UINT error_code, ULONG block, ULONG page) +{ + LX_PARAMETER_NOT_USED(error_code); + LX_PARAMETER_NOT_USED(block); + LX_PARAMETER_NOT_USED(page); + + /* Custom processing goes here... all errors except for LX_NAND_ERROR_CORRECTED are fatal. */ + return(LX_ERROR); +} + + diff --git a/common/stm32_drivers/lx_stm32_nor_simulator_driver.c b/common/stm32_drivers/lx_stm32_nor_simulator_driver.c new file mode 100644 index 0000000..53edfa8 --- /dev/null +++ b/common/stm32_drivers/lx_stm32_nor_simulator_driver.c @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* Include necessary files. */ +#include "lx_stm32_nor_simulator_driver.h" + +static UINT lx_nor_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words); +static UINT lx_nor_simulator_write(ULONG *flash_address, ULONG *source, ULONG words); + +static UINT lx_nor_simulator_block_erase(ULONG block, ULONG erase_count); +static UINT lx_nor_simulator_block_erased_verify(ULONG block); + +#ifndef LX_DIRECT_READ +static ULONG nor_sector_memory[LX_NOR_SIMULATOR_SECTOR_SIZE]; +#endif + +static ULONG words_per_block = (LX_NOR_SIMULATOR_SECTOR_SIZE * LX_NOR_SIMULATOR_SECTORS_PER_BLOCK) / sizeof(ULONG); + +static UINT is_erased = 0; + +static void mem_set(void* s, int c, size_t sz) +{ + UCHAR *p = (UCHAR*)s; + UCHAR x = c & 0xff; + + while (sz--) + *p++ = x; +} + +UINT lx_stm32_nor_simulator_initialize(LX_NOR_FLASH *nor_flash) +{ + /* Setup the base address of the flash memory. */ + nor_flash->lx_nor_flash_base_address = (ULONG *) LX_NOR_SIMULATOR_FLASH_BASE_ADDRESS; + + if (is_erased == 0) + { + mem_set(nor_flash->lx_nor_flash_base_address, 0xff, LX_NOR_SIMULATOR_FLASH_SIZE); + is_erased = 1; + } + /* Setup geometry of the flash. */ + nor_flash->lx_nor_flash_total_blocks = (LX_NOR_SIMULATOR_FLASH_SIZE / (LX_NOR_SIMULATOR_SECTOR_SIZE * LX_NOR_SIMULATOR_SECTORS_PER_BLOCK)); + nor_flash->lx_nor_flash_words_per_block = words_per_block; + + /* Setup function pointers for the NOR flash services. */ + nor_flash->lx_nor_flash_driver_read = lx_nor_simulator_read; + nor_flash->lx_nor_flash_driver_write = lx_nor_simulator_write; + + nor_flash->lx_nor_flash_driver_block_erase = lx_nor_simulator_block_erase; + nor_flash->lx_nor_flash_driver_block_erased_verify = lx_nor_simulator_block_erased_verify; + +#ifndef LX_DIRECT_READ + /* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */ + nor_flash->lx_nor_flash_sector_buffer = &nor_sector_memory[0]; +#endif + /* Return success. */ + return(LX_SUCCESS); +} + + +static UINT lx_nor_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words) +{ + + memcpy((VOID *)destination, (VOID *)flash_address, words * sizeof(ULONG)); + + return(LX_SUCCESS); +} + + +static UINT lx_nor_simulator_write(ULONG *flash_address, ULONG *source, ULONG words) +{ + + memcpy((VOID *)flash_address, (VOID *)source, words * sizeof(ULONG)); + + return(LX_SUCCESS); +} + +static UINT lx_nor_simulator_block_erase(ULONG block, ULONG erase_count) +{ + + ULONG *pointer; + + LX_PARAMETER_NOT_USED(erase_count); + + /* Setup pointer. */ + + pointer = (ULONG *) (LX_NOR_SIMULATOR_FLASH_BASE_ADDRESS + block * (LX_NOR_SIMULATOR_SECTOR_SIZE * LX_NOR_SIMULATOR_SECTORS_PER_BLOCK)); + + /* Loop to erase block. */ + + mem_set((VOID *) pointer, 0xff, words_per_block * sizeof(ULONG)); + return(LX_SUCCESS); +} + +static UINT lx_nor_simulator_block_erased_verify(ULONG block) +{ + ULONG *word_ptr; + ULONG words; + + /* Determine if the block is completely erased. */ + + word_ptr = (ULONG *) (LX_NOR_SIMULATOR_FLASH_BASE_ADDRESS + block * (LX_NOR_SIMULATOR_SECTOR_SIZE * LX_NOR_SIMULATOR_SECTORS_PER_BLOCK)); + + /* Calculate the number of words in a block. */ + words = words_per_block; + + /* Loop to check if the block is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + diff --git a/common/stm32_drivers/lx_stm32_ospi_driver.c b/common/stm32_drivers/lx_stm32_ospi_driver.c new file mode 100644 index 0000000..d4d887c --- /dev/null +++ b/common/stm32_drivers/lx_stm32_ospi_driver.c @@ -0,0 +1,245 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "lx_stm32_ospi_driver.h" + +static UINT lx_ospi_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words); +static UINT lx_ospi_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words); + +static UINT lx_ospi_driver_erase_block(ULONG block, ULONG erase_count); +static UINT lx_ospi_driver_block_erased_verify(ULONG block); + +#ifndef LX_DIRECT_READ +extern ULONG ospi_sector_buffer[LX_STM32_OSPI_SECTOR_SIZE/sizeof(ULONG)]; +#endif + +static UINT is_initialized = LX_FALSE; + +/** +* @brief check the status of the octospi memory. +* @param none +* @retval LX_SUCCESS if the octospi is ready, LX_ERROR otherwise +*/ + +static UINT check_status(void) +{ + ULONG start = LX_STM32_OSPI_CURRENT_TIME(); + while (LX_STM32_OSPI_CURRENT_TIME() - start < LX_STM32_OSPI_DEFAULT_TIMEOUT) + { + if (lx_stm32_ospi_get_status(LX_STM32_OSPI_INSTANCE) == 0) + { + return LX_SUCCESS; + } + } + + return LX_ERROR; +} + +/** +* @brief initialize the OctoSPI memory +* @param LX_NOR_FLASH * the levelx NOR flash main instance. +* @retval LX_SUCCESS if the octospi is ready, LX_ERROR otherwise +*/ + +UINT lx_stm32_ospi_initialize(LX_NOR_FLASH *nor_flash) +{ + INT ret; + ULONG block_size; + ULONG total_blocks; + + if (is_initialized == LX_FALSE) + { + + ret = lx_stm32_ospi_lowlevel_init(LX_STM32_OSPI_INSTANCE); + + if (ret != 0) + { + return LX_ERROR; + } + +#if (LX_STM32_OSPI_ERASE == 1) + + ret = lx_stm32_ospi_erase(LX_STM32_OSPI_INSTANCE, (ULONG)0, (ULONG)0, 1); + + if (ret != 0) + { + return LX_ERROR; + } +#endif + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + ret = lx_stm32_ospi_get_info(LX_STM32_OSPI_INSTANCE, &block_size, &total_blocks); + + if (ret != 0) + { + return LX_ERROR; + } + + /* Setup the base address of the flash memory. */ + nor_flash->lx_nor_flash_base_address = 0; + + /* Setup geometry of the flash. */ + nor_flash->lx_nor_flash_total_blocks = total_blocks; + nor_flash->lx_nor_flash_words_per_block = block_size / sizeof(ULONG); + + nor_flash->lx_nor_flash_driver_read = lx_ospi_driver_read_sector; + nor_flash->lx_nor_flash_driver_write = lx_ospi_driver_write_sector; + + nor_flash->lx_nor_flash_driver_block_erase = lx_ospi_driver_erase_block; + nor_flash->lx_nor_flash_driver_block_erased_verify = lx_ospi_driver_block_erased_verify; + + nor_flash->lx_nor_flash_driver_system_error = lx_ospi_driver_system_error; + +#ifndef LX_DIRECT_READ + /* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */ + nor_flash->lx_nor_flash_sector_buffer = &ospi_sector_buffer[0]; +#endif + is_initialized = LX_TRUE; + } + + /* call post init routine*/ + LX_STM32_OSPI_POST_INIT(); + + /* Return success. */ + return LX_SUCCESS; +} + +/** +* @brief read data from flash memory address into a destination buffer +* @param ULONG* flash_address the flash address to read from +* @param ULONG* destination the buffer to hold the read data +* @param ULONG words the number of words to read +* @retval LX_SUCCESS if data is read correctly, LX_ERROR on errors +*/ + + +static UINT lx_ospi_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words) +{ + UINT status = LX_SUCCESS; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + LX_STM32_OSPI_PRE_READ_TRANSFER(status); + + if (status != LX_SUCCESS) + { + return status; + } + + if (lx_stm32_ospi_read(LX_STM32_OSPI_INSTANCE, flash_address, destination, words) != 0) + { + status = LX_ERROR; + LX_STM32_OSPI_READ_TRANSFER_ERROR(status); + } + else + { + LX_STM32_OSPI_READ_CPLT_NOTIFY(status); + } + + LX_STM32_OSPI_POST_READ_TRANSFER(status); + + return status; +} + +/** +* @brief write source buffer into flash memory address +* @param ULONG* flash_address the flash address to write into +* @param ULONG* source the data buffer to be written +* @param ULONG words the number of words to write +* @retval LX_SUCCESS if data is written correctly, LX_ERROR on errors +*/ + +static UINT lx_ospi_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words) +{ + UINT status = LX_SUCCESS; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + LX_STM32_OSPI_PRE_WRITE_TRANSFER(status); + + if (status != LX_SUCCESS) + { + return status; + } + + if (lx_stm32_ospi_write(LX_STM32_OSPI_INSTANCE, flash_address, source, words) != 0) + { + status = LX_ERROR; + LX_STM32_OSPI_WRITE_TRANSFER_ERROR(status); + } + else + { + LX_STM32_OSPI_WRITE_CPLT_NOTIFY(status); + } + + LX_STM32_OSPI_POST_WRITE_TRANSFER(status); + + return status; +} + +static UINT lx_ospi_driver_erase_block(ULONG block, ULONG erase_count) +{ + UINT status; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + if (lx_stm32_ospi_erase(LX_STM32_OSPI_INSTANCE, block, erase_count, 0) != 0) + { + status = LX_ERROR; + } + else + { + status = check_status(); + } + + return status; +} + +static UINT lx_ospi_driver_block_erased_verify(ULONG block) +{ + UINT status; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + if (lx_stm32_ospi_is_block_erased(LX_STM32_OSPI_INSTANCE, block) == 0) + { + status = LX_SUCCESS; + } + else + { + status = LX_ERROR; + } + + return status; +} + +__WEAK UINT lx_ospi_driver_system_error(UINT error_code) +{ + LX_PARAMETER_NOT_USED(error_code); + + return LX_ERROR; +} + diff --git a/common/stm32_drivers/lx_stm32_qspi_driver.c b/common/stm32_drivers/lx_stm32_qspi_driver.c new file mode 100644 index 0000000..931c9d3 --- /dev/null +++ b/common/stm32_drivers/lx_stm32_qspi_driver.c @@ -0,0 +1,244 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "lx_stm32_qspi_driver.h" + +static UINT lx_qspi_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words); +static UINT lx_qspi_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words); + +static UINT lx_qspi_driver_erase_block(ULONG block, ULONG erase_count); +static UINT lx_qspi_driver_block_erased_verify(ULONG block); + +#ifndef LX_DIRECT_READ +extern ULONG qspi_sector_buffer[LX_STM32_QSPI_SECTOR_SIZE/sizeof(ULONG)]; +#endif + +static UINT is_initialized = LX_FALSE; + +/** +* @brief check the status of the octospi memory. +* @param none +* @retval LX_SUCCESS if the octospi is ready, LX_ERROR otherwise +*/ + +static UINT check_status(void) +{ + ULONG start = LX_STM32_QSPI_CURRENT_TIME(); + while (LX_STM32_QSPI_CURRENT_TIME() - start < LX_STM32_QSPI_DEFAULT_TIMEOUT) + { + if (lx_stm32_qspi_get_status(LX_STM32_QSPI_INSTANCE) == 0) + { + return LX_SUCCESS; + } + } + + return LX_ERROR; +} + +/** +* @brief initialize the OctoSPI memory +* @param LX_NOR_FLASH * the levelx NOR flash main instance. +* @retval LX_SUCCESS if the octospi is ready, LX_ERROR otherwise +*/ + +UINT lx_stm32_qspi_initialize(LX_NOR_FLASH *nor_flash) +{ + INT ret; + ULONG block_size; + ULONG total_blocks; + + if (is_initialized == LX_FALSE) + { + + ret = lx_stm32_qspi_lowlevel_init(LX_STM32_QSPI_INSTANCE); + + if (ret != 0) + { + return LX_ERROR; + } + +#if (LX_STM32_QSPI_ERASE == 1) + + ret = lx_stm32_qspi_erase(LX_STM32_QSPI_INSTANCE, (ULONG)0, (ULONG)0, 1); + + if (ret != 0) + { + return LX_ERROR; + } +#endif + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + ret = lx_stm32_qspi_get_info(LX_STM32_QSPI_INSTANCE, &block_size, &total_blocks); + + if (ret != 0) + { + return LX_ERROR; + } + + /* Setup the base address of the flash memory. */ + nor_flash->lx_nor_flash_base_address = 0; + + /* Setup geometry of the flash. */ + nor_flash->lx_nor_flash_total_blocks = total_blocks; + nor_flash->lx_nor_flash_words_per_block = block_size / sizeof(ULONG); + + nor_flash->lx_nor_flash_driver_read = lx_qspi_driver_read_sector; + nor_flash->lx_nor_flash_driver_write = lx_qspi_driver_write_sector; + + nor_flash->lx_nor_flash_driver_block_erase = lx_qspi_driver_erase_block; + nor_flash->lx_nor_flash_driver_block_erased_verify = lx_qspi_driver_block_erased_verify; + + nor_flash->lx_nor_flash_driver_system_error = lx_qspi_driver_system_error; + +#ifndef LX_DIRECT_READ + /* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */ + nor_flash->lx_nor_flash_sector_buffer = &qspi_sector_buffer[0]; +#endif + is_initialized = LX_TRUE; + } + + /* call post init routine*/ + LX_STM32_QSPI_POST_INIT(); + + /* Return success. */ + return LX_SUCCESS; +} + +/** +* @brief read data from flash memory address into a destination buffer +* @param ULONG* flash_address the flash address to read from +* @param ULONG* destination the buffer to hold the read data +* @param ULONG words the number of words to read +* @retval LX_SUCCESS if data is read correctly, LX_ERROR on errors +*/ + + +static UINT lx_qspi_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words) +{ + UINT status = LX_SUCCESS; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + LX_STM32_QSPI_PRE_READ_TRANSFER(status); + + if (status != LX_SUCCESS) + { + return status; + } + + if (lx_stm32_qspi_read(LX_STM32_QSPI_INSTANCE, flash_address, destination, words) != 0) + { + status = LX_ERROR; + LX_STM32_QSPI_READ_TRANSFER_ERROR(status); + } + else + { + LX_STM32_QSPI_READ_CPLT_NOTIFY(status); + } + + LX_STM32_QSPI_POST_READ_TRANSFER(status); + + return status; +} + +/** +* @brief write source buffer into flash memory address +* @param ULONG* flash_address the flash address to write into +* @param ULONG* source the data buffer to be written +* @param ULONG words the number of words to write +* @retval LX_SUCCESS if data is written correctly, LX_ERROR on errors +*/ + +static UINT lx_qspi_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words) +{ + UINT status = LX_SUCCESS; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + LX_STM32_QSPI_PRE_WRITE_TRANSFER(status); + + if (status != LX_SUCCESS) + { + return status; + } + + if (lx_stm32_qspi_write(LX_STM32_QSPI_INSTANCE, flash_address, source, words) != 0) + { + status = LX_ERROR; + LX_STM32_QSPI_WRITE_TRANSFER_ERROR(status); + } + else + { + LX_STM32_QSPI_WRITE_CPLT_NOTIFY(status); + } + + LX_STM32_QSPI_POST_WRITE_TRANSFER(status); + + return status; +} + +static UINT lx_qspi_driver_erase_block(ULONG block, ULONG erase_count) +{ + UINT status; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + if (lx_stm32_qspi_erase(LX_STM32_QSPI_INSTANCE, block, erase_count, 0) != 0) + { + status = LX_ERROR; + } + else + { + status = check_status(); + } + + return status; +} + +static UINT lx_qspi_driver_block_erased_verify(ULONG block) +{ + UINT status; + + if (check_status() != LX_SUCCESS) + { + return LX_ERROR; + } + + if (lx_stm32_qspi_is_block_erased(LX_STM32_QSPI_INSTANCE, block) == 0) + { + status = LX_SUCCESS; + } + else + { + status = LX_ERROR; + } + + return status; +} + +__WEAK UINT lx_qspi_driver_system_error(UINT error_code) +{ + LX_PARAMETER_NOT_USED(error_code); + + return LX_ERROR; +} diff --git a/common/stm32_drivers/template/lx_stm32_nand_driver.c b/common/stm32_drivers/template/lx_stm32_nand_driver.c new file mode 100644 index 0000000..cc7ffb1 --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nand_driver.c @@ -0,0 +1,230 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#include "lx_stm32_nand_custom_driver.h" +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ + + + + +static UINT lx_nand_driver_read(ULONG block, ULONG page, ULONG *destination, ULONG words); +static UINT lx_nand_driver_write(ULONG block, ULONG page, ULONG *source, ULONG words); + +static UINT lx_nand_driver_block_erase(ULONG block, ULONG erase_count); +static UINT lx_nand_driver_block_erased_verify(ULONG block); +static UINT lx_nand_driver_page_erased_verify(ULONG block, ULONG page); + +static UINT lx_nand_driver_block_status_get(ULONG block, UCHAR *bad_block_byte); +static UINT lx_nand_driver_block_status_set(ULONG block, UCHAR bad_block_byte); + +static UINT lx_nand_driver_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size); +static UINT lx_nand_driver_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size); + +static UINT lx_nand_driver_system_error(UINT error_code, ULONG block, ULONG page); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +#ifndef WORDS_PER_PHYSICAL_PAGE +#define WORDS_PER_PHYSICAL_PAGE 512 +#endif + +ULONG nand_flash_buffer[WORDS_PER_PHYSICAL_PAGE]; + +UINT lx_stm32_nand_custom_driver_initialize(LX_NAND_FLASH *nand_flash) +{ + UINT ret = LX_SUCCESS; + + ULONG total_blocks = 0; + ULONG pages_per_block = 0; + ULONG bytes_per_page = 0; + + /* USER CODE BEGIN Init_Section_0 */ + + /*USER CODE END Init_Section_0 */ + + nand_flash->lx_nand_flash_total_blocks = total_blocks; + nand_flash->lx_nand_flash_pages_per_block = pages_per_block; + nand_flash->lx_nand_flash_bytes_per_page = bytes_per_page; + + /* USER CODE BEGIN Init_Section_1 */ + + /*USER CODE END Init_Section_1 */ + + nand_flash->lx_nand_flash_driver_read = lx_nand_driver_read; + nand_flash->lx_nand_flash_driver_write = lx_nand_driver_write; + + nand_flash->lx_nand_flash_driver_block_erase = lx_nand_driver_block_erase; + nand_flash->lx_nand_flash_driver_block_erased_verify = lx_nand_driver_block_erased_verify; + nand_flash->lx_nand_flash_driver_page_erased_verify = lx_nand_driver_page_erased_verify; + + nand_flash->lx_nand_flash_driver_block_status_get = lx_nand_driver_block_status_get; + nand_flash->lx_nand_flash_driver_block_status_set = lx_nand_driver_block_status_set; + + nand_flash->lx_nand_flash_driver_extra_bytes_get = lx_nand_driver_extra_bytes_get; + nand_flash->lx_nand_flash_driver_extra_bytes_set = lx_nand_driver_extra_bytes_set; + + nand_flash->lx_nand_flash_driver_system_error = lx_nand_driver_system_error; + + + /* USER CODE BEGIN Init_Section_2 */ + + /*USER CODE END Init_Section_2 */ + + nand_flash->lx_nand_flash_page_buffer = &nand_flash_buffer[0]; + + /* USER CODE BEGIN Init_Section_3 */ + + /*USER CODE END Init_Section_3 */ + return ret; + +} + +static UINT lx_nand_driver_read(ULONG block, ULONG page, ULONG *destination, ULONG words) +{ + + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN driver_read */ + + /* USER CODE END driver_read */ + + return ret; +} + +static UINT lx_nand_driver_write(ULONG block, ULONG page, ULONG *source, ULONG words) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN driver_write */ + + /* USER CODE END driver_write */ + + return ret; +} + +static UINT lx_nand_driver_block_erase(ULONG block, ULONG erase_count) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN block_erase */ + + /* USER CODE END block_erase */ + + return ret; +} + +static UINT lx_nand_driver_block_erased_verify(ULONG block) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN block_erase_verify */ + + /* USER CODE END block_erase_verify */ + + return ret; +} + +static UINT lx_nand_driver_page_erased_verify(ULONG block, ULONG page) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN page_erased_verify */ + + /* USER CODE END page_erased_verify */ + + return ret; +} + +static UINT lx_nand_driver_block_status_get(ULONG block, UCHAR *bad_block_byte) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN block_status_get */ + + /* USER CODE END block_status_get*/ + + return ret; +} + +static UINT lx_nand_driver_block_status_set(ULONG block, UCHAR bad_block_byte) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN block_status_set */ + + /* USER CODE END block_status_set */ + + return ret; +} + +static UINT lx_nand_driver_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN extra_bytes_get */ + + /* USER CODE END extra_bytes_get */ + + return ret; +} + +static UINT lx_nand_driver_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN extra_bytes_set */ + + /* USER CODE END extra_bytes_set */ + + return ret; +} + +static UINT lx_nand_driver_system_error(UINT error_code, ULONG block, ULONG page) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN system_error */ + + /* USER CODE END system_error */ + + return ret; +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/common/stm32_drivers/template/lx_stm32_nand_driver.h b/common/stm32_drivers/template/lx_stm32_nand_driver.h new file mode 100644 index 0000000..ba16d82 --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nand_driver.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_NAND_DRIVER_H +#define LX_STM32_NAND_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ + +UINT lx_stm32_nand_custom_driver_initialize(LX_NAND_FLASH *nand_flash); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_NOR_DRIVER_H */ + diff --git a/common/stm32_drivers/template/lx_stm32_nand_simulator_driver.h b/common/stm32_drivers/template/lx_stm32_nand_simulator_driver.h new file mode 100644 index 0000000..6303dab --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nand_simulator_driver.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_NAND_SIMULATOR_DRIVER_H +#define LX_STM32_NAND_SIMULATOR_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" +#include "stm32XXX_hal.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ + +#define TOTAL_BLOCKS 8 + +#define PHYSICAL_PAGES_PER_BLOCK 16 /* Min value of 2 */ +#define BYTES_PER_PHYSICAL_PAGE 2048 /* 2048 bytes per page */ + +#define WORDS_PER_PHYSICAL_PAGE 2048/4 /* Words per page */ +#define SPARE_BYTES_PER_PAGE 64 /* 64 "spare" bytes per page */ + +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ + +/* Define the function prototypes of the LevelX driver entry function. */ + +UINT lx_stm32_nand_simulator_initialize(LX_NAND_FLASH *nor_flash); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_NAND_SIMULATOR_DRIVER_H */ + diff --git a/common/stm32_drivers/template/lx_stm32_nor_driver.c b/common/stm32_drivers/template/lx_stm32_nor_driver.c new file mode 100644 index 0000000..08d5e5d --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nor_driver.c @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "lx_stm32_nor_custom_driver.h" + +/* Private includes ----------------------------------------------------------*/ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN PV */ +/* USER CODE END PV */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN PFP */ + +/* USER CODE END PFP */ + +static UINT lx_nor_driver_read(ULONG *flash_address, ULONG *destination, ULONG words); +static UINT lx_nor_driver_write(ULONG *flash_address, ULONG *source, ULONG words); + +static UINT lx_nor_driver_block_erase(ULONG block, ULONG erase_count); +static UINT lx_nor_driver_block_erased_verify(ULONG block); + +/* USER CODE BEGIN USER_CODE_SECTION_1 */ + +/* USER CODE END USER_CODE_SECTION_1 */ + +#ifndef LX_DIRECT_READ + +#ifndef NOR_SECTOR_BUFFER_SIZE +#define NOR_SECTOR_BUFFER_SIZE 512 +#endif + +static ULONG nor_sector_memory[NOR_SECTOR_BUFFER_SIZE]; +#endif + +UINT lx_stm32_nor_custom_driver_initialize(LX_NOR_FLASH *nor_flash) +{ + UINT ret = LX_SUCCESS; + + ULONG total_blocks = 0; + ULONG words_per_block = 0; + + /* USER CODE BEGIN Init_Section_0 */ + + /* USER CODE END Init_Section_0 */ + + nor_flash->lx_nor_flash_total_blocks = total_blocks; + nor_flash->lx_nor_flash_words_per_block = words_per_block; + + + /* USER CODE BEGIN Init_Section_1 */ + + /* USER CODE END Init_Section_1 */ + + + nor_flash->lx_nor_flash_driver_read = lx_nor_driver_read; + nor_flash->lx_nor_flash_driver_write = lx_nor_driver_write; + + nor_flash->lx_nor_flash_driver_block_erase = lx_nor_driver_block_erase; + nor_flash->lx_nor_flash_driver_block_erased_verify = lx_nor_driver_block_erased_verify; + +#ifndef LX_DIRECT_READ + nor_flash->lx_nor_flash_sector_buffer = nor_sector_memory; +#endif + + /* USER CODE BEGIN Init_Section_2 */ + + /* USER CODE END Init_Section_2 */ + + return ret; +} + + +/* USER CODE BEGIN USER_CODE_SECTION_2 */ + +/* USER CODE END USER_CODE_SECTION_2 */ + +static UINT lx_nor_driver_read(ULONG *flash_address, ULONG *destination, ULONG words) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN NOR_READ */ + + /* USER CODE END NOR_READ */ + + return ret; +} + +static UINT lx_nor_driver_write(ULONG *flash_address, ULONG *source, ULONG words) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN NOR_WRITE */ + + /* USER CODE END NOR_WRITE */ + + return ret; +} + +static UINT lx_nor_driver_block_erase(ULONG block, ULONG erase_count) +{ + + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN NOR_WRITE */ + + /* USER CODE END NOR_WRITE */ + + return ret; +} + +static UINT lx_nor_driver_block_erased_verify(ULONG block) +{ + UINT ret = LX_SUCCESS; + + /* USER CODE BEGIN NOR_WRITE */ + + /* USER CODE END NOR_WRITE */ + + return ret; +} + +/* USER CODE BEGIN USER_CODE_SECTION_3 */ + +/* USER CODE END USER_CODE_SECTION_3 */ diff --git a/common/stm32_drivers/template/lx_stm32_nor_driver.h b/common/stm32_drivers/template/lx_stm32_nor_driver.h new file mode 100644 index 0000000..690350a --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nor_driver.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_NOR_DRIVER_H +#define LX_STM32_NOR_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ + +UINT lx_stm32_nor_custom_driver_initialize(LX_NOR_FLASH *nor_flash); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_NOR_DRIVER_H */ + diff --git a/common/stm32_drivers/template/lx_stm32_nor_simulator_driver.h b/common/stm32_drivers/template/lx_stm32_nor_simulator_driver.h new file mode 100644 index 0000000..984da19 --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_nor_simulator_driver.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_NOR_SIMULATOR_DRIVER_H +#define LX_STM32_NOR_SIMULATOR_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" +#include "stm32xxxx.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* define the NOR Flash base address */ +#define LX_NOR_SIMULATOR_FLASH_BASE_ADDRESS D1_AXISRAM_BASE + +/* define the size of the NOR flash*/ +#define LX_NOR_SIMULATOR_FLASH_SIZE (1024 * 256) + +/* define the size of the NOR SECTOR size */ +#define LX_NOR_SIMULATOR_SECTOR_SIZE 512 + +/* define the number of sectors per block */ +#define LX_NOR_SIMULATOR_SECTORS_PER_BLOCK 16 + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ + +UINT lx_stm32_nor_simulator_initialize(LX_NOR_FLASH *nor_flash); + +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_NOR_SIMULATOR_DRIVER_H */ + diff --git a/common/stm32_drivers/template/lx_stm32_ospi_driver.h b/common/stm32_drivers/template/lx_stm32_ospi_driver.h new file mode 100644 index 0000000..077462b --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_ospi_driver.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_OSPI_DRIVER_H +#define LX_STM32_OSPI_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" +#include "stm32NNxx_hal.h" +/* #include "mx25lm51245g.h" */ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ + +/* the OctoSPI instance, default value set to 0 */ +#define LX_STM32_OSPI_INSTANCE 0 +#define LX_STM32_OSPI_DEFAULT_TIMEOUT 10 * TX_TIMER_TICKS_PER_SECOND +#define LX_STM32_DEFAULT_SECTOR_SIZE LX_STM32_OSPI_SECTOR_SIZE + +/* when set to 1 LevelX is initializing the OctoSPI memory, + * otherwise it is the up to the application to perform it. + */ +#define LX_STM32_OSPI_INIT 1 + +#if (LX_STM32_OSPI_INIT == 1) + +/* allow the driver to fully erase the OctoSPI chip. This should be used carefully. + * the call is blocking and takes a while. by default it is set to 0. + */ +#define LX_STM32_OSPI_ERASE 0 +#endif + +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +#define LX_STM32_OSPI_CURRENT_TIME tx_time_get + +#define LX_STM32_OSPI_POST_INIT() + +#define LX_STM32_OSPI_PRE_READ_TRANSFER(__status__) + +#define LX_STM32_OSPI_READ_CPLT_NOTIFY(__status__) + +#define LX_STM32_OSPI_POST_READ_TRANSFER(__status__) + +#define LX_STM32_OSPI_READ_TRANSFER_ERROR(__status__) + +#define LX_STM32_OSPI_PRE_WRITE_TRANSFER(__status__) + +#define LX_STM32_OSPI_WRITE_CPLT_NOTIFY(__status__) + +#define LX_STM32_OSPI_POST_WRITE_TRANSFER(__status__) + +#define LX_STM32_OSPI_WRITE_TRANSFER_ERROR(__status__) + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +INT lx_stm32_ospi_lowlevel_init(UINT instance); +INT lx_stm32_ospi_lowlevel_deinit(UINT instance); + +INT lx_stm32_ospi_get_status(UINT instance); +INT lx_stm32_ospi_get_info(UINT instance, ULONG *block_size, ULONG *total_blocks); + +INT lx_stm32_ospi_read(UINT instance, ULONG *address, ULONG *buffer, ULONG words); +INT lx_stm32_ospi_write(UINT instance, ULONG *address, ULONG *buffer, ULONG words); + +INT lx_stm32_ospi_erase(UINT instance, ULONG block, ULONG erase_count, UINT full_chip_erase); +INT lx_stm32_ospi_is_block_erased(UINT instance, ULONG block); + +UINT lx_ospi_driver_system_error(UINT error_code); + +UINT lx_stm32_ospi_initialize(LX_NOR_FLASH *nor_flash); +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* The following defines should be set according to the OctoSPI component used */ +#define LX_STM32_OSPI_SECTOR_SIZE MX25LM51245G_BLOCK_SIZE +#define LX_STM32_OSPI_FLASH_SIZE MX25LM51245G_FLASH_SIZE +#define LX_STM32_OSPI_PAGE_SIZE MX25LM51245G_PAGE_SIZE +#define LX_STM32_OSPI_BULK_ERASE_MAX_TIME MX25LM51245G_CHIP_ERASE_MAX_TIME +#define LX_STM32_OSPI_SECTOR_ERASE_MAX_TIME MX25LM51245G_SECTOR_ERASE_MAX_TIME +#define LX_STM32_OSPI_WRITE_REG_MAX_TIME MX25LM51245G_WRITE_REG_MAX_TIME +#define LX_STM32_OSPI_DUMMY_CYCLES_READ_OCTAL MX25LM51245G_DUMMY_CYCLES_READ_OCTAL_66M +#define LX_STM32_OSPI_DUMMY_CYCLES_CR_CFG MX25LM51245G_CR2_DC_66M +#define LX_STM32_OSPI_CR2_REG3_ADDR MX25LM51245G_CR2_REG3_ADDR +#define LX_STM32_QSPI_CR2_REG1_ADDR MX25LM51245G_CR2_REG1_ADDR +#define LX_STM32_OSPI_SR_WEL MX25LM51245G_SR_WEL +#define LX_STM32_OSPI_SR_WIP MX25LM51245G_SR_WIP +#define LX_STM32_OSPI_CR2_SOPI MX25LM51245G_CR2_SOPI +#define LX_STM32_OSPI_CR2_DOPI MX25LM51245G_CR2_DOPI + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_OSPI_DRIVER_H */ diff --git a/common/stm32_drivers/template/lx_stm32_ospi_driver_glue.c b/common/stm32_drivers/template/lx_stm32_ospi_driver_glue.c new file mode 100644 index 0000000..e8e6126 --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_ospi_driver_glue.c @@ -0,0 +1,906 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "lx_stm32_ospi_driver.h" + +/* USER CODE BEGIN OSPI_CONFIG */ +/* Sample OctoSPI glue implementation using the the HAL/OSPI polling API. + * This has been tested with the MX25LM51245G OctoSPI memory. + + The OSPI IP was configured as below: + + Instance = OCTOSPI1 + FifoThreshold = 4 + DualQuad = disabled + MemoryType = Macronix + DeviceSize = 26 + ChipSelectHighTime = 2 + FreeRunningClock = disabled + ClockMode = low + ClockPrescaler = 2 + SampleShifting = none + DelayHoldQuarterCycle = enabled + ChipSelectBoundary = 0 + DelayBlockBypass = used + + * Different configuration can be used but need to be reflected in + * the implementation guarded with OSPI_HAL_CFG_xxx user tags. + */ + /* USER CODE END OSPI_CONFIG */ + +extern OSPI_HandleTypeDef hospi1; + +extern void MX_OCTOSPI1_Init(void); + +static uint8_t ospi_memory_reset (OSPI_HandleTypeDef *hospi); +static uint8_t ospi_set_write_enable (OSPI_HandleTypeDef *hospi); +static uint8_t ospi_auto_polling_ready (OSPI_HandleTypeDef *hospi, uint32_t timeout); +static uint8_t ospi_set_octalmode (OSPI_HandleTypeDef *hospi, uint8_t mode); + +/* USER CODE BEGIN SECTOR_BUFFER */ +ULONG ospi_sector_buffer[LX_STM32_OSPI_SECTOR_SIZE / sizeof(ULONG)]; +/* USER CODE END SECTOR_BUFFER */ + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +/** +* @brief Initializes the OSPI IP instance +* @param UINT instance OSPI instance to initialize +* @retval 0 on success error value otherwise +*/ +INT lx_stm32_ospi_lowlevel_init(UINT instance) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Init */ + + LX_PARAMETER_NOT_USED(instance); + + /* USER CODE END PRE_OSPI_Init */ + + /* Call the DeInit function to reset the driver */ + hospi1.Instance = OCTOSPI1; + if (HAL_OSPI_DeInit(&hospi1) != HAL_OK) + { + return 1; + } + + /* Init the OSPI */ + MX_OCTOSPI1_Init(); + + /* OSPI memory reset */ + if (ospi_memory_reset(&hospi1) != 0) + { + return 1; + } + + /* Insert 10 config intervals delay */ + HAL_Delay(1); + + /* Enable Octal Mode */ + if (ospi_set_octalmode(&hospi1, 1) != 0) + { + return 1; + } + + /* USER CODE BEGIN POST_OSPI_Init */ + + /* USER CODE END POST_OSPI_Init */ + + return status; +} + +/** +* @brief Get the status of the OSPI instance +* @param UINT instance OSPI instance +* @retval 0 if the OSPI is ready 1 otherwise +*/ +INT lx_stm32_ospi_get_status(UINT instance) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Get_Status */ + + LX_PARAMETER_NOT_USED(instance); + + /* USER CODE END PRE_OSPI_Get_Status */ + + OSPI_RegularCmdTypeDef s_command; + uint8_t reg[2]; + + /* Initialize the read status register command */ + /* USER CODE BEGIN OSPI_HAL_CFG_Get_Status */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.Instruction = OCTAL_READ_STATUS_REG_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Address = 0; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.NbData = 2; + s_command.DummyCycles = LX_STM32_OSPI_DUMMY_CYCLES_READ_OCTAL; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + /* USER CODE END OSPI_HAL_CFG_Get_Status */ + + /* Configure the command */ + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Reception of the data */ + if (HAL_OSPI_Receive(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Check the value of the register */ + if ((reg[0] & LX_STM32_OSPI_SR_WIP) != 0) + { + return 1; + } + + /* USER CODE BEGIN POST_OSPI_Get_Status */ + + /* USER CODE END POST_OSPI_Get_Status */ + + return status; +} + +/** +* @brief Get size info of the flash meomory +* @param UINT instance OSPI instance +* @param ULONG * block_size pointer to be filled with Flash block size +* @param ULONG * total_blocks pointer to be filled with Flash total number of blocks +* @retval 0 on Success and block_size and total_blocks are correctly filled + 1 on Failure, block_size = 0, total_blocks = 0 +*/ +INT lx_stm32_ospi_get_info(UINT instance, ULONG *block_size, ULONG *total_blocks) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Get_Info */ + + LX_PARAMETER_NOT_USED(instance); + + /* USER CODE END PRE_OSPI_Get_Info */ + + status = 0; + *block_size = LX_STM32_OSPI_SECTOR_SIZE; + *total_blocks = (LX_STM32_OSPI_FLASH_SIZE / LX_STM32_OSPI_SECTOR_SIZE); + + /* USER CODE BEGIN POST_OSPI_Get_Info */ + + /* USER CODE END POST_OSPI_Get_Info */ + + return status; +} + +/** +* @brief Read data from the OSPI memory into a buffer +* @param UINT instance OSPI instance +* @param ULONG * address the start address to read from +* @param ULONG * buffer the destination buffer +* @param ULONG words the total number of words to be read +* @retval 0 on Success 1 on Failure +*/ +INT lx_stm32_ospi_read(UINT instance, ULONG *address, ULONG *buffer, ULONG words) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Read */ + + LX_PARAMETER_NOT_USED(instance); + + /* USER CODE END PRE_OSPI_Read */ + + OSPI_RegularCmdTypeDef s_command; + + /* Initialize the read command */ + /* USER CODE BEGIN OSPI_HAL_CFG_read */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Address = (uint32_t)address; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.NbData = words * sizeof(ULONG); + s_command.DummyCycles = LX_STM32_OSPI_DUMMY_CYCLES_READ_OCTAL; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.Instruction = OCTAL_IO_DTR_READ_CMD; + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + /* USER CODE END OSPI_HAL_CFG_read */ + + /* Configure the command */ + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Reception of the data */ + if (HAL_OSPI_Receive(&hospi1, (uint8_t*)buffer, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* USER CODE BEGIN POST_OSPI_Read */ + + /* USER CODE END POST_OSPI_Read */ + + return status; +} + +/** +* @brief write a data buffer into the OSPI memory +* @param UINT instance OSPI instance +* @param ULONG * address the start address to write into +* @param ULONG * buffer the data source buffer +* @param ULONG words the total number of words to be written +* @retval 0 on Success 1 on Failure +*/ +INT lx_stm32_ospi_write(UINT instance, ULONG *address, ULONG *buffer, ULONG words) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Write */ + + LX_PARAMETER_NOT_USED(instance); + + /* USER CODE END PRE_OSPI_Write */ + + OSPI_RegularCmdTypeDef s_command; + uint32_t end_addr, current_size, current_addr; + + /* Calculation of the size between the write address and the end of the page */ + current_size = LX_STM32_OSPI_PAGE_SIZE - ((uint32_t)address % LX_STM32_OSPI_PAGE_SIZE); + + /* Check if the size of the data is less than the remaining place in the page */ + if (current_size > (uint32_t) words * sizeof(ULONG)) + { + current_size = (uint32_t) words * sizeof(ULONG); + } + + /* Initialize the address variables */ + current_addr = (uint32_t)address; + end_addr = (uint32_t)address + (uint32_t) words * sizeof(ULONG); + + /* Initialize the program command */ + /* USER CODE BEGIN OSPI_HAL_CFG_write */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.Instruction = OCTAL_PAGE_PROG_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.DummyCycles = 0; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + /* USER CODE END OSPI_HAL_CFG_write */ + + /* Perform the write page by page */ + do + { + s_command.Address = current_addr; + s_command.NbData = current_size; + + /* Enable write operations */ + if (ospi_set_write_enable(&hospi1) != 0) + { + return 1; + } + + /* Configure the command */ + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Transmission of the data */ + if (HAL_OSPI_Transmit(&hospi1, (uint8_t*)buffer, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait for end of program */ + if (ospi_auto_polling_ready(&hospi1, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != 0) + { + return 1; + } + + /* Update the address and size variables for next page programming */ + current_addr += current_size; + address += current_size; + current_size = ((current_addr + LX_STM32_OSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : LX_STM32_OSPI_PAGE_SIZE; + } while (current_addr < end_addr); + + /* USER CODE BEGIN POST_OSPI_Write */ + + /* USER CODE END POST_OSPI_Write */ + + return status; +} + +/** +* @brief Erase the whole flash or a single block +* @param UINT instance OSPI instance +* @param ULONG block the block to be erased +* @param ULONG erase_count the number of times the block was erased +* @param UINT full_chip_erase if set to 0 a single block is erased otherwise the whole flash is erased +* @retval 0 on Success 1 on Failure +*/ +INT lx_stm32_ospi_erase(UINT instance, ULONG block, ULONG erase_count, UINT full_chip_erase) +{ + INT status = 0; + + /* USER CODE BEGIN PRE_OSPI_Erase */ + + LX_PARAMETER_NOT_USED(instance); + LX_PARAMETER_NOT_USED(erase_count); + + /* USER CODE END PRE_OSPI_Erase */ + + OSPI_RegularCmdTypeDef s_command; + + /* Initialize the erase command */ + /* USER CODE BEGIN OSPI_HAL_CFG_erase */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + s_command.DummyCycles = 0; + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + + if(full_chip_erase) + { + s_command.Instruction = OCTAL_CHIP_ERASE_CMD; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + } + else + { + s_command.Instruction = OCTAL_BLOCK_ERASE_CMD; + s_command.Address = (block * LX_STM32_OSPI_SECTOR_SIZE); + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; /* DTR mode is enabled */ + } + /* USER CODE END OSPI_HAL_CFG_erase */ + + /* Enable write operations */ + if (ospi_set_write_enable(&hospi1) != 0) + { + return 1; + } + + /* Send the command */ + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait for end of erase */ + if (ospi_auto_polling_ready(&hospi1, LX_STM32_OSPI_BULK_ERASE_MAX_TIME) != 0) + { + return 1; + } + + /* USER CODE BEGIN POST_OSPI_Erase */ + + /* USER CODE END POST_OSPI_Erase */ + + return status; +} + +/** +* @brief Check that a block was actually erased +* @param UINT instance OSPI instance +* @param ULONG block the block to be checked +* @retval 0 on Success 1 on Failure +*/ +INT lx_stm32_ospi_is_block_erased(UINT instance, ULONG block) +{ + INT status = 0; + + /* USER CODE BEGIN OSPI_Block_Erased */ + + /* USER CODE END OSPI_Block_Erased */ + + return status; +} + +UINT lx_ospi_driver_system_error(UINT error_code) +{ + UINT status = LX_ERROR; + + /* USER CODE BEGIN OSPI_System_Error */ + + /* USER CODE END OSPI_System_Error */ + + return status; +} + +/** + * @brief Reset the OSPI memory. + * @param hospi: OSPI handle pointer + */ +static uint8_t ospi_memory_reset(OSPI_HandleTypeDef *hospi) +{ + /* USER CODE BEGIN OSPI_HAL_CFG_MEMORY_RESET */ + + OSPI_RegularCmdTypeDef s_command; + OSPI_AutoPollingTypeDef s_config; + + /* Initialize the reset enable command */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.Instruction = RESET_ENABLE_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS; + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + s_command.DummyCycles = 0; + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* Send the command */ + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Send the reset memory command */ + s_command.Instruction = RESET_MEMORY_CMD; + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait the memory is ready */ + s_command.Instruction = READ_STATUS_REG_CMD; + s_command.DataMode = HAL_OSPI_DATA_1_LINE; + s_command.NbData = 1; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE; + + s_config.Match = 0; + s_config.Mask = LX_STM32_OSPI_SR_WIP; + s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND; + s_config.Interval = 0x10; + s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_AutoPolling(&hospi1, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* USER CODE END OSPI_HAL_CFG_MEMORY_RESET */ + + return 0; +} + +/** + * @brief Send a Write Enable command and wait its effective. + * @param hospi: OSPI handle pointer + */ +static uint8_t ospi_set_write_enable(OSPI_HandleTypeDef *hospi) +{ + /* USER CODE BEGIN OSPI_HAL_CFG_WRITE_ENABLE */ + + OSPI_RegularCmdTypeDef s_command; + OSPI_AutoPollingTypeDef s_config; + uint8_t reg[2]; + + /* Enable write operations */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.Instruction = OCTAL_WRITE_ENABLE_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + s_command.DummyCycles = 0; + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait for write enabling */ + s_config.Match = LX_STM32_OSPI_SR_WEL; + s_config.Mask = LX_STM32_OSPI_SR_WEL; + + s_command.Instruction = OCTAL_READ_STATUS_REG_CMD; + s_command.Address = 0x0; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.NbData = 2; + + /* DTR mode is enabled */ + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + s_command.DummyCycles = 4; + + do + { + /* Wait for 10 config interval between each request */ + HAL_Delay(1); /* s_config.Interval(0x10) / Clock(55 MHz) = 0.29 ms */ + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Receive(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + }while((reg[0] & s_config.Mask ) != s_config.Match); + + /* USER CODE END OSPI_HAL_CFG_WRITE_ENABLE */ + + return 0; +} + +/** + * @brief Read the SR of the memory and wait the EOP. + * @param hospi: OSPI handle pointer + * @param timeout: timeout value before returning an error + */ +static uint8_t ospi_auto_polling_ready(OSPI_HandleTypeDef *hospi, uint32_t timeout) +{ + /* USER CODE BEGIN OSPI_HAL_CFG_AUTO_POLLING_READY */ + + OSPI_RegularCmdTypeDef s_command; + OSPI_AutoPollingTypeDef s_config; + uint8_t reg[2]; + + /* Configure automatic polling mode to wait for memory ready */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.Instruction = OCTAL_READ_STATUS_REG_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Address = 0; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.NbData = 2; + s_command.DummyCycles = LX_STM32_OSPI_DUMMY_CYCLES_READ_OCTAL; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + + s_config.Match = 0; + s_config.Mask = LX_STM32_OSPI_SR_WIP; + + do + { + /* Wait for 10 config interval between each request */ + HAL_Delay(1); /* s_config.Interval(0x10) / Clock(55 MHz) = 0.29 ms */ + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Receive(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + }while((reg[0] & s_config.Mask ) != s_config.Match); + + /* USER CODE END OSPI_HAL_CFG_AUTO_POLLING_READY */ + + return 0; +} + +/** + * @brief This function enables/disables the octal mode of the memory. + * @param hospi: OSPI handle + * @param mode: Octal operation mode enable/disable + * @retval None + */ +static uint8_t ospi_set_octalmode(OSPI_HandleTypeDef *hospi, uint8_t mode) +{ + /* USER CODE BEGIN OSPI_HAL_CFG_OCTAL_MODE */ + + OSPI_RegularCmdTypeDef s_command; + OSPI_AutoPollingTypeDef s_config; + uint8_t reg[2]; + + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE; + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND; + s_config.Interval = 0x10; + s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE; + + /* Activate/deactivate the Octal mode */ + if (mode) + { + /* Enable write operations */ + s_command.Instruction = WRITE_ENABLE_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + s_command.DummyCycles = 0; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait for write enabling */ + s_config.Match = LX_STM32_OSPI_SR_WEL; + s_config.Mask = LX_STM32_OSPI_SR_WEL; + + s_command.Instruction = READ_STATUS_REG_CMD; + s_command.DataMode = HAL_OSPI_DATA_1_LINE; + s_command.NbData = 1; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_AutoPolling(&hospi1, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Write Configuration register 2 (with new dummy cycles) */ + s_command.Instruction = WRITE_CFG_REG_2_CMD; + s_command.Address = LX_STM32_OSPI_CR2_REG3_ADDR; + s_command.AddressMode = HAL_OSPI_ADDRESS_1_LINE; + reg[0] = LX_STM32_OSPI_DUMMY_CYCLES_CR_CFG; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Transmit(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Enable write operations */ + s_command.Instruction = WRITE_ENABLE_CMD; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Configure automatic polling mode to wait for write enabling */ + s_command.Instruction = READ_STATUS_REG_CMD; + s_command.DataMode = HAL_OSPI_DATA_1_LINE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_AutoPolling(&hospi1, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Write Configuration register 2 (with Octal I/O SPI protocol) */ + s_command.Instruction = WRITE_CFG_REG_2_CMD; + s_command.Address = LX_STM32_QSPI_CR2_REG1_ADDR; + s_command.AddressMode = HAL_OSPI_ADDRESS_1_LINE; + + /* DTR mode is enabled */ + reg[0] = LX_STM32_OSPI_CR2_DOPI; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Transmit(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Wait that the configuration is effective and check that memory is ready */ + HAL_Delay(LX_STM32_OSPI_WRITE_REG_MAX_TIME); + + if (ospi_auto_polling_ready(&hospi1, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != 0) + { + return 1; + } + + /* Check the configuration has been correctly done */ + s_command.Instruction = OCTAL_READ_CFG_REG_2_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.DummyCycles = LX_STM32_OSPI_DUMMY_CYCLES_READ_OCTAL; + s_command.NbData = 2; + reg[0] = 0; + + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + s_command.DQSMode = HAL_OSPI_DQS_ENABLE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Receive(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* DTR mode is enabled */ + if (reg[0] != LX_STM32_OSPI_CR2_DOPI) + { + return 1; + } + } + else + { + /* Enable write operations */ + if (ospi_set_write_enable(&hospi1) != 0) + { + return 1; + } + + /* Write Configuration register 2 (with Octal I/O SPI protocol) */ + s_command.Instruction = OCTAL_WRITE_CFG_REG_2_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Address = LX_STM32_QSPI_CR2_REG1_ADDR; + s_command.AddressMode = HAL_OSPI_ADDRESS_8_LINES; + s_command.DataMode = HAL_OSPI_DATA_8_LINES; + s_command.NbData = 2; + s_command.DummyCycles = 0; + reg[0] = 0; + reg[1] = 0; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Transmit(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Wait that the configuration is effective and check that memory is ready */ + HAL_Delay(LX_STM32_OSPI_WRITE_REG_MAX_TIME); + + s_command.Instruction = READ_STATUS_REG_CMD; + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.DataMode = HAL_OSPI_DATA_1_LINE; + s_command.NbData = 1; + s_command.DummyCycles = 0; + + /* DTR mode is enabled */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE; + + s_config.Match = 0; + s_config.Mask = LX_STM32_OSPI_SR_WIP; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_AutoPolling(&hospi1, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + /* Check the configuration has been correctly done */ + s_command.Instruction = READ_CFG_REG_2_CMD; + s_command.Address = LX_STM32_QSPI_CR2_REG1_ADDR; + s_command.AddressMode = HAL_OSPI_ADDRESS_1_LINE; + + if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (HAL_OSPI_Receive(&hospi1, reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) + { + return 1; + } + + if (reg[0] != 0) + { + return 1; + } + } + + /* USER CODE END OSPI_HAL_CFG_OCTAL_MODE */ + + return 0; +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ diff --git a/common/stm32_drivers/template/lx_stm32_qspi_driver.h b/common/stm32_drivers/template/lx_stm32_qspi_driver.h new file mode 100644 index 0000000..06c1595 --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_qspi_driver.h @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef LX_STM32_QSPI_DRIVER_H +#define LX_STM32_QSPI_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "lx_api.h" +#include "stm32NNxx_hal.h" + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Exported types ------------------------------------------------------------*/ +/* USER CODE BEGIN ET */ + +/* USER CODE END ET */ + +/* Exported constants --------------------------------------------------------*/ + +/* the QuadSPI instance, default value set to 0 */ +#define LX_STM32_QSPI_INSTANCE 0 +#define LX_STM32_QSPI_DEFAULT_TIMEOUT 10 * TX_TIMER_TICKS_PER_SECOND +#define LX_STM32_DEFAULT_SECTOR_SIZE LX_STM32_QSPI_SECTOR_SIZE +#define LX_STM32_QSPI_DMA_API 0 + +/* when set to 1 LevelX is initializing the QuadSPI memory, + * otherwise it is the up to the application to perform it. + */ +#define LX_STM32_QSPI_INIT 1 + +#if (LX_STM32_QSPI_INIT == 1) + +/* allow the driver to fully erase the QuadSPI chip. This should be used carefully. + * the call is blocking and takes a while. by default it is set to 0. + */ +#define LX_STM32_QSPI_ERASE 0 +#endif + +/* USER CODE BEGIN EC */ + +/* USER CODE END EC */ + +/* Exported macro ------------------------------------------------------------*/ +/* USER CODE BEGIN EM */ + +#define LX_STM32_QSPI_CURRENT_TIME tx_time_get + +#define LX_STM32_QSPI_POST_INIT() + +#define LX_STM32_QSPI_PRE_READ_TRANSFER(__status__) + +#define LX_STM32_QSPI_READ_CPLT_NOTIFY(__status__) + +#define LX_STM32_QSPI_POST_READ_TRANSFER(__status__) + +#define LX_STM32_QSPI_READ_TRANSFER_ERROR(__status__) + +#define LX_STM32_QSPI_PRE_WRITE_TRANSFER(__status__) + +#define LX_STM32_QSPI_WRITE_CPLT_NOTIFY(__status__) + +#define LX_STM32_QSPI_POST_WRITE_TRANSFER(__status__) + +#define LX_STM32_QSPI_WRITE_TRANSFER_ERROR(__status__) + +/* USER CODE END EM */ + +/* Exported functions prototypes ---------------------------------------------*/ +INT lx_stm32_qspi_lowlevel_init(UINT instance); +INT lx_stm32_qspi_lowlevel_deinit(UINT instance); + +INT lx_stm32_qspi_get_status(UINT instance); +INT lx_stm32_qspi_get_info(UINT instance, ULONG *block_size, ULONG *total_blocks); + +INT lx_stm32_qspi_read(UINT instance, ULONG *address, ULONG *buffer, ULONG words); +INT lx_stm32_qspi_write(UINT instance, ULONG *address, ULONG *buffer, ULONG words); + +INT lx_stm32_qspi_erase(UINT instance, ULONG block, ULONG erase_count, UINT full_chip_erase); +INT lx_stm32_qspi_is_block_erased(UINT instance, ULONG block); + +UINT lx_qspi_driver_system_error(UINT error_code); + +UINT lx_stm32_qspi_initialize(LX_NOR_FLASH *nor_flash); + +/* USER CODE BEGIN EFP */ + +/* USER CODE END EFP */ + +/* Private defines -----------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* QSPI Memory related defines */ +#define LX_STM32_QSPI_SECTOR_SIZE +#define LX_STM32_QSPI_FLASH_SIZE +#define LX_STM32_QSPI_StatusReg_READY +#define LX_STM32_QSPI_DUMMY_CYCLES_READ_QUAD +#define LX_STM32_QSPI_PAGE_SIZE +#define LX_STM32_QSPI_BULK_ERASE_MAX_TIME +#define LX_STM32_QSPI_SECTOR_ERASE_MAX_TIME +#define LX_STM32_QSPI_VCR_NB_DUMMY +#define LX_STM32_QSPI_SR_WREN +#define LX_STM32_QSPI_SR_WIP + +#define LX_STM32_QSPI_GET_STATUS_REG_CMD +#define LX_STM32_QSPI_QUAD_INOUT_FAST_READ_CMD +#define LX_STM32_QSPI_QUAD_IN_FAST_PROG_CMD +#define LX_STM32_QSPI_BULK_ERASE_CMD +#define LX_STM32_QSPI_SECTOR_ERASE_CMD +#define LX_STM32_QSPI_RESET_ENABLE_CMD +#define LX_STM32_QSPI_RESET_MEMORY_CMD +#define LX_STM32_QSPI_READ_VOL_CFG_REG_CMD +#define LX_STM32_QSPI_WRITE_VOL_CFG_REG_CMD +#define LX_STM32_QSPI_WRITE_ENABLE_CMD +#define LX_STM32_QSPI_READ_STATUS_REG_CMD + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */ + +#ifdef __cplusplus +} +#endif +#endif /* LX_STM32_QSPI_DRIVER_H */ diff --git a/common/stm32_drivers/template/lx_stm32_qspi_driver_glue.c b/common/stm32_drivers/template/lx_stm32_qspi_driver_glue.c new file mode 100644 index 0000000..d6f14da --- /dev/null +++ b/common/stm32_drivers/template/lx_stm32_qspi_driver_glue.c @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "lx_stm32_qspi_driver.h" +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ + +ULONG qspi_sector_buffer[LX_STM32_QSPI_SECTOR_SIZE/sizeof(ULONG)]; + +/** +* @brief Initializes the QSPI IP instance +* @param UINT Instance QSPI instance to initialize +* @retval 0 on success error value otherwise +*/ +INT lx_stm32_qspi_lowlevel_init(UINT instance) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Init */ + + /* USER CODE END OSPI_Init */ + + return status; +} + +/** +* @brief Get the status of the QSPI instance +* @param UINT Instance QSPI instance +* @retval 0 if the QSPI is ready 1 otherwise +*/ +INT lx_stm32_qspi_get_status(UINT instance) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Get_Status */ + + /* USER CODE END OSPI_Get_Status */ + + return status; +} + + +/** +* @brief Get size info of the flash meomory +* @param UINT Instance QSPI instance +* @param UINT * block_size pointer to be filled with Flash block size +* @param UINT * total_blocks pointer to be filled with Flash total number of blocks +* @retval 0 on Success and block_size and total_blocks are correctly filled + 1 on Failure, block_size = 0, total_blocks = 0 +*/ +INT lx_stm32_qspi_get_info(UINT instance, ULONG *block_size, ULONG *total_blocks) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Get_Info */ + + /* USER CODE END OSPI_Get_Info */ + + return status; +} + +/** +* @brief Read data from the QSPI memory into a buffer +* @param UINT Instance QSPI instance +* @param ULONG * address the start address to read from +* @param ULONG * buffer the destination buffer +* @param ULONG words the total number of words to be read +* @retval 0 on Success +1 on Failure +*/ + +INT lx_stm32_qspi_read(UINT instance, ULONG *address, ULONG *buffer, ULONG words) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Read */ + + /* USER CODE END OSPI_Read */ + + return status; +} + +/** +* @brief write a data buffer into the QSPI memory +* @param UINT Instance QSPI instance +* @param ULONG * address the start address to write into +* @param ULONG * buffer the data source buffer +* @param ULONG words the total number of words to be written +* @retval 0 on Success +1 on Failure +*/ + +INT lx_stm32_qspi_write(UINT instance, ULONG *address, ULONG *buffer, ULONG words) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Write */ + + /* USER CODE END OSPI_Write */ + + return status; +} + +/** +* @brief Erase the whole flash or a single block +* @param UINT Instance QSPI instance +* @param ULONG block the block to be erased +* @param ULONG erase_count the number of times the block was erased +* @param UINT full_chip_erase if set to 0 a single block is erased otherwise the whole flash is +* @retval 0 on Success +1 on Failure +*/ +INT lx_stm32_qspi_erase(UINT instance, ULONG block, ULONG erase_count, UINT full_chip_erase) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Erase_Block */ + + /* USER CODE END OSPI_Erase_Block */ + + return status; +} + +/** +* @brief Check that a block was actually erased +* @param UINT Instance QSPI instance +* @param ULONG block the block to be checked +* @retval 0 on Success +1 on Failure +*/ +INT lx_stm32_qspi_is_block_erased(UINT instance, ULONG block) +{ + INT status = 0; + /* USER CODE BEGIN OSPI_Block_Erased */ + + /* USER CODE END OSPI_Block_Erased */ + + return status; +} + +UINT lx_ospi_driver_system_error(UINT error_code) +{ + UINT status = LX_ERROR; + /* USER CODE BEGIN OSPI_Block_Erased */ + + /* USER CODE END OSPI_Block_Erased */ + + return status; +} + +/* USER CODE BEGIN 1 */ + +/* USER CODE END 1 */