/******************************************************************************
* Copyright (C) 2014 - 2022 Xilinx, Inc. All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xqspipsu_options.c
* @addtogroup Overview
* @{
*
* This file implements functions to configure the QSPIPSU component,
* specifically some optional settings, clock and flash related information.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- --- -------- -----------------------------------------------
* 1.0 hk 08/21/14 First release
* sk 03/13/15 Added IO mode support.
* sk 04/24/15 Modified the code according to MISRAC-2012.
* 1.1 sk 04/12/16 Added debug message prints.
* 1.2 nsk 07/01/16 Modified XQspiPsu_SetOptions() to support
* LQSPI options and updated OptionsTable
* rk 07/15/16 Added support for TapDelays at different frequencies.
* 1.7 tjs 01/17/18 Added support to toggle the WP pin of flash. (PR#2448)
* 1.7 tjs 03/14/18 Added support in EL1 NS mode. (CR#974882)
* 1.8 tjs 05/02/18 Added support for IS25LP064 and IS25WP064.
* 1.8 tjs 07/26/18 Resolved cppcheck errors. (CR#1006336)
* 1.9 tjs 04/17/18 Updated register addresses as per the latest revision
* of versal (CR#999610)
* 1.9 aru 01/17/19 Fixes violations according to MISRAC-2012
* in safety mode and modified the code such as
* Added Xil_MemCpy inplace of memcpy,Declared the pointer param
* as Pointer to const, declared XQspi_Set_TapDelay() as static.
* 1.9 akm 03/08/19 Set recommended clock and data tap delay values for 40MHZ,
* 100MHZ and 150MHZ frequencies(CR#1023187)
* 1.10 akm 08/22/19 Set recommended tap delay values for 37.5MHZ, 100MHZ and
* 150MHZ frequencies in Versal.
* 1.11 akm 11/07/19 Removed LQSPI register access in Versal.
* 1.11 akm 11/15/19 Fixed Coverity deadcode warning in
* XQspipsu_Calculate_Tapdelay().
* 1.11 akm 03/09/20 Reorganize the source code, enable qspi controller and
* interrupts in XQspiPsu_CfgInitialize() API.
* 1.13 akm 01/04/21 Fix MISRA-C violations.
* 1.15 akm 03/03/22 Enable tapdelay settings for applications on Microblaze
* platform.
*
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xqspipsu_control.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
/**
* Create the table of options which are processed to get/set the device
* options. These options are table driven to allow easy maintenance and
* expansion of the options.
*/
typedef struct {
u32 Option; /**< Get/Set the device option */
u32 Mask; /**< Mask */
} OptionsMap;
static OptionsMap OptionsTable[] = {
{XQSPIPSU_CLK_ACTIVE_LOW_OPTION, XQSPIPSU_CFG_CLK_POL_MASK},
{XQSPIPSU_CLK_PHASE_1_OPTION, XQSPIPSU_CFG_CLK_PHA_MASK},
{XQSPIPSU_MANUAL_START_OPTION, XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK},
#if !defined (versal)
{XQSPIPSU_LQSPI_MODE_OPTION, XQSPIPSU_CFG_WP_HOLD_MASK},
#endif
};
/**
* Number of options in option table
*/
#define XQSPIPSU_NUM_OPTIONS (sizeof(OptionsTable) / sizeof(OptionsMap))
/*****************************************************************************/
/**
*
* This function sets the options for the QSPIPSU device driver.The options
* control how the device behaves relative to the QSPIPSU bus. The device must be
* idle rather than busy transferring data before setting these device options.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param Options contains the specified options to be set. This is a bit
* mask where a 1 indicates the option should be turned ON and
* a 0 indicates no action. One or more bit values may be
* contained in the mask. See the bit definitions named
* XQSPIPSU_*_OPTIONS in the file xqspipsu.h.
*
* @return
* - XST_SUCCESS if options are successfully set.
* - XST_DEVICE_BUSY if the device is currently transferring data.
* The transfer must complete or be aborted before setting options.
*
* @note
* This function is not thread-safe.
*
******************************************************************************/
s32 XQspiPsu_SetOptions(XQspiPsu *InstancePtr, u32 Options)
{
u32 ConfigReg;
u32 Index;
#if !defined (versal)
u32 QspiPsuOptions;
#endif
s32 Status;
u32 OptionsVal;
OptionsVal = Options;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Do not allow to modify the Control Register while a transfer is in
* progress. Not thread-safe.
*/
if (InstancePtr->IsBusy == (u32)TRUE) {
Status = (s32)XST_DEVICE_BUSY;
} else {
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
#if !defined (versal)
QspiPsuOptions = OptionsVal & XQSPIPSU_LQSPI_MODE_OPTION;
OptionsVal &= (~XQSPIPSU_LQSPI_MODE_OPTION);
#endif
/*
* Loop through the options table, turning the option on
* depending on whether the bit is set in the incoming options flag.
*/
for (Index = 0U; Index < XQSPIPSU_NUM_OPTIONS; Index++) {
if ((OptionsVal & OptionsTable[Index].Option) ==
OptionsTable[Index].Option) {
/* Turn it on */
ConfigReg |= OptionsTable[Index].Mask;
} else {
/* Turn it off */
ConfigReg &= ~(OptionsTable[Index].Mask);
}
}
/*
* Now write the control register. Leave it to the upper layers
* to restart the device.
*/
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
ConfigReg);
if ((OptionsVal & XQSPIPSU_MANUAL_START_OPTION) != (u32)FALSE) {
InstancePtr->IsManualstart = (u8)TRUE;
}
#if !defined (versal)
if ((QspiPsuOptions & XQSPIPSU_LQSPI_MODE_OPTION) != (u32)FALSE) {
if ((Options & XQSPIPSU_LQSPI_LESS_THEN_SIXTEENMB) != (u32)FALSE) {
XQspiPsu_WriteReg(XQSPIPS_BASEADDR,XQSPIPSU_LQSPI_CR_OFFSET,XQSPIPS_LQSPI_CR_RST_STATE);
} else {
XQspiPsu_WriteReg(XQSPIPS_BASEADDR,XQSPIPSU_LQSPI_CR_OFFSET,XQSPIPS_LQSPI_CR_4_BYTE_STATE);
}
XQspiPsu_WriteReg(XQSPIPS_BASEADDR,XQSPIPSU_CFG_OFFSET,XQSPIPS_LQSPI_CFG_RST_STATE);
/* Enable the QSPI controller */
XQspiPsu_WriteReg(XQSPIPS_BASEADDR,XQSPIPSU_EN_OFFSET,XQSPIPSU_EN_MASK);
} else {
/*
* Check for the LQSPI configuration options.
*/
ConfigReg = XQspiPsu_ReadReg(XQSPIPS_BASEADDR,XQSPIPSU_LQSPI_CR_OFFSET);
ConfigReg &= ~(XQSPIPSU_LQSPI_CR_LINEAR_MASK);
XQspiPsu_WriteReg(XQSPIPS_BASEADDR,XQSPIPSU_LQSPI_CR_OFFSET, ConfigReg);
}
#endif
Status = (s32)XST_SUCCESS;
}
return Status;
}
/*****************************************************************************/
/**
*
* This function resets the options for the QSPIPSU device driver.The options
* control how the device behaves relative to the QSPIPSU bus. The device must be
* idle rather than busy transferring data before setting these device options.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param Options contains the specified options to be set. This is a bit
* mask where a 1 indicates the option should be turned OFF and
* a 0 indicates no action. One or more bit values may be
* contained in the mask. See the bit definitions named
* XQSPIPSU_*_OPTIONS in the file xqspipsu.h.
*
* @return
* - XST_SUCCESS if options are successfully set.
* - XST_DEVICE_BUSY if the device is currently transferring data.
* The transfer must complete or be aborted before setting options.
*
* @note
* This function is not thread-safe.
*
******************************************************************************/
s32 XQspiPsu_ClearOptions(XQspiPsu *InstancePtr, u32 Options)
{
u32 ConfigReg;
u32 Index;
s32 Status;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Do not allow to modify the Control Register while a transfer is in
* progress. Not thread-safe.
*/
if (InstancePtr->IsBusy == (u32)TRUE) {
Status = (s32)XST_DEVICE_BUSY;
} else {
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
/*
* Loop through the options table, turning the option on
* depending on whether the bit is set in the incoming options flag.
*/
for (Index = 0U; Index < XQSPIPSU_NUM_OPTIONS; Index++) {
if ((Options & OptionsTable[Index].Option) != (u32)FALSE) {
/* Turn it off */
ConfigReg &= ~OptionsTable[Index].Mask;
}
}
/*
* Now write the control register. Leave it to the upper layers
* to restart the device.
*/
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
ConfigReg);
if ((Options & XQSPIPSU_MANUAL_START_OPTION) != (u32)FALSE) {
InstancePtr->IsManualstart = (u8)FALSE;
}
Status = (s32)XST_SUCCESS;
}
return Status;
}
/*****************************************************************************/
/**
*
* This function gets the options for the QSPIPSU device. The options control how
* the device behaves relative to the QSPIPSU bus.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
*
* @return
*
* Options contains the specified options currently set. This is a bit value
* where a 1 means the option is on, and a 0 means the option is off.
* See the bit definitions named XQSPIPSU_*_OPTIONS in file xqspipsu.h.
*
* @note None.
*
******************************************************************************/
u32 XQspiPsu_GetOptions(const XQspiPsu *InstancePtr)
{
u32 OptionsFlag = 0;
u32 ConfigReg;
u32 Index;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/* Loop through the options table to grab options */
for (Index = 0U; Index < XQSPIPSU_NUM_OPTIONS; Index++) {
/*
* Get the current options from QSPIPSU configuration register.
*/
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
if ((ConfigReg & OptionsTable[Index].Mask) != (u32)FALSE) {
OptionsFlag |= OptionsTable[Index].Option;
}
}
return OptionsFlag;
}
/*****************************************************************************/
/**
*
* Configures the clock according to the prescaler passed.
*
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param Prescaler - clock prescaler to be set.
*
* @return
* - XST_SUCCESS if successful.
* - XST_DEVICE_IS_STARTED if the device is already started.
* - XST_DEVICE_BUSY if the device is currently transferring data.
* It must be stopped to re-initialize.
*
* @note None.
*
******************************************************************************/
s32 XQspiPsu_SetClkPrescaler(const XQspiPsu *InstancePtr, u8 Prescaler)
{
u32 ConfigReg;
s32 Status;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Prescaler <= XQSPIPSU_CR_PRESC_MAXIMUM);
/*
* Do not allow the slave select to change while a transfer is in
* progress. Not thread-safe.
*/
if (InstancePtr->IsBusy == (u32)TRUE) {
Status = (s32)XST_DEVICE_BUSY;
} else {
/*
* Read the configuration register, mask out the relevant bits, and set
* them with the shifted value passed into the function. Write the
* results back to the configuration register.
*/
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
ConfigReg &= ~(u32)XQSPIPSU_CFG_BAUD_RATE_DIV_MASK;
ConfigReg |= (u32) ((u32)Prescaler & (u32)XQSPIPSU_CR_PRESC_MAXIMUM) <<
XQSPIPSU_CFG_BAUD_RATE_DIV_SHIFT;
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
ConfigReg);
#if defined (ARMR5) || defined (__aarch64__) || defined (__MICROBLAZE__)
Status = XQspipsu_Calculate_Tapdelay(InstancePtr,Prescaler);
#else
Status = (s32)XST_SUCCESS;
#endif
}
return Status;
}
/*****************************************************************************/
/**
*
* This function should be used to tell the QSPIPSU driver the HW flash
* configuration being used. This API should be called at least once in the
* application. If desired, it can be called multiple times when switching
* between communicating to different flahs devices/using different configs.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param FlashCS - Flash Chip Select.
* @param FlashBus - Flash Bus (Upper, Lower or Both).
*
* @return
* - XST_SUCCESS if successful.
* - XST_DEVICE_IS_STARTED if the device is already started.
* It must be stopped to re-initialize.
*
* @note If this function is not called at least once in the application,
* the driver assumes there is a single flash connected to the
* lower bus and CS line.
*
******************************************************************************/
void XQspiPsu_SelectFlash(XQspiPsu *InstancePtr, u8 FlashCS, u8 FlashBus)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(FlashCS > 0U);
Xil_AssertVoid(FlashBus > 0U);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
#ifdef DEBUG
xil_printf("\nXQspiPsu_SelectFlash\r\n");
#endif
/*
* Bus and CS lines selected here will be updated in the instance and
* used for subsequent GENFIFO entries during transfer.
*/
/* Choose slave select line */
switch (FlashCS) {
case XQSPIPSU_SELECT_FLASH_CS_BOTH:
InstancePtr->GenFifoCS = (u32)XQSPIPSU_GENFIFO_CS_LOWER |
(u32)XQSPIPSU_GENFIFO_CS_UPPER;
break;
case XQSPIPSU_SELECT_FLASH_CS_UPPER:
InstancePtr->GenFifoCS = XQSPIPSU_GENFIFO_CS_UPPER;
break;
case XQSPIPSU_SELECT_FLASH_CS_LOWER:
InstancePtr->GenFifoCS = XQSPIPSU_GENFIFO_CS_LOWER;
break;
default:
InstancePtr->GenFifoCS = XQSPIPSU_GENFIFO_CS_LOWER;
break;
}
/* Choose bus */
switch (FlashBus) {
case XQSPIPSU_SELECT_FLASH_BUS_BOTH:
InstancePtr->GenFifoBus = (u32)XQSPIPSU_GENFIFO_BUS_LOWER |
(u32)XQSPIPSU_GENFIFO_BUS_UPPER;
break;
case XQSPIPSU_SELECT_FLASH_BUS_UPPER:
InstancePtr->GenFifoBus = XQSPIPSU_GENFIFO_BUS_UPPER;
break;
case XQSPIPSU_SELECT_FLASH_BUS_LOWER:
InstancePtr->GenFifoBus = XQSPIPSU_GENFIFO_BUS_LOWER;
break;
default:
InstancePtr->GenFifoBus = XQSPIPSU_GENFIFO_BUS_LOWER;
break;
}
#ifdef DEBUG
xil_printf("\nGenFifoCS is %08x and GenFifoBus is %08x\r\n",
InstancePtr->GenFifoCS, InstancePtr->GenFifoBus);
#endif
}
/*****************************************************************************/
/**
*
* This function sets the Read mode for the QSPIPSU device driver.The device
* must be idle rather than busy transferring data before setting Read mode
* options.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param Mode contains the specified Mode to be set. See the
* bit definitions named XQSPIPSU_READMODE_* in the file xqspipsu.h.
*
* @return
* - XST_SUCCESS if options are successfully set.
* - XST_DEVICE_BUSY if the device is currently transferring data.
* The transfer must complete or be aborted before setting Mode.
*
* @note
* This function is not thread-safe.
*
******************************************************************************/
s32 XQspiPsu_SetReadMode(XQspiPsu *InstancePtr, u32 Mode)
{
u32 ConfigReg;
s32 Status;
#ifdef DEBUG
xil_printf("\nXQspiPsu_SetReadMode\r\n");
#endif
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid((Mode == XQSPIPSU_READMODE_DMA) || (Mode == XQSPIPSU_READMODE_IO));
/*
* Do not allow to modify the Control Register while a transfer is in
* progress. Not thread-safe.
*/
if (InstancePtr->IsBusy == (u32)TRUE) {
Status = (s32)XST_DEVICE_BUSY;
} else {
InstancePtr->ReadMode = Mode;
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
if (Mode == XQSPIPSU_READMODE_DMA) {
ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK;
ConfigReg |= XQSPIPSU_CFG_MODE_EN_DMA_MASK;
} else {
ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK;
}
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
ConfigReg);
Status = (s32)XST_SUCCESS;
}
#ifdef DEBUG
xil_printf("\nRead Mode is %08x\r\n", InstancePtr->ReadMode);
#endif
return Status;
}
/*****************************************************************************/
/**
*
* This function sets the Write Protect and Hold options for the QSPIPSU device
* driver.The device must be idle rather than busy transferring data before
* setting Write Protect and Hold options.
*
* @param InstancePtr is a pointer to the XQspiPsu instance.
* @param Value of the WP_HOLD bit in configuration register
*
* @return None
*
* @note
* This function is not thread-safe. This function can only be used with single
* flash configuration and x1/x2 data mode. This function cannot be used with
* x4 data mode and dual parallel and stacked flash configuration.
*
******************************************************************************/
void XQspiPsu_SetWP(const XQspiPsu *InstancePtr, u8 Value)
{
u32 ConfigReg;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(InstancePtr->IsBusy != TRUE);
ConfigReg = XQspiPsu_ReadReg(InstancePtr->Config.BaseAddress,
XQSPIPSU_CFG_OFFSET);
ConfigReg |= (u32)((u32)Value << XQSPIPSU_CFG_WP_HOLD_SHIFT);
XQspiPsu_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPSU_CFG_OFFSET,
ConfigReg);
}
/** @} */