/** ****************************************************************************** * @file stm32_sal_xspi.c * @author MCD Application Team * @brief This file implements the software adaptation layer for XSPI. ****************************************************************************** * @attention * * Copyright (c) 2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32_extmem.h" #include "stm32_extmem_conf.h" #if EXTMEM_SAL_XSPI_DEBUG_LEVEL != 0 && defined(EXTMEM_MACRO_DEBUG) #include #endif /* EXTMEM_SAL_XSPI_DEBUG_LEVEL != 0 && EXTMEM_MACRO_DEBUG */ #if EXTMEM_SAL_XSPI == 1 #include "stm32_sal_xspi_type.h" #include "stm32_sal_xspi_api.h" /** @defgroup SAL_XSPI SAL_XSPI : Software adaptation layer for XSPI * @ingroup EXTMEM_SAL * @{ */ /* Private Macros ------------------------------------------------------------*/ /** @defgroup SAL_XSPI_Private_Macros SAL XSPI Private Macros * @{ */ #if EXTMEM_SAL_XSPI_DEBUG_LEVEL == 0 || !defined(EXTMEM_MACRO_DEBUG) #define DEBUG_PARAM_BEGIN() #define DEBUG_PARAM_DATA(_STR_) #define DEBUG_PARAM_INT(_INT_ ) #define DEBUG_PARAM_INTD(_INT_ ) #define DEBUG_PARAM_END() #define DEBUG_AUTOPOLLING(_DR_,_MVAL_,_MMASK_) #define STR_PHY_LINK(_PHY_) #else /** * @brief Trace header macro. */ #define DEBUG_PARAM_BEGIN() EXTMEM_MACRO_DEBUG("\t\tSALXSPI::"); const uint8_t phylink_string[][17] = {"PHY_LINK_1S1S1S", "PHY_LINK_1S1S2S", "PHY_LINK_1S2S2S", "PHY_LINK_1S1D1D", "PHY_LINK_4S4S4S", "PHY_LINK_4S4D4D", "PHY_LINK_4D4D4D", "PHY_LINK_1S8S8S", "PHY_LINK_8S8D8D", "PHY_LINK_8D8D8D", "PHY_LINK_RAM8", #if defined(HAL_XSPI_DATA_16_LINES) "PHY_LINK_RAM16", #endif /* HAL_XSPI_DATA_16_LINES */ }; #define STR_PHY_LINK(_PHY_) phylink_string[_PHY_] /** * @brief Trace data string macro. */ #define DEBUG_PARAM_DATA(_STR_) EXTMEM_MACRO_DEBUG(_STR_); /** * @brief Trace data integer macro. */ #define DEBUG_PARAM_INT(_INT_) \ { \ char str[10]; \ (void)snprintf(str, sizeof(str), "0x%x", _INT_); \ EXTMEM_MACRO_DEBUG(str); \ } #define DEBUG_PARAM_INTD(_INT_) \ { \ char str[15]; \ (void)snprintf(str, sizeof(str), "%zu", _INT_); \ EXTMEM_MACRO_DEBUG(str); \ } /** * @brief Trace close macro */ #define DEBUG_PARAM_END() EXTMEM_MACRO_DEBUG("\n"); #if EXTMEM_SAL_XSPI_DEBUG_LEVEL == 2 #define DEBUG_AUTOPOLLING(_DR_,_MVAL_,_MMASK_) \ { \ char str[50]; \ (void)snprintf(str, sizeof(str),"DR:0x%x::MVal:0x%x::MMask:0x%x\n\r", _DR_, _MVAL_, _MMASK_); \ DEBUG_PARAM_BEGIN() \ DEBUG_PARAM_DATA(str) \ DEBUG_PARAM_END() \ } #else #define DEBUG_AUTOPOLLING(_DR_,_MVAL_,_MMASK_) #endif /* EXTMEM_SAL_XSPI_DEBUG_LEVEL == 2 */ #endif /* EXTMEM_SAL_XSPI_DEBUG_LEVEL == 0 || !EXTMEM_MACRO_DEBUG */ /** * @brief Default SAL timeout (100 ms) */ #define SAL_XSPI_TIMEOUT_DEFAULT_VALUE (100U) /** * @} */ #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) /** @defgroup SAL_XSPI_Private_Transfer SAL XSPI Dma transfer management definition * @{ */ /** * @brief State of the transfer status */ typedef enum { SALXSPI_TRANSFER_NONE, /*!< */ SALXSPI_TRANSFER_OK, SALXSPI_TRANSFER_ERROR } SAL_XSPI_TRANSFER_STATUS; /** * @brief Variable of the transfer status */ volatile SAL_XSPI_TRANSFER_STATUS salXSPI_status = SALXSPI_TRANSFER_NONE; /** * @} */ #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ /* Private typedefs ---------------------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /** @defgroup SAL_XSPI_Private_Functions SAL XSP Private Functions * @{ */ uint16_t XSPI_FormatCommand(uint8_t CommandExtension, uint32_t InstructionWidth, uint8_t Command); HAL_StatusTypeDef XSPI_Transmit(SAL_XSPI_ObjectTypeDef *SalXspi, const uint8_t *Data); HAL_StatusTypeDef XSPI_Receive(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t *Data); #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) void SAL_XSPI_ErrorCallback(struct __XSPI_HandleTypeDef *hxspi); void SAL_XSPI_CompleteCallback(struct __XSPI_HandleTypeDef *hxspi); #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ /** * @} */ /* Exported variables ---------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /** @defgroup SAL_XSPI_Exported_Functions SAL XSP Exported Functions * @{ */ /** * @brief This function sets the clock according the clock in and the expected clock * @param SalXspi SAL XSPI handle * @param ClockIn Clock in input * @param ClockRequested Clock requested * @param ClockReal Pointer on the value of the real clock used * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_SetClock(SAL_XSPI_ObjectTypeDef *SalXspi, uint32_t ClockIn, uint32_t ClockRequested, uint32_t *ClockReal) { HAL_StatusTypeDef retr = HAL_OK; uint32_t divider; if (ClockRequested == 0u) { retr = HAL_ERROR; } else { divider = (ClockIn / ClockRequested); if (divider >= 1u) { *ClockReal = ClockIn / divider; if (*ClockReal <= ClockRequested) { divider--; } } /* Real clock calculation */ *ClockReal = ClockIn / (divider + 1u); DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::CLOCKDIV::"); DEBUG_PARAM_INT(divider + 1); DEBUG_PARAM_END(); DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::CLKFREQ::"); DEBUG_PARAM_INTD(*ClockReal); DEBUG_PARAM_END(); MODIFY_REG(SalXspi->hxspi->Instance->DCR2, XSPI_DCR2_PRESCALER, (uint32_t)divider << XSPI_DCR2_PRESCALER_Pos); } return retr; } /** * @brief This function is used to configure the way to discuss with the memory * @param SalXspi SAL XSPI handle * @param HALHandle HAl XSPI handle used for memory access * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_Init(SAL_XSPI_ObjectTypeDef *SalXspi, void *HALHandle) { XSPI_RegularCmdTypeDef s_commandbase = { .OperationType = HAL_XSPI_OPTYPE_COMMON_CFG, .IOSelect = HAL_XSPI_SELECT_IO_7_0, .InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE, .Instruction = EXTMEM_READ_SFDP_COMMAND, .InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS, .InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE, .AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE, .AddressMode = HAL_XSPI_ADDRESS_1_LINE, .AddressWidth = HAL_XSPI_ADDRESS_24_BITS, .Address = 0x0, .AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE, .DataMode = HAL_XSPI_DATA_1_LINE, .DataLength = 0x0, .DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE, .DummyCycles = EXTMEM_READ_SFDP_NB_DUMMY_CYCLES_DEFAULT, .DQSMode = HAL_XSPI_DQS_DISABLE, #if defined(XSPI_CCR_SIOO) .SIOOMode = HAL_XSPI_SIOO_INST_EVERY_CMD, #endif /* XSPI_CCR_SIOO */ }; SalXspi->hxspi = (XSPI_HandleTypeDef *)HALHandle; SalXspi->Commandbase = s_commandbase; SalXspi->CommandExtension = 0; SalXspi->PhyLink = PHY_LINK_1S1S1S; #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) /* Set completion call back */ HAL_XSPI_RegisterCallback(SalXspi->hxspi, HAL_XSPI_RX_CPLT_CB_ID, SAL_XSPI_CompleteCallback); HAL_XSPI_RegisterCallback(SalXspi->hxspi, HAL_XSPI_TX_CPLT_CB_ID, SAL_XSPI_CompleteCallback); /* Set the error callback */ HAL_XSPI_RegisterCallback(SalXspi->hxspi, HAL_XSPI_ERROR_CB_ID, SAL_XSPI_ErrorCallback); #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ return HAL_OK; } /** * @brief This function sets a configuration parameter * @param SalXspi SAL XSPI handle * @param ParametersType @ref SAL_XSPI_MemParamTypeTypeDef * @param ParamVal Pointer on the parameter value * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_MemoryConfig(SAL_XSPI_ObjectTypeDef *SalXspi, SAL_XSPI_MemParamTypeTypeDef ParametersType, void *ParamVal) { HAL_StatusTypeDef retr = HAL_OK; XSPI_RegularCmdTypeDef s_commandbase = SalXspi->Commandbase; switch (ParametersType) { case PARAM_PHY_LINK: { SalXspi->PhyLink = *((SAL_XSPI_PhysicalLinkTypeDef *)ParamVal); DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::PARAM_PHY_LINK::"); DEBUG_PARAM_DATA(STR_PHY_LINK(SalXspi->PhyLink)); switch (SalXspi->PhyLink) { case PHY_LINK_1S1D1D: case PHY_LINK_1S2S2S: case PHY_LINK_1S1S2S: case PHY_LINK_1S1S1S: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_1_LINE; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_24_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE; s_commandbase.DataMode = HAL_XSPI_DATA_1_LINE; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; s_commandbase.DummyCycles = 8; s_commandbase.DQSMode = HAL_XSPI_DQS_DISABLE; break; } case PHY_LINK_4S4D4D: case PHY_LINK_4S4S4S: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_4_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_4_LINES; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_24_BITS; s_commandbase.DataMode = HAL_XSPI_DATA_4_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; s_commandbase.DummyCycles = 6; s_commandbase.DQSMode = HAL_XSPI_DQS_DISABLE; break; } case PHY_LINK_4D4D4D: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_4_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_4_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_24_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_commandbase.DataMode = HAL_XSPI_DATA_4_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_commandbase.DummyCycles = 6; s_commandbase.DQSMode = HAL_XSPI_DQS_DISABLE; break; } case PHY_LINK_1S8S8S: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE; s_commandbase.DataMode = HAL_XSPI_DATA_8_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; s_commandbase.DummyCycles = 8; s_commandbase.DQSMode = HAL_XSPI_DQS_DISABLE; break; } case PHY_LINK_8S8D8D: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_commandbase.DataMode = HAL_XSPI_DATA_8_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_commandbase.DummyCycles = 8; s_commandbase.DQSMode = HAL_XSPI_DQS_ENABLE; break; } case PHY_LINK_8D8D8D: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_commandbase.DataMode = HAL_XSPI_DATA_8_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_commandbase.DummyCycles = 20; s_commandbase.DQSMode = HAL_XSPI_DQS_ENABLE; break; } case PHY_LINK_RAM8: { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_commandbase.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE; s_commandbase.DataMode = HAL_XSPI_DATA_8_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_commandbase.DummyCycles = 10; s_commandbase.DQSMode = HAL_XSPI_DQS_ENABLE; break; } #if defined(HAL_XSPI_DATA_16_LINES) case PHY_LINK_RAM16 : { s_commandbase.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES; s_commandbase.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS; s_commandbase.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE; s_commandbase.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_commandbase.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_commandbase.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE; s_commandbase.DataMode = HAL_XSPI_DATA_16_LINES; s_commandbase.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_commandbase.DummyCycles = 10; s_commandbase.DQSMode = HAL_XSPI_DQS_ENABLE; break; } #endif /* HAL_XSPI_DATA_16_LINES */ default: retr = HAL_ERROR; break; } DEBUG_PARAM_END(); break; } case PARAM_ADDRESS_4BYTES: { DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::PARAM_ADDRESS_4BYTES"); DEBUG_PARAM_END(); s_commandbase.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; break; } case PARAM_FLASHSIZE: { uint8_t valParam = *((uint8_t *)ParamVal); DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::PARAM_FLASHSIZE::"); DEBUG_PARAM_INT(valParam); DEBUG_PARAM_END(); MODIFY_REG(SalXspi->hxspi->Instance->DCR1, XSPI_DCR1_DEVSIZE, ((uint32_t)valParam) << XSPI_DCR1_DEVSIZE_Pos); break; } case PARAM_DUMMY_CYCLES: { uint8_t valParam = *((uint8_t *)ParamVal); DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::PARAM_DUMMY_CYCLES::"); DEBUG_PARAM_INT(valParam); DEBUG_PARAM_END(); s_commandbase.DummyCycles = valParam; break; } default: DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::SAL_XSPI_MemoryConfig::ERROR"); DEBUG_PARAM_END(); retr = HAL_ERROR; break; } SalXspi->Commandbase = s_commandbase; return retr; } /** * @brief This function gets SFDP data * @param SalXspi SAL XSPI handle * @param Address Address to read the data * @param Data Data pointer * @param DataSize Size of the data to read * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_GetSFDP(SAL_XSPI_ObjectTypeDef *SalXspi, uint32_t Address, uint8_t *Data, uint32_t DataSize) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the read ID command */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, EXTMEM_READ_SFDP_COMMAND); s_command.Address = Address; s_command.DataLength = DataSize; s_command.DummyCycles = SalXspi->SFDPDummyCycle; if ((s_command.AddressMode == HAL_XSPI_ADDRESS_1_LINE) || (s_command.AddressMode == HAL_XSPI_ADDRESS_4_LINES)) { s_command.AddressWidth = HAL_XSPI_ADDRESS_24_BITS; } if (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE) { s_command.DQSMode = HAL_XSPI_DQS_ENABLE; } else { s_command.DQSMode = HAL_XSPI_DQS_DISABLE; } /* Configure the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } /* Reception of the data */ retr = HAL_XSPI_Receive(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function reads the flash ID * @param SalXspi SAL XSPI handle * @param Data Data pointer where read ID should be stored * @param DataSize Number of data to read * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_GetId(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t *Data, uint32_t DataSize) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the Read ID command */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, EXTMEM_READ_JEDEC_ID_SPI_COMMAND); s_command.DataLength = DataSize; if (s_command.InstructionMode == HAL_XSPI_INSTRUCTION_1_LINE) { s_command.AddressMode = HAL_XSPI_ADDRESS_NONE; s_command.DummyCycles = 0; /* This behavior is linked with micron memory to read ID in 1S8S8S */ s_command.DataMode = HAL_XSPI_DATA_1_LINE; } else if (s_command.InstructionMode == HAL_XSPI_INSTRUCTION_4_LINES) { s_command.AddressMode = HAL_XSPI_ADDRESS_NONE; s_command.DummyCycles = 0; /* This behavior is linked with ISSI memory to read ID in 4S4S4S */ s_command.DataMode = HAL_XSPI_DATA_4_LINES; } else if (s_command.InstructionMode == HAL_XSPI_INSTRUCTION_8_LINES) { s_command.Address = 0; /* Specific case for Macronix memories : RDID is not Data DTR */ if ((Data[0] == 0xC2) && (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE)) { s_command.DummyCycles = 4; s_command.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; } /* Specific case for GigaDevice memories : RDID has no address even in Octal mode */ else if ((Data[0] == 0xC8) && (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE)) { s_command.DummyCycles = 8; s_command.AddressMode = HAL_XSPI_ADDRESS_NONE; } else { s_command.DummyCycles = 8; } /* Required behavior to be confirmed on the other memories */ } else { s_command.Address = 0; s_command.DummyCycles = 8; } /* Configure the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } /* Reception of the data */ retr = HAL_XSPI_Receive(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function reads data from the flash * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Address Address to read the data * @param Data Data pointer * @param DataSize Size of the data to read * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_Read(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address, uint8_t *Data, uint32_t DataSize) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the read ID command */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.Address = Address; s_command.DataLength = DataSize; /* DTR management for single/dual/quad */ switch (SalXspi->PhyLink) { case PHY_LINK_4S4D4D : s_command.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_ENABLE; s_command.DataDTRMode = HAL_XSPI_DATA_DTR_ENABLE; s_command.DummyCycles = SalXspi->DTRDummyCycle; break; case PHY_LINK_1S2S2S : s_command.AddressMode = HAL_XSPI_ADDRESS_2_LINES; s_command.DataMode = HAL_XSPI_DATA_2_LINES; break; case PHY_LINK_1S1S2S : s_command.DataMode = HAL_XSPI_DATA_2_LINES; break; default : /* Keep default parameters */ break; } /* Configure the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } /* Read data */ retr = XSPI_Receive(SalXspi, Data); error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function writes data at an Address * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Address Address to write the data * @param Data Data pointer * @param DataSize Size of the data to write * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_Write(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address, const uint8_t *Data, uint32_t DataSize) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the read ID command */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.Address = Address; s_command.DataLength = DataSize; s_command.DummyCycles = 0u; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; /* Configure the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (HAL_OK != retr) { goto error; } /* Transmit data */ retr = XSPI_Transmit(SalXspi, Data); error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function sends a command and an address * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Address Address to write the data * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_CommandSendAddress(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the writing of status register */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); if (s_command.InstructionMode == HAL_XSPI_INSTRUCTION_1_LINE) { s_command.AddressMode = HAL_XSPI_ADDRESS_1_LINE; } s_command.Address = Address; s_command.DummyCycles = 0U; s_command.DataMode = HAL_XSPI_DATA_NONE; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; /* Send the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function sends a command + data * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Data Data pointer * @param DataSize Size of the data to write * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_CommandSendData(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint8_t *Data, uint16_t DataSize) { XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; HAL_StatusTypeDef retr; /* Initialize the writing of status register */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.AddressMode = HAL_XSPI_ADDRESS_NONE; s_command.DummyCycles = 0U; s_command.DataLength = DataSize; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; if (DataSize == 0u) { s_command.DataMode = HAL_XSPI_DATA_NONE; } /* Send the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if ((retr == HAL_OK) && (DataSize != 0u)) { retr = HAL_XSPI_Transmit(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); } if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function sends a command to read the data * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Data Data pointer * @param DataSize Size of the data to receive * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_SendReadCommand(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint8_t *Data, uint16_t DataSize) { XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; HAL_StatusTypeDef retr; /* Initialize the reading of status register */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.AddressMode = HAL_XSPI_ADDRESS_NONE; s_command.DummyCycles = 0u; s_command.DataLength = DataSize; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; if (DataSize == 0u) { s_command.DataMode = HAL_XSPI_DATA_NONE; } /* Send the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if ((retr == HAL_OK) && (DataSize != 0u)) { /* Receive data */ retr = HAL_XSPI_Receive(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); } if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function sends a command with address and read the data * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Address Address value * @param Data Data pointer * @param DataSize Size of the data to read * @param ManuId Manufacturer Identifier * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_CommandSendReadAddress(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address, uint8_t *Data, uint16_t DataSize, uint8_t ManuId) { XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; HAL_StatusTypeDef retr; /* Initialize the reading of status register */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.Address = Address; s_command.DummyCycles = SalXspi->SFDPDummyCycle; s_command.DataLength = DataSize; /* Specific case for Macronix memories : RDID and RDCR are not Data DTR */ if ((ManuId == EXTMEM_MANUFACTURER_MACRONIX) && (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE)) { s_command.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; } /* Specific case for GigaDevice memories : Read Configuration Register are not Data DTR */ else if ((ManuId == 0xC8) && (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE)) { s_command.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; } else { s_command.DQSMode = HAL_XSPI_DQS_DISABLE; } /* Send the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr == HAL_OK) { /* Retrieve data */ retr = HAL_XSPI_Receive(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); } if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function controls the status register * @param SalXspi SAL XSPI handle * @param Command Command to execute * @param Address Specify the address * @param MatchValue Expected value * @param MatchMask Mask used to control the expected value * @param ManuId Manufacturer Identifier * @param Timeout Timeout parameter * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_CheckStatusRegister(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t Command, uint32_t Address, uint8_t MatchValue, uint8_t MatchMask, uint8_t ManuId, uint32_t Timeout) { XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; XSPI_AutoPollingTypeDef s_config = { .MatchValue = MatchValue, .MatchMask = MatchMask, .MatchMode = HAL_XSPI_MATCH_MODE_AND, .AutomaticStop = HAL_XSPI_AUTOMATIC_STOP_ENABLE, .IntervalTime = 0x10 }; HAL_StatusTypeDef retr; /* Initialize the reading of status register */ s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, Command); s_command.DataLength = 1u; s_command.DQSMode = HAL_XSPI_DQS_DISABLE; if (s_command.InstructionMode == HAL_XSPI_INSTRUCTION_1_LINE) { /* Specific behavior for Cypress to force 1 line on status read */ s_command.DataMode = HAL_XSPI_DATA_1_LINE; s_command.AddressMode = HAL_XSPI_DATA_NONE; s_command.DummyCycles = 0u; } /* Address is used only in 8 LINES format */ if (s_command.DataMode == HAL_XSPI_DATA_8_LINES) { /* Specific case for Macronix memories : RDID and RDCR are not Data DTR */ if ((ManuId == EXTMEM_MANUFACTURER_MACRONIX) && (s_command.DataDTRMode == HAL_XSPI_DATA_DTR_ENABLE)) { s_command.DQSMode = HAL_XSPI_DQS_ENABLE; s_command.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE; } s_command.AddressMode = HAL_XSPI_ADDRESS_8_LINES; s_command.AddressWidth = HAL_XSPI_ADDRESS_32_BITS; s_command.Address = Address; } /* Send the command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr == HAL_OK) { retr = HAL_XSPI_AutoPolling(SalXspi->hxspi, &s_config, Timeout); DEBUG_AUTOPOLLING(SalXspi->hxspi->Instance->DR, s_config.MatchValue, s_config.MatchMask) } if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function enables the memory mapped mode * @param SalXspi SAL XSPI handle * @param WrapCommand Wrap command to execute in case of write operation * @param WrapDummy Number of dummy cycle for the read operation * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_ConfigureWrappMode(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t WrapCommand, uint8_t WrapDummy) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; /* Initialize the read ID command */ s_command.OperationType = HAL_XSPI_OPTYPE_WRAP_CFG; s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, WrapCommand); s_command.DummyCycles = WrapDummy; /* Configure the read command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function enables the memory mapped mode * @param SalXspi SAL XSPI handle * @param CommandRead command to execute in case of read operation * @param DummyRead number of dummy cycle for the read operation * @param CommandWrite command to execute in case of write operation * @param DummyWrite number of dummy cycle for the read operation * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_EnableMapMode(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t CommandRead, uint8_t DummyRead, uint8_t CommandWrite, uint8_t DummyWrite) { HAL_StatusTypeDef retr; XSPI_RegularCmdTypeDef s_command = SalXspi->Commandbase; XSPI_MemoryMappedTypeDef sMemMappedCfg = {0}; /* Initialize the read ID command */ s_command.OperationType = HAL_XSPI_OPTYPE_READ_CFG; s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, CommandRead); s_command.DummyCycles = DummyRead; /* Configure the read command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } /* Initialize the read ID command */ s_command.OperationType = HAL_XSPI_OPTYPE_WRITE_CFG; s_command.Instruction = XSPI_FormatCommand(SalXspi->CommandExtension, s_command.InstructionWidth, CommandWrite); s_command.DummyCycles = DummyWrite; /* Configure the read command */ retr = HAL_XSPI_Command(SalXspi->hxspi, &s_command, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); if (retr != HAL_OK) { goto error; } /* Activation of memory-mapped mode */ sMemMappedCfg.TimeOutActivation = HAL_XSPI_TIMEOUT_COUNTER_DISABLE; sMemMappedCfg.TimeoutPeriodClock = 0x50; retr = HAL_XSPI_MemoryMapped(SalXspi->hxspi, &sMemMappedCfg); error: if (retr != HAL_OK) { /* Abort any ongoing transaction for the next action */ (void)HAL_XSPI_Abort(SalXspi->hxspi); } return retr; } /** * @brief This function disables the memory mapped mode * @param SalXspi SAL XSPI handle * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_DisableMapMode(SAL_XSPI_ObjectTypeDef *SalXspi) { __DSB(); return HAL_XSPI_Abort(SalXspi->hxspi); } /** * @brief This function updates the memory according the SFDP signature value * @param SalXspi SAL XSPI handle * @param DataOrder Selected data order * @return @ref HAL_StatusTypeDef **/ HAL_StatusTypeDef SAL_XSPI_UpdateMemoryType(SAL_XSPI_ObjectTypeDef *SalXspi, SAL_XSPI_DataOrderTypeDef DataOrder) { HAL_StatusTypeDef retr = HAL_OK; /* Read the memory type value */ uint32_t memorytype = READ_REG(SalXspi->hxspi->Instance->DCR1) & XSPI_DCR1_MTYP; switch (DataOrder) { case SAL_XSPI_ORDERINVERTED : if (memorytype == HAL_XSPI_MEMTYPE_MICRON) { memorytype = HAL_XSPI_MEMTYPE_MACRONIX; } else if (memorytype == HAL_XSPI_MEMTYPE_MACRONIX) { memorytype = HAL_XSPI_MEMTYPE_MICRON; } else { retr = HAL_ERROR; } MODIFY_REG(SalXspi->hxspi->Instance->DCR1, XSPI_DCR1_MTYP, memorytype); break; default : return HAL_ERROR; break; } DEBUG_PARAM_BEGIN(); DEBUG_PARAM_DATA("::SAL_XSPI_UpdateMemoryType::"); DEBUG_PARAM_INT(memorytype); DEBUG_PARAM_END(); return retr; } HAL_StatusTypeDef SAL_XSPI_Abort(SAL_XSPI_ObjectTypeDef *SalXspi) { return HAL_XSPI_Abort(SalXspi->hxspi); } /** * @} */ /** @defgroup SAL_XSPI_Private_Functions SAL XSP Private Functions * @{ */ /** * @brief This function return a formatted command * * @param CommandExtension Type of the command extension 0: the complement 1 : the same * @param InstructionWidth Instruction width * @param Command Command * @return Formatted command */ uint16_t XSPI_FormatCommand(uint8_t CommandExtension, uint32_t InstructionWidth, uint8_t Command) { uint16_t retr; if (InstructionWidth == HAL_XSPI_INSTRUCTION_16_BITS) { /* 0b00 Command Extension is the same as the Command. (Command / Command Extension has the same value for the whole clock period. */ /* 0b01 Command Extension is the inverse of the Command. Command Extension acts as a confirmation of the Command */ /* 0b11 Command and Command Extension forms a 16 bit command word :: Not yet handled */ retr = ((uint16_t)Command << 8u); if (CommandExtension == 1u) { retr |= (uint8_t)(~Command & 0xFFu); } else { retr |= (uint8_t)(Command & 0xFFu); } } else { retr = Command; } return retr; } /** * @brief This function transmits the data * * @param SalXspi SAL XSPI Handle * @param Data Data pointer * @return Status of the command execution */ HAL_StatusTypeDef XSPI_Transmit(SAL_XSPI_ObjectTypeDef *SalXspi, const uint8_t *Data) { HAL_StatusTypeDef retr; #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) if (SalXspi->hxspi->hdmatx == NULL) #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ { /* Transmit data */ retr = HAL_XSPI_Transmit(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); } #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) else { /* Set completion call back */ salXSPI_status = SALXSPI_TRANSFER_NONE; /* Reception of the data */ retr = HAL_XSPI_Transmit_DMA(SalXspi->hxspi, (uint8_t *)Data); if (retr == HAL_OK) { /* Wait data reception completion */ while (salXSPI_status == SALXSPI_TRANSFER_NONE); if (salXSPI_status == SALXSPI_TRANSFER_ERROR) { retr = HAL_ERROR; } } } #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ return retr; } /** * @brief Start data reception in polling mode or DMA * * @param SalXspi SAL XSPI Handle * @param Data Data pointer * @return Status of the command execution */ HAL_StatusTypeDef XSPI_Receive(SAL_XSPI_ObjectTypeDef *SalXspi, uint8_t *Data) { HAL_StatusTypeDef retr; #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) if (SalXspi->hxspi->hdmarx == NULL) #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ { /* Reception of the data */ retr = HAL_XSPI_Receive(SalXspi->hxspi, Data, SAL_XSPI_TIMEOUT_DEFAULT_VALUE); } #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) else { /* Set completion call back */ salXSPI_status = SALXSPI_TRANSFER_NONE; /* Reception of the data */ retr = HAL_XSPI_Receive_DMA(SalXspi->hxspi, Data); if (retr == HAL_OK) { /* Wait data completion */ while (salXSPI_status == SALXSPI_TRANSFER_NONE); if (salXSPI_status == SALXSPI_TRANSFER_ERROR) { retr = HAL_ERROR; } } } #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ return retr; } #if defined (USE_HAL_XSPI_REGISTER_CALLBACKS) && (USE_HAL_XSPI_REGISTER_CALLBACKS == 1U) /** * @brief This callback is executed when a DMA transfer error occurs * * @param hxspi Handle on the XSPI peripheral * @return none */ void SAL_XSPI_ErrorCallback(struct __XSPI_HandleTypeDef *hxspi) { salXSPI_status = SALXSPI_TRANSFER_ERROR; } /** * @brief this callback is executed when a DMA transfer is complete * * @param hxspi Handle on the XSPI peripheral * @return none */ void SAL_XSPI_CompleteCallback(struct __XSPI_HandleTypeDef *hxspi) { salXSPI_status = SALXSPI_TRANSFER_OK; } #endif /* USE_HAL_XSPI_REGISTER_CALLBACKS */ /** * @} */ /** * @} */ #endif /* EXTMEM_SAL_XSPI == 1 */