diff --git a/XilinxProcessorIPLib/drivers/qspips/data/qspips.mdd b/XilinxProcessorIPLib/drivers/qspips/data/qspips.mdd new file mode 100755 index 00000000..a9acc102 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/data/qspips.mdd @@ -0,0 +1,42 @@ +############################################################################### +# +# Copyright (C) 2011 - 2014 Xilinx, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Use of the Software is limited solely to applications: +# (a) running on a Xilinx device, or +# (b) that interact with a Xilinx device through a bus or interconnect. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Except as contained in this notice, the name of the Xilinx shall not be used +# in advertising or otherwise to promote the sale, use or other dealings in +# this Software without prior written authorization from Xilinx. +# +############################################################################### +OPTION psf_version = 2.1; + +BEGIN driver qspips + + OPTION supported_peripherals = (ps7_qspi); + OPTION driver_state = ACTIVE; + OPTION copyfiles = all; + OPTION VERSION = 3.2; + OPTION NAME = qspips; + +END driver diff --git a/XilinxProcessorIPLib/drivers/qspips/data/qspips.tcl b/XilinxProcessorIPLib/drivers/qspips/data/qspips.tcl new file mode 100755 index 00000000..038bf229 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/data/qspips.tcl @@ -0,0 +1,52 @@ +############################################################################### +# +# Copyright (C) 2011 - 2014 Xilinx, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Use of the Software is limited solely to applications: +# (a) running on a Xilinx device, or +# (b) that interact with a Xilinx device through a bus or interconnect. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Except as contained in this notice, the name of the Xilinx shall not be used +# in advertising or otherwise to promote the sale, use or other dealings in +# this Software without prior written authorization from Xilinx. +# +############################################################################### +############################################################################## +# +# Modification History +# +# Ver Who Date Changes +# ----- ---- -------- ----------------------------------------------- +# 1.00a sdm 11/22/11 Created +# 2.02a hk 26/03/13 Added C_QSPI_MODE +# +############################################################################## + +#uses "xillib.tcl" + +proc generate {drv_handle} { + ::hsi::utils::define_zynq_include_file $drv_handle "xparameters.h" "XQspiPs" "NUM_INSTANCES" "DEVICE_ID" "C_S_AXI_BASEADDR" "C_S_AXI_HIGHADDR" "C_QSPI_CLK_FREQ_HZ" "C_QSPI_MODE" + + ::hsi::utils::define_zynq_config_file $drv_handle "xqspips_g.c" "XQspiPs" "DEVICE_ID" "C_S_AXI_BASEADDR" "C_QSPI_CLK_FREQ_HZ" "C_QSPI_MODE" + + ::hsi::utils::define_zynq_canonical_xpars $drv_handle "xparameters.h" "XQspiPs" "DEVICE_ID" "C_S_AXI_BASEADDR" "C_S_AXI_HIGHADDR" "C_QSPI_CLK_FREQ_HZ" "C_QSPI_MODE" + +} diff --git a/XilinxProcessorIPLib/drivers/qspips/data/qspips_header.h b/XilinxProcessorIPLib/drivers/qspips/data/qspips_header.h new file mode 100644 index 00000000..728ae396 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/data/qspips_header.h @@ -0,0 +1,40 @@ +/****************************************************************************** +* +* Copyright (C) 2011 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +#ifndef QSPIPS_HEADER_H /* prevent circular inclusions */ +#define QSPIPS_HEADER_H /* by using protection macros */ + +#include "xil_types.h" +#include "xil_assert.h" +#include "xstatus.h" + +int QspiPsSelfTestExample(u16 DeviceId); +#endif diff --git a/XilinxProcessorIPLib/drivers/qspips/data/qspips_tapp.tcl b/XilinxProcessorIPLib/drivers/qspips/data/qspips_tapp.tcl new file mode 100755 index 00000000..55b94b77 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/data/qspips_tapp.tcl @@ -0,0 +1,139 @@ +############################################################################### +# +# Copyright (C) 2011 - 2014 Xilinx, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# Use of the Software is limited solely to applications: +# (a) running on a Xilinx device, or +# (b) that interact with a Xilinx device through a bus or interconnect. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Except as contained in this notice, the name of the Xilinx shall not be used +# in advertising or otherwise to promote the sale, use or other dealings in +# this Software without prior written authorization from Xilinx. +# +############################################################################### +############################################################################### +# +# MODIFICATION HISTORY: +# Ver Who Date Changes +# -------- ------ -------- ------------------------------------ +# 3.0 adk 12/10/13 Updated as per the New Tcl API's +############################################################################## + +# Uses $XILINX_EDK/bin/lib/xillib_sw.tcl +# ----------------------------------------------------------------- +# Software Project Types (swproj): +# 0 : MemoryTest - Calls basic memorytest routines from common driver dir +# 1 : PeripheralTest - Calls any existing polled_example and/or selftest +# ----------------------------------------------------------------- + +# ----------------------------------------------------------------- +# TCL Procedures: +# ----------------------------------------------------------------- + +proc gen_include_files {swproj mhsinst} { + if {$swproj == 0} { + return "" + } + if {$swproj == 1} { + set inc_file_lines { xqspips.h qspips_header.h} + } + return $inc_file_lines +} + +proc gen_src_files {swproj mhsinst} { + if {$swproj == 0} { + return "" + } + if {$swproj == 1} { + + set inc_file_lines {examples/xqspips_selftest_example.c data/qspips_header.h} + + return $inc_file_lines + } +} + +proc gen_testfunc_def {swproj mhsinst} { + return "" +} + +proc gen_init_code {swproj mhsinst} { + + if {$swproj == 0} { + return "" + } + if {$swproj == 1} { + return "" + } + +} + +proc gen_testfunc_call {swproj mhsinst} { + + if {$swproj == 0} { + return "" + } + + set ipname [common::get_property NAME $mhsinst] + set deviceid [::hsi::utils::get_ip_param_name $mhsinst "DEVICE_ID"] + set stdout [common::get_property CONFIG.STDOUT [hsi::get_os]] + if { $stdout == "" || $stdout == "none" } { + set hasStdout 0 + } else { + set hasStdout 1 + } + + set testfunc_call "" + + if {${hasStdout} == 0} { + + append testfunc_call " + + { + int Status; + + Status = QspiPsSelfTestExample(${deviceid}); + + }" + + + } else { + + append testfunc_call " + + { + int Status; + + print(\"\\r\\n Running QspiSelfTestExample() for ${ipname}...\\r\\n\"); + + Status = QspiPsSelfTestExample(${deviceid}); + + if (Status == 0) { + print(\"QspiPsSelfTestExample PASSED\\r\\n\"); + } + else { + print(\"QspiPsSelfTestExample FAILED\\r\\n\"); + } + }" + + } + + return $testfunc_call +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/index.html b/XilinxProcessorIPLib/drivers/qspips/examples/index.html new file mode 100755 index 00000000..0320e7e2 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/index.html @@ -0,0 +1,23 @@ + + +
+ + +Copyright � 1995-2014 Xilinx, Inc. All rights reserved.
+ + diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_lqspi_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_lqspi_example.c new file mode 100644 index 00000000..320cd0dd --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_lqspi_example.c @@ -0,0 +1,642 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_dual_flash_lqspi_example.c +* +* This file contains a design example using the QSPI driver (XQspiPs) in +* Linear QSPI mode, with two serial FLASH devices on seperate buses. With +* two flash memories on seperate buses, even numbered bits in data words are +* written to the lower memory and odd numbered bits are written to the upper +* memory. This example writes to the two flash memories in QSPI mode and reads +* the data back from the flash memories, in Linear QSPI mode. +* It is recommended to use Manual CS + Auto start for best performance. +* +* The hardware which this example runs on, must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.00a kka 22/08/12 Updated the example as XQspiPs_PolledTransfer API has +* changed. Changed the prescalar to use divide by 8. +* The user can change the prescalar to a maximum of +* divide by 2 based on the reference clock in the +* system. +* Set the Holdb_dr bit in the configuration register using +* XQSPIPS_HOLD_B_DRIVE_OPTION. Setting this bit +* drives the HOLD bit of the QSPI controller. +* This is required for QSPI to be used in Non QSPI boot +* mode else there needs to be an external pullup on this +* line. +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID + +/* + * The following constants define the commands which may be sent to the FLASH + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* FLASH instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ + +/* + * The following constants specify the extra bytes which are sent to the + * FLASH on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * The following constants specify the page size, sector size, and number of + * pages and sectors for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define SECTOR_SIZE 0x20000 /* half from each flash */ +#define NUM_SECTORS 0x100 +#define NUM_PAGES 0x10000 +#define PAGE_SIZE 512 /* 256 bytes from each flash */ + +/* + * The following defines are for dual flash interface. + */ +#define LQSPI_CR_FAST_QUAD_READ 0x0000006B /* Fast Quad Read output */ +#define LQSPI_CR_1_DUMMY_BYTE 0x00000100 /* 1 Dummy Byte between + address and return data */ +#define DUAL_QSPI_CONFIG_WRITE (XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + XQSPIPS_LQSPI_CR_SEP_BUS_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +#define DUAL_QSPI_CONFIG_QUAD_READ (XQSPIPS_LQSPI_CR_LINEAR_MASK | \ + XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + XQSPIPS_LQSPI_CR_SEP_BUS_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 32 + +/* + * Flash address to which data is ot be written. + */ +#define TEST_ADDRESS 0x0000 +#define UNIQUE_VALUE 0x3 +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +int LinearQspiFlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId); + +/************************** Variable Definitions *****************************/ + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XQspiPs QspiInstance; + +/* + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test = 0x2; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE]; +u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET]; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("Linear QSPI Dual FLASH Example Test \r\n"); + + /* + * Run the Qspi Interrupt example. + */ + Status = LinearQspiFlashExample(&QspiInstance, QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("Linear QSPI Dual FLASH Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran Linear QSPI Dual FLASH Example Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver in Linear mode. This function writes data to the serial +* FLASH in QSPI mode and reads data in Linear QSPI mode. +* +* @param None. +* +* @return XST_SUCCESS if successful, else XST_FAILURE. +* +* @note None. +* +*****************************************************************************/ +int LinearQspiFlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId) +{ + int Status; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Enable two flash memories on seperate buses + */ + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_CONFIG_WRITE); + + /* + * Set the QSPI device as a master and enable manual CS, manual start + * and flash interface mode options and drive HOLD_B pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Assert the FLASH chip select. + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + /* + * Erase the flash sectors + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write data to the two flash memories on seperate buses, starting from + * TEST_ADDRESS. This is same as writing to a single flash memory. The + * LQSPI controller takes care of splitting the data words and writing + * them to the two flash memories. The user needs to take care of the + * address translation + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, ((Page * PAGE_SIZE) + + TEST_ADDRESS) / 2, PAGE_SIZE, WRITE_CMD); + } + + /* + * Read from the two flash memories on seperate buses in LQSPI mode. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_CONFIG_QUAD_READ); + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != WriteBuffer[DATA_OFFSET + + (Count % PAGE_SIZE)]) { + return XST_FAILURE; + } + } + + /* + * Set the QSPI device as a master and enable manual CS, manual start + * and flash interface mode options and drive HOLD_B pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Erase the flash sectors + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write data to the two flash memories on seperate buses, starting from + * TEST_ADDRESS. This is same as writing to a single flash memory. The + * LQSPI controller takes care of splitting the data words and writing + * them to the two flash memories. The user needs to take care of the + * address translation + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, ((Page * PAGE_SIZE) + + TEST_ADDRESS) / 2, PAGE_SIZE, WRITE_CMD); + } + + /* + * Read from the two flash memories on seperate buses in LQSPI mode. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_CONFIG_QUAD_READ); + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != WriteBuffer[DATA_OFFSET + + (Count % PAGE_SIZE)]) { + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* +* This function writes to the serial FLASH connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer before + * the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the write command, address, and data to the FLASH to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + ByteCount + OVERHEAD_SIZE); + + /* + * Wait for the write command to the FLASH to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the FLASH to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + u32 RealAddr; + int Sector; + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + return; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) { + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a seperate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * The effective sector address in each flash is the actual + * address / 2 + */ + RealAddr = Address / 2; + + /* + * Setup the write command with the specified address and data + * for the FLASH + */ + WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)(RealAddr >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)(RealAddr >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + SEC_ERASE_SIZE); + + /* + * Wait for the sector erse command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + Address += SECTOR_SIZE; + } +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_stack_lqspi_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_stack_lqspi_example.c new file mode 100644 index 00000000..685cf3d7 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_dual_flash_stack_lqspi_example.c @@ -0,0 +1,918 @@ +/****************************************************************************** +* +* Copyright (C) 2013 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_dual_flash_lqspi_example.c +* +* This file contains a design example using the QSPI driver (XQspiPs) in +* Linear QSPI mode, with two serial Flash devices in stacked mode. +* One flash s accessed at a time on a common bus by using separate selects. +* This example writes to the two flash memories in QSPI mode and reads +* the data back from the flash memories, in Linear QSPI mode. +* It is recommended to use Manual CS + Auto start for best performance. +* +* The hardware which this example runs on, must have a serial Flash (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Winbond Serial Flash (W25Q128) and Spansion S25FL. +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 2.02a hk 05/07/13 First release +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID + +/* + * The following constants define the commands which may be sent to the Flash + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F +#define READ_CONFIG_CMD 0x35 +#define WRITE_CONFIG_CMD 0x01 + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* Flash instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ +#define RD_CFG_SIZE 2 /* 1 byte Configuration register + RD CFG command*/ +#define WR_CFG_SIZE 3 /* WRR command + 1 byte each Status and Config Reg*/ + +/* + * The following constants specify the extra bytes which are sent to the + * Flash on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * The following constants specify the page size, sector size, and number of + * pages and sectors for the Flash. The page size specifies a max number of + * bytes that can be written to the Flash with a single transfer. + */ +#define SECTOR_SIZE 0x10000 +#define NUM_SECTORS 0x200 /* 100 sectors from each flash */ +#define NUM_PAGES 0x20000 /* 10000 pages from each flash */ +#define PAGE_SIZE 256 + +/* + * The following defines are for dual flash stacked mode interface. + */ +#define LQSPI_CR_FAST_QUAD_READ 0x0000006B /* Fast Quad Read output */ +#define LQSPI_CR_1_DUMMY_BYTE 0x00000100 /* 1 Dummy Byte between + address and return data */ +#define LQSPI_CR_FAST_READ 0x0000000B /* Fast Read */ +#define LQSPI_CR_NORMAL_READ 0x00000003 /* Fast Read */ + +#define DUAL_STACK_CONFIG_WRITE (XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +#define DUAL_STACK_CONFIG_QUAD_READ (XQSPIPS_LQSPI_CR_LINEAR_MASK | \ + XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 16 + +/* + * Flash address to which data is to be written. + */ +/* + * Test address defined so as to cover both flash devices + */ +#define TEST_ADDRESS 0xFFF800 + + +#define UNIQUE_VALUE 0x0C +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the Flash. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE + +/* + * Base address of Flash1 and Flash2 + */ +#define FLASH1BASE 0x0000000 +#define FLASH2BASE 0x1000000 + +/* + * Mask for sector start address + */ +#define SECTORMASK 0xFFF0000 + + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +int DualStackExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId); + + + +/************************** Variable Definitions *****************************/ + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XQspiPs QspiInstance; + +/* + * The following variable allows a test value to be added to the values that + * are written to the Flash such that unique values can be generated to + * guarantee the writes to the Flash were successful + */ +int Test = 2; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE]; +u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET]; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Dual Stack example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPI Dual Stack Example Test \r\n"); + + /* + * Run the QSPI Dual Stack example. + */ + Status = DualStackExample(&QspiInstance, QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPI Dual Stack Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPI Dual Stack Example Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver with two Flash devices in Stacked mode. +* This function writes data to the serial Flash in QSPI mode and +* reads data in Linear QSPI mode. +* +* @param None. +* +* @return XST_SUCCESS if successful, else XST_FAILURE. +* +* @note None. +* +*****************************************************************************/ +int DualStackExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId) +{ + int Status; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Check if two flash devices are connected in stacked mode + */ + if(QspiConfig->ConnectionMode != XQSPIPS_CONNECTION_MODE_STACKED) { + xil_printf("QSPI not connected in Stacked Configuration \n"); + return XST_FAILURE; + } + + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + /* + * Set the QSPI device as a master and enable manual CS, manual start + * and flash interface mode options and drive HOLD_B pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Enable two flash memories, shared bus (NOT separate bus0, + * L_PAGE selected by default + */ + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_STACK_CONFIG_WRITE); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + /* + * Initialize the write buffer for a pattern to write to the Flash + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + + + /* + * Erase the flash sectors + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * The two stacked 16MB memories are equivalent to one continuous 32MB. + * Write data to the two flash memories on the same bus one at a time, + * starting from TEST_ADDRESS. Flash_Write and Flash_Erase functions + * will select the lower or upper memory based on the address. + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, ((Page * PAGE_SIZE) + + TEST_ADDRESS), PAGE_SIZE, WRITE_CMD); + } + + + + /* + * Read from the two flash memories one after the other using + * LQSPI mode. The LQSPI controller takes care of selecting + * the lower or upper memory based on address bit 25. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_STACK_CONFIG_QUAD_READ); + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + + return XST_FAILURE; + } + } + + /* + * Set the QSPI device as a master and enable manual CS, manual start + * and flash interface mode options and drive HOLD_B pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Initialize the write buffer for a pattern to write to the Flash + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Erase the flash sectors + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * The two stacked 16MB memories are equivalent to one continuous 32MB. + * Write data to the two flash memories on the same bus one at a time, + * starting from TEST_ADDRESS. Flash_Write and Flash_Erase functions + * will select the lower or upper memory based on the address. + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, ((Page * PAGE_SIZE) + + TEST_ADDRESS), PAGE_SIZE, WRITE_CMD); + } + + /* + * Read from the two flash memories one after the other using + * LQSPI mode. The LQSPI controller takes care of selecting + * the lower or upper memory based on address bit 25. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_STACK_CONFIG_QUAD_READ); + + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* +* This function writes to the serial Flash connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the Flash. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* Must send 2 bytes */ + u8 FlashStatus[2]; + u32 LqspiCr; + u32 RealAddr; + + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* Select lower or upper Flash based on address */ + if(Address & FLASH2BASE) { + /* + * Set selection to U_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr | XQSPIPS_LQSPI_CR_U_PAGE_MASK); + + /* + * Subtract 16MB when accessing second flash + */ + RealAddr = Address & (~FLASH2BASE); + + }else{ + + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + RealAddr = Address; + } + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer before + * the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + + /* + * Setup the write command with the specified address and data for the + * Flash + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + /* + * Send the write command, address, and data to the Flash to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + ByteCount + OVERHEAD_SIZE); + + /* + * Wait for the write command to the Flash to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the Flash to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial Flash connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* Must send 2 bytes */ + u8 FlashStatus[2]; + u32 RealAddr; + int Sector; + u32 LqspiCr; + u32 NumSect; + + /* + * If erase size is same as the total size of the two flash devices, + * use bulk erase command on both flash devices. + */ + if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { + + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* + * Set selection to U_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr | XQSPIPS_LQSPI_CR_U_PAGE_MASK); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + return; + } + + /* + * If entire first flash needs + some sectors in the second flash + * need to be erased, then bulk erase first flash here and + * second flash sectors will be erased in the following loop. + */ + if ((Address == FLASH1BASE) && + (ByteCount == (NUM_SECTORS * SECTOR_SIZE) / 2)) { + + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + /* + * Increment address to second flash + */ + Address = FLASH2BASE; + } + + /* + * If the erase size is less than the total size of the either flash, + * use sector erase command. + */ + /* + * Calculate no. of sectors to erase based on byte count + */ + NumSect = ByteCount/SECTOR_SIZE + 1; + + /* + * If ByteCount to k sectors, + * but the address range spans from N to N+k+1 sectors, then + * increment no. of sectors to be erased + */ + if( ((Address + ByteCount) & SECTORMASK) == + ((Address + (NumSect*SECTOR_SIZE)) & SECTORMASK) ) { + NumSect++; + } + + /* + * Check condition - This is for the case when the byte count is + * less than a sector but the address range spreads over two sectors + */ + for (Sector = 0; Sector < NumSect; Sector++) { + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + + /* Select lower or upper Flash based on sector address */ + if(Address & FLASH2BASE) { + /* + * Set selection to U_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr | XQSPIPS_LQSPI_CR_U_PAGE_MASK); + + /* + * Subtract 16MB when accessing second flash + */ + RealAddr = Address & (~FLASH2BASE); + }else{ + + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + RealAddr = Address; + } + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a separate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the write command with the specified address and data + * for the Flash + */ + WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)(RealAddr >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)(RealAddr >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + SEC_ERASE_SIZE); + + /* + * Wait for the sector erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + Address += SECTOR_SIZE; + + } +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_intr_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_intr_example.c new file mode 100644 index 00000000..5d977b82 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_intr_example.c @@ -0,0 +1,1046 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_flash_intr_example.c +* +* +* This file contains a design example using the QSPI driver (XQspiPs) in +* interrupt mode with a serial FLASH device. This examples performs +* some transfers in Manual Chip Select and Start mode. +* It is recommended to use Manual CS + Auto start for best performance. +* The hardware which this example runs on, must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 1.01 srt 06/12/12 Changed to meet frequency requirements of READ command +* for CR 663787 +* 2.00a kka 22/08/12 Updated the example as XQspiPs_Transfer API has +* changed. Changed the prescalar to use divide by 8. +* The user can change the prescalar to a maximum of +* divide by 2 based on the reference clock in the +* system. +* Set the Holdb_dr bit in the configuration register using +* XQSPIPS_HOLD_B_DRIVE_OPTION. Setting this bit +* drives the HOLD bit of the QSPI controller. +* This is required for QSPI to be used in Non QSPI boot +* mode else there needs to be an external pullup on this +* line. See http://www.xilinx.com/support/answers/47596.htm +* 2.01a sg 02/03/13 Created a function FlashReadID. Removed multiple +* initialization using SetOptions. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xscugic.h" /* Interrupt controller device driver */ +#include "xil_exception.h" +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID +#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID +#define QSPI_INTR_ID XPAR_XQSPIPS_0_INTR + +/* + * The following constants define the commands which may be sent to the FLASH + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* FLASH instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ + +/* + * The following constants specify the extra bytes which are sent to the + * FLASH on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * The following constants specify the page size, sector size, and number of + * pages and sectors for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define SECTOR_SIZE 0x10000 +#define NUM_SECTORS 0x100 +#define NUM_PAGES 0x10000 +#define PAGE_SIZE 256 + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 16 + +/* + * Flash address to which data is ot be written. + */ +#define TEST_ADDRESS 0x00090000 +#define UNIQUE_VALUE 0x05 +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static int QspiSetupIntrSystem(XScuGic *IntcInstancePtr, + XQspiPs *QspiInstancePtr, u16 QspiIntrId); + +static void QspiDisableIntrSystem(XScuGic *IntcInstancePtr, u16 QspiIntrId); + +void QspiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount); + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +int FlashReadID(void); + +int QspiFlashIntrExample(XScuGic *IntcInstancePtr, XQspiPs *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId); + +/************************** Variable Definitions *****************************/ + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XScuGic IntcInstance; +static XQspiPs QspiInstance; + +/* + * The following variables are shared between non-interrupt processing and + * interrupt processing such that they must be global. + */ +volatile int TransferInProgress; + +/* + * The following variable tracks any errors that occur during interrupt + * processing + */ +int Error; + +/* + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test = 0xF; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE]; +u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET]; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPI FLASH Interrupt Example Test \r\n"); + + /* + * Run the Qspi Interrupt example. + */ + Status = QspiFlashIntrExample(&IntcInstance, &QspiInstance, + QSPI_DEVICE_ID, QSPI_INTR_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPI FLASH Interrupt Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPI FLASH Interrupt Example Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver in interrupt mode. This function writes and reads data +* from a serial FLASH. +* +* @param None. +* +* @return XST_SUCCESS if successful else XST_FAILURE. +* +* @note +* +* This function calls other functions which contain loops that may be infinite +* if interrupts are not working such that it may not return. If the device +* slave select is not correct and the device is not responding on bus it will +* read a status of 0xFF for the status register as the bus is pulled up. +* +*****************************************************************************/ +int QspiFlashIntrExample(XScuGic *IntcInstancePtr, XQspiPs *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId) +{ + int Status; + u8 *BufferPtr; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Connect the Qspi device to the interrupt subsystem such that + * interrupts can occur. This function is application specific + */ + Status = QspiSetupIntrSystem(IntcInstancePtr, QspiInstancePtr, + QspiIntrId); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup the handler for the QSPI that will be called from the + * interrupt context when an QSPI status occurs, specify a pointer to + * the QSPI driver instance as the callback reference so the handler is + * able to access the instance data + */ + XQspiPs_SetStatusHandler(QspiInstancePtr, QspiInstancePtr, + (XQspiPs_StatusHandler) QspiHandler); + + /* + * Set Manual Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Set the operating clock frequency using the clock divider + */ + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + + /* + * Assert the FLASH chip select + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + FlashReadID(); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Normal Read + * command. + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Fast Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, FAST_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Dual Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, DUAL_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Quad Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Set Auto Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Normal Read + * command. + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Fast Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, FAST_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Dual Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, DUAL_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Quad Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + QspiDisableIntrSystem(IntcInstancePtr, QspiIntrId); + return XST_SUCCESS; +} + +/****************************************************************************** +* +* This function is the handler which performs processing for the QSPI driver. +* It is called from an interrupt context such that the amount of processing +* performed should be minimized. It is called when a transfer of QSPI data +* completes or an error occurs. +* +* This handler provides an example of how to handle QSPI interrupts but is +* application specific. +* +* @param CallBackRef is a reference passed to the handler. +* @param StatusEvent is the status of the QSPI . +* @param ByteCount is the number of bytes transferred. +* +* @return None +* +* @note None. +* +******************************************************************************/ +void QspiHandler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount) +{ + /* + * Indicate the transfer on the QSPI bus is no longer in progress + * regardless of the status event + */ + TransferInProgress = FALSE; + + /* + * If the event was not transfer done, then track it as an error + */ + if (StatusEvent != XST_SPI_TRANSFER_DONE) { + Error++; + } +} + +/****************************************************************************** +* +* +* This function writes to the serial FLASH connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer before + * the write + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete before + * proceeding + */ + while (TransferInProgress); + + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the write command, address, and data to the FLASH to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + TransferInProgress = TRUE; + XQspiPs_Transfer(QspiPtr, WriteBuffer, NULL, + ByteCount + OVERHEAD_SIZE); + + while (TransferInProgress); + + /* + * Wait for the write command to the FLASH to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the FLASH to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete before + * proceeding + */ + while (TransferInProgress); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* This function reads from the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to read data from in the FLASH. +* @param ByteCount contains the number of bytes to read. +* @param Command is the command used to read data from the flash. QSPI +* device supports one of the Read, Fast Read, Dual Read and Fast +* Read commands to read data from the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || + (Command == QUAD_READ_CMD)) { + ByteCount += DUMMY_SIZE; + } + /* + * Send the read command to the FLASH to read the specified number + * of bytes from the FLASH, send the read command and address and + * receive the specified number of bytes of data in the data buffer + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, WriteBuffer, ReadBuffer, + ByteCount + OVERHEAD_SIZE); + + /* + * Wait for the transfer on the QSPI bus to be complete before + * proceeding + */ + while (TransferInProgress); +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + int Sector; + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer + * before the erase + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete before + * proceeding + */ + while (TransferInProgress); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + TransferInProgress = TRUE; + XQspiPs_Transfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + while (TransferInProgress); + + /* + * Wait for the erase command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete + * before proceeding + */ + while (TransferInProgress); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + return; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) { + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a seperate transfer + * before the write + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete before + * proceeding + */ + while (TransferInProgress); + + /* + * Setup the write command with the specified address and data + * for the FLASH + */ + WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + TransferInProgress = TRUE; + XQspiPs_Transfer(QspiPtr, WriteBuffer, NULL, + SEC_ERASE_SIZE); + + while (TransferInProgress); + + /* + * Wait for the sector erse command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + TransferInProgress = TRUE; + + XQspiPs_Transfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * Wait for the transfer on the QSPI bus to be complete + * before proceeding + */ + while (TransferInProgress); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + Address += SECTOR_SIZE; + } +} + +/****************************************************************************** +* +* This function reads serial FLASH ID connected to the SPI interface. +* +* @param None. +* +* @return XST_SUCCESS if read id, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +int FlashReadID(void) +{ + /* + * Read ID + */ + WriteBuffer[COMMAND_OFFSET] = READ_ID; + WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ + WriteBuffer[ADDRESS_2_OFFSET] = 0x08; + WriteBuffer[ADDRESS_3_OFFSET] = 0x09; + + TransferInProgress = TRUE; + + XQspiPs_Transfer(&QspiInstance, WriteBuffer, ReadBuffer, + RD_ID_SIZE); + + while (TransferInProgress); + + xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2], + ReadBuffer[3]); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function setups the interrupt system for an Qspi device. +* +* @param IntcInstancePtr is a pointer to the instance of the Intc device. +* @param QspiInstancePtr is a pointer to the instance of the Qspi device. +* @param QspiIntrId is the interrupt Id for an QSPI device. +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +static int QspiSetupIntrSystem(XScuGic *IntcInstancePtr, + XQspiPs *QspiInstancePtr, u16 QspiIntrId) +{ + int Status; + + XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */ + + Xil_ExceptionInit(); + + /* + * Initialize the interrupt controller driver so that it is ready to + * use. + */ + IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); + if (NULL == IntcConfig) { + return XST_FAILURE; + } + + Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, + IntcConfig->CpuBaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Connect the interrupt controller interrupt handler to the hardware + * interrupt handling logic in the processor. + */ + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, + (Xil_ExceptionHandler)XScuGic_InterruptHandler, + IntcInstancePtr); + + /* + * Connect the device driver handler that will be called when an + * interrupt for the device occurs, the handler defined above performs + * the specific interrupt processing for the device. + */ + Status = XScuGic_Connect(IntcInstancePtr, QspiIntrId, + (Xil_ExceptionHandler)XQspiPs_InterruptHandler, + (void *)QspiInstancePtr); + if (Status != XST_SUCCESS) { + return Status; + } + + /* + * Enable the interrupt for the Qspi device. + */ + XScuGic_Enable(IntcInstancePtr, QspiIntrId); + + /* + * Enable interrupts in the Processor. + */ + Xil_ExceptionEnable(); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function disables the interrupts that occur for the Qspi device. +* +* @param IntcInstancePtr is the pointer to an INTC instance. +* @param QspiIntrId is the interrupt Id for an QSPI device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +static void QspiDisableIntrSystem(XScuGic *IntcInstancePtr, u16 QspiIntrId) +{ + /* + * Disable the interrupt for the QSPI device. + */ + XScuGic_Disable(IntcInstancePtr, QspiIntrId); + + /* + * Disconnect and disable the interrupt for the Qspi device. + */ + XScuGic_Disconnect(IntcInstancePtr, QspiIntrId); +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_lqspi_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_lqspi_example.c new file mode 100644 index 00000000..6233c504 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_lqspi_example.c @@ -0,0 +1,644 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_flash_lqspi_example.c +* +* +* This file contains a design example using the QSPI driver (XQspiPs) in +* Linear QSPI mode with a serial FLASH device. The example writes to the flash +* in QSPI mode and reads it back in Linear QSPI mode. This examples performs +* some transfers in Auto mode and Manual start mode, to illustrate the modes +* available. It is recommended to use Manual CS + Auto start for +* best performance. +* The hardware which this example runs on, must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.00a kka 22/08/12 Updated the example as XQspiPs_PolledTransfer API has +* changed. Changed the prescalar to use divide by 8. +* The user can change the prescalar to a maximum of +* divide by 2 based on the reference clock in the +* system. +* Set the Holdb_dr bit in the configuration register using +* XQSPIPS_HOLD_B_DRIVE_OPTION. Setting this bit +* drives the HOLD bit of the QSPI controller. +* This is required for QSPI to be used in Non QSPI boot +* mode else there needs to be an external pullup on this +* line. See http://www.xilinx.com/support/answers/47596.htm +* 2.01a sg 02/03/13 Created a function FlashReadID. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID + +/* + * The following constants define the commands which may be sent to the FLASH + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* FLASH instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ + +/* + * The following constants specify the extra bytes which are sent to the + * FLASH on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * The following constants specify the page size, sector size, and number of + * pages and sectors for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define SECTOR_SIZE 0x10000 +#define NUM_SECTORS 0x100 +#define NUM_PAGES 0x10000 +#define PAGE_SIZE 256 + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 16 + +/* + * Flash address to which data is ot be written. + */ +#define TEST_ADDRESS 0x00055000 +#define UNIQUE_VALUE 0x05 +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +int FlashReadID(void); + +int LinearQspiFlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId); + +/************************** Variable Definitions *****************************/ + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XQspiPs QspiInstance; + +/* + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test = 5; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE]; +u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET]; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("Linear QSPI FLASH Example Test \r\n"); + + /* + * Run the Qspi Interrupt example. + */ + Status = LinearQspiFlashExample(&QspiInstance, QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("Linear QSPI FLASH Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran Linear QSPI FLASH Example Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver in Linear mode. This function writes data to the serial +* FLASH in QSPI mode and reads data in Linear QSPI mode. +* +* @param None. +* +* @return XST_SUCCESS if successful, else XST_FAILURE. +* +* @note None. +* +*****************************************************************************/ +int LinearQspiFlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId) +{ + int Status; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Set the prescaler for QSPI clock + */ + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + + /* + * Set Manual Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Assert the FLASH chip select. + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + FlashReadID(); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read from the flash in LQSPI mode. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Set Auto Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read from the flash in LQSPI mode. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_LQSPI_MODE_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + Status = XQspiPs_LqspiRead(QspiInstancePtr, ReadBuffer, TEST_ADDRESS, + MAX_DATA); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* +* This function writes to the serial FLASH connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer before + * the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the write command, address, and data to the FLASH to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + ByteCount + OVERHEAD_SIZE); + + /* + * Wait for the write command to the FLASH to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the FLASH to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + int Sector; + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + return; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) { + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a seperate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the write command with the specified address and data + * for the FLASH + */ + WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + SEC_ERASE_SIZE); + + /* + * Wait for the sector erse command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + Address += SECTOR_SIZE; + } +} + +/****************************************************************************** +* +* This function reads serial FLASH ID connected to the SPI interface. +* +* @param None. +* +* @return XST_SUCCESS if read id, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +int FlashReadID(void) +{ + int Status; + + /* + * Read ID in Auto mode. + */ + WriteBuffer[COMMAND_OFFSET] = READ_ID; + WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ + WriteBuffer[ADDRESS_2_OFFSET] = 0x08; + WriteBuffer[ADDRESS_3_OFFSET] = 0x09; + + Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer, + RD_ID_SIZE); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2], + ReadBuffer[3]); + + return XST_SUCCESS; +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_polled_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_polled_example.c new file mode 100644 index 00000000..fd94b9ee --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_flash_polled_example.c @@ -0,0 +1,806 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_flash_polled_example.c +* +* +* This file contains a design example using the QSPI driver (XQspiPs) in +* polled mode with a serial FLASH device. This examples performs +* some transfers in Auto mode and Manual start mode, to illustrate the modes +* available. It is recommended to use Manual CS + Auto start for +* best performance. +* The hardware which this example runs on. must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 1.01 srt 06/12/12 Changed to meet frequency requirements of READ command +* for CR 663787 +* 2.00a kka 22/08/12 Updated the example as XQspiPs_PolledTransfer API has +* changed. Changed the prescalar to use divide by 8. +* The user can change the prescalar to a maximum of +* divide by 2 based on the reference clock in the +* system. +* Set the Holdb_dr bit in the configuration register using +* XQSPIPS_HOLD_B_DRIVE_OPTION. Setting this bit +* drives the HOLD bit of the QSPI controller. +* This is required for QSPI to be used in Non QSPI boot +* mode else there needs to be an external pullup on this +* line. +* See http://www.xilinx.com/support/answers/47596.htm. +* 2.01a sg 02/03/13 Created a function FlashReadID. Removed multiple +* initialization using SetOptions. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID + +/* + * The following constants define the commands which may be sent to the FLASH + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* FLASH instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ + +/* + * The following constants specify the extra bytes which are sent to the + * FLASH on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * The following constants specify the page size, sector size, and number of + * pages and sectors for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define SECTOR_SIZE 0x10000 +#define NUM_SECTORS 0x100 +#define NUM_PAGES 0x10000 +#define PAGE_SIZE 256 + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 16 + +/* + * Flash address to which data is ot be written. + */ +#define TEST_ADDRESS 0x00055000 +#define UNIQUE_VALUE 0x05 +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command); + +int FlashReadID(void); + +int QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId); + +/************************** Variable Definitions *****************************/ + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XQspiPs QspiInstance; + +/* + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test = 5; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[MAX_DATA + DATA_OFFSET + DUMMY_SIZE]; +u8 WriteBuffer[PAGE_SIZE + DATA_OFFSET]; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPI FLASH Polled Example Test \r\n"); + + /* + * Run the Qspi Interrupt example. + */ + Status = QspiFlashPolledExample(&QspiInstance, QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPI FLASH Polled Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPI FLASH Polled Example Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver in polled mode. This function writes and reads data +* from a serial FLASH. +* +* @param None. +* +* @return XST_SUCCESS if successful, else XST_FAILURE. +* +* @note None. +* +*****************************************************************************/ +int QspiFlashPolledExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId) +{ + int Status; + u8 *BufferPtr; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, + * the test value that is added to the unique value allows the value + * to be changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Set Manual Start and Manual Chip select options and drive HOLD_B + * pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Set the prescaler for QSPI clock + */ + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + + /* + * Assert the FLASH chip select. + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + + FlashReadID(); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Normal Read + * command. Change the prescaler as the READ command operates at a + * lower frequency. + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Fast Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, FAST_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Dual Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, DUAL_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Quad Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, + * the test value that is added to the unique value allows the value + * to be changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Set Auto Start and Manual Chip select options and drive HOLD_B + * pin high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MAX_DATA); + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, WRITE_CMD); + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Normal Read + * command. Change the prescaler as the READ command operates at a + * lower frequency. + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Fast Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, FAST_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Dual Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, DUAL_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Quad Read + * command + */ + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + FlashRead(QspiInstancePtr, TEST_ADDRESS, MAX_DATA, QUAD_READ_CMD); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + BufferPtr = &ReadBuffer[DATA_OFFSET + DUMMY_SIZE]; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* +* This function writes to the serial FLASH connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer before + * the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the write command, address, and data to the FLASH to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + ByteCount + OVERHEAD_SIZE); + + /* + * Wait for the write command to the FLASH to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the FLASH to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* This function reads from the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to read data from in the FLASH. +* @param ByteCount contains the number of bytes to read. +* @param Command is the command used to read data from the flash. QSPI +* device supports one of the Read, Fast Read, Dual Read and Fast +* Read commands to read data from the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command) +{ + /* + * Setup the write command with the specified address and data for the + * FLASH + */ + WriteBuffer[COMMAND_OFFSET] = Command; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || + (Command == QUAD_READ_CMD)) { + ByteCount += DUMMY_SIZE; + } + /* + * Send the read command to the FLASH to read the specified number + * of bytes from the FLASH, send the read command and address and + * receive the specified number of bytes of data in the data buffer + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer, + ByteCount + OVERHEAD_SIZE); +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial FLASH connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* must send 2 bytes */ + u8 FlashStatus[2]; + int Sector; + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) { + /* + * Send the write enable command to the FLASH so that it can be + * written to, this needs to be sent as a seperate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBuffer[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + return; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) { + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a seperate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the write command with the specified address and data + * for the FLASH + */ + WriteBuffer[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16); + WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8); + WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL, + SEC_ERASE_SIZE); + + /* + * Wait for the sector erse command to the FLASH to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + Address += SECTOR_SIZE; + } +} + +/****************************************************************************** +* +* This function reads serial FLASH ID connected to the SPI interface. +* +* @param None. +* +* @return XST_SUCCESS if read id, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +int FlashReadID(void) +{ + int Status; + + /* + * Read ID in Auto mode. + */ + WriteBuffer[COMMAND_OFFSET] = READ_ID; + WriteBuffer[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ + WriteBuffer[ADDRESS_2_OFFSET] = 0x08; + WriteBuffer[ADDRESS_3_OFFSET] = 0x09; + + Status = XQspiPs_PolledTransfer(&QspiInstance, WriteBuffer, ReadBuffer, + RD_ID_SIZE); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBuffer[1], ReadBuffer[2], + ReadBuffer[3]); + + return XST_SUCCESS; +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_g128_flash_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_g128_flash_example.c new file mode 100644 index 00000000..460bfd85 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_g128_flash_example.c @@ -0,0 +1,1524 @@ +/****************************************************************************** +* +* Copyright (C) 2013 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_g128_flash_example.c +* +* +* This file contains a design example using the QSPI driver (XQspiPs) +* with a serial Flash device greater than 128Mb. The example writes to flash +* and reads it back in I/O mode. This examples performs +* some transfers in Auto mode and Manual start mode, to illustrate the modes +* available. It is recommended to use Manual CS + Auto start for +* best performance. +* This example illustrates single, parallel and stacked modes. +* Both the flash devices have to be of the same make and size. +* The hardware which this example runs on, must have a serial Flash (Micron +* N25Q or Spansion S25FL) for it to run. This example has been +* tested with the Micron Serial Flash (N25Q256, N25Q512 & N25Q00AA) and +* Spansion (S25FL256 & S25FL512) +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 2.02a hk 05/07/13 First release +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* SDK generated parameters */ +#include "xqspips.h" /* QSPI device driver */ +#include "xil_printf.h" + +/************************** Constant Definitions *****************************/ + +/* + * The following constants define the commands which may be sent to the Flash + * device. + */ +#define WRITE_STATUS_CMD 0x01 +#define WRITE_CMD 0x02 +#define READ_CMD 0x03 +#define WRITE_DISABLE_CMD 0x04 +#define READ_STATUS_CMD 0x05 +#define WRITE_ENABLE_CMD 0x06 +#define FAST_READ_CMD 0x0B +#define DUAL_READ_CMD 0x3B +#define QUAD_READ_CMD 0x6B +#define BULK_ERASE_CMD 0xC7 +#define SEC_ERASE_CMD 0xD8 +#define READ_ID 0x9F +#define READ_CONFIG_CMD 0x35 +#define WRITE_CONFIG_CMD 0x01 +#define BANK_REG_RD 0x16 +#define BANK_REG_WR 0x17 +/* Bank register is called Extended Address Register in Micron */ +#define EXTADD_REG_RD 0xC8 +#define EXTADD_REG_WR 0xC5 +#define DIE_ERASE_CMD 0xC4 +#define READ_FLAG_STATUS_CMD 0x70 + +/* + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define COMMAND_OFFSET 0 /* Flash instruction */ +#define ADDRESS_1_OFFSET 1 /* MSB byte of address to read or write */ +#define ADDRESS_2_OFFSET 2 /* Middle byte of address to read or write */ +#define ADDRESS_3_OFFSET 3 /* LSB byte of address to read or write */ +#define DATA_OFFSET 4 /* Start of Data for Read/Write */ +#define DUMMY_OFFSET 4 /* Dummy byte offset for fast, dual and quad + reads */ +#define DUMMY_SIZE 1 /* Number of dummy bytes for fast, dual and + quad reads */ +#define RD_ID_SIZE 4 /* Read ID command + 3 bytes ID response */ +#define BULK_ERASE_SIZE 1 /* Bulk Erase command size */ +#define SEC_ERASE_SIZE 4 /* Sector Erase command + Sector address */ +#define BANK_SEL_SIZE 2 /* BRWR or EARWR command + 1 byte bank value */ +#define RD_CFG_SIZE 2 /* 1 byte Configuration register + RD CFG command*/ +#define WR_CFG_SIZE 3 /* WRR command + 1 byte each Status and Config Reg*/ +#define DIE_ERASE_SIZE 4 /* Die Erase command + Die address */ + +/* + * The following constants specify the extra bytes which are sent to the + * Flash on the QSPI interface, that are not data, but control information + * which includes the command and address + */ +#define OVERHEAD_SIZE 4 + +/* + * Base address of Flash1 + */ +#define FLASH1BASE 0x0000000 + +/* + * Sixteen MB + */ +#define SIXTEENMB 0x1000000 + + +/* + * Mask for quad enable bit in Flash configuration register + */ +#define FLASH_QUAD_EN_MASK 0x02 + +#define FLASH_SRWD_MASK 0x80 + +/* + * Bank mask + */ +#define BANKMASK 0xF000000 + +/* + * Identification of Flash + * Micron: + * Byte 0 is Manufacturer ID; + * Byte 1 is first byte of Device ID - 0xBB or 0xBA + * Byte 2 is second byte of Device ID describes flash size: + * 128Mbit : 0x18; 256Mbit : 0x19; 512Mbit : 0x20 + * Spansion: + * Byte 0 is Manufacturer ID; + * Byte 1 is Device ID - Memory Interface type - 0x20 or 0x02 + * Byte 2 is second byte of Device ID describes flash size: + * 128Mbit : 0x18; 256Mbit : 0x19; 512Mbit : 0x20 + */ +#define MICRON_ID_BYTE0 0x20 +#define MICRON_ID_BYTE2_128 0x18 +#define MICRON_ID_BYTE2_256 0x19 +#define MICRON_ID_BYTE2_512 0x20 +#define MICRON_ID_BYTE2_1G 0x21 + +#define SPANSION_ID_BYTE0 0x01 +#define SPANSION_ID_BYTE2_128 0x18 +#define SPANSION_ID_BYTE2_256 0x19 +#define SPANSION_ID_BYTE2_512 0x20 + +#define WINBOND_ID_BYTE0 0xEF +#define WINBOND_ID_BYTE2_128 0x18 + + +/* + * The index for Flash config table + */ +/* Spansion*/ +#define SPANSION_INDEX_START 0 +#define FLASH_CFG_TBL_SINGLE_128_SP SPANSION_INDEX_START +#define FLASH_CFG_TBL_STACKED_128_SP (SPANSION_INDEX_START + 1) +#define FLASH_CFG_TBL_PARALLEL_128_SP (SPANSION_INDEX_START + 2) +#define FLASH_CFG_TBL_SINGLE_256_SP (SPANSION_INDEX_START + 3) +#define FLASH_CFG_TBL_STACKED_256_SP (SPANSION_INDEX_START + 4) +#define FLASH_CFG_TBL_PARALLEL_256_SP (SPANSION_INDEX_START + 5) +#define FLASH_CFG_TBL_SINGLE_512_SP (SPANSION_INDEX_START + 6) +#define FLASH_CFG_TBL_STACKED_512_SP (SPANSION_INDEX_START + 7) +#define FLASH_CFG_TBL_PARALLEL_512_SP (SPANSION_INDEX_START + 8) + +/* Micron */ +#define MICRON_INDEX_START (FLASH_CFG_TBL_PARALLEL_512_SP + 1) +#define FLASH_CFG_TBL_SINGLE_128_MC MICRON_INDEX_START +#define FLASH_CFG_TBL_STACKED_128_MC (MICRON_INDEX_START + 1) +#define FLASH_CFG_TBL_PARALLEL_128_MC (MICRON_INDEX_START + 2) +#define FLASH_CFG_TBL_SINGLE_256_MC (MICRON_INDEX_START + 3) +#define FLASH_CFG_TBL_STACKED_256_MC (MICRON_INDEX_START + 4) +#define FLASH_CFG_TBL_PARALLEL_256_MC (MICRON_INDEX_START + 5) +#define FLASH_CFG_TBL_SINGLE_512_MC (MICRON_INDEX_START + 6) +#define FLASH_CFG_TBL_STACKED_512_MC (MICRON_INDEX_START + 7) +#define FLASH_CFG_TBL_PARALLEL_512_MC (MICRON_INDEX_START + 8) +#define FLASH_CFG_TBL_SINGLE_1GB_MC (MICRON_INDEX_START + 9) +#define FLASH_CFG_TBL_STACKED_1GB_MC (MICRON_INDEX_START + 10) +#define FLASH_CFG_TBL_PARALLEL_1GB_MC (MICRON_INDEX_START + 11) + +/* Winbond */ +#define WINBOND_INDEX_START (FLASH_CFG_TBL_PARALLEL_1GB_MC + 1) +#define FLASH_CFG_TBL_SINGLE_128_WB WINBOND_INDEX_START +#define FLASH_CFG_TBL_STACKED_128_WB (WINBOND_INDEX_START + 1) +#define FLASH_CFG_TBL_PARALLEL_128_WB (WINBOND_INDEX_START + 2) + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID +/* + * The following defines are for dual flash stacked mode interface. + */ +#define LQSPI_CR_FAST_QUAD_READ 0x0000006B /* Fast Quad Read output */ +#define LQSPI_CR_1_DUMMY_BYTE 0x00000100 /* 1 Dummy Byte between + address and return data */ + +#define DUAL_STACK_CONFIG_WRITE (XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +#define DUAL_QSPI_CONFIG_WRITE (XQSPIPS_LQSPI_CR_TWO_MEM_MASK | \ + XQSPIPS_LQSPI_CR_SEP_BUS_MASK | \ + LQSPI_CR_1_DUMMY_BYTE | \ + LQSPI_CR_FAST_QUAD_READ) + +/* + * Number of flash pages to be written. + */ +#define PAGE_COUNT 32 + +/* + * Max page size to initialize write and read buffer + */ +#define MAX_PAGE_SIZE 1024 + +/* + * Flash address to which data is to be written. + */ +#define TEST_ADDRESS 0x000000 + + +#define UNIQUE_VALUE 0x06 + +/**************************** Type Definitions *******************************/ + +typedef struct{ + u32 SectSize; /* Individual sector size or + * combined sector size in case of parallel config*/ + u32 NumSect; /* Total no. of sectors in one/two flash devices */ + u32 PageSize; /* Individual page size or + * combined page size in case of parallel config*/ + u32 NumPage; /* Total no. of pages in one/two flash devices */ + u32 FlashDeviceSize; /* This is the size of one flash device + * NOT the combination of both devices, if present + */ + u8 ManufacturerID; /* Manufacturer ID - used to identify make */ + u8 DeviceIDMemSize; /* Byte of device ID indicating the memory size */ + u32 SectMask; /* Mask to get sector start address */ + u8 NumDie; /* No. of die forming a single flash */ + +}FlashInfo; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +int QspiG128FlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId); + +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr); + +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command, + u8 *WriteBfrPtr); + +int FlashReadID(XQspiPs *QspiPtr, u8 *WriteBfrPtr, u8 *ReadBfrPtr); + +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command, + u8 *WriteBfrPtr, u8 *ReadBfrPtr); + +int SendBankSelect(XQspiPs *QspiPtr, u8 *WriteBfrPtr, u32 BankSel); + +void BulkErase(XQspiPs *QspiPtr, u8 *WriteBfrPtr); + +void DieErase(XQspiPs *QspiPtr, u8 *WriteBfrPtr); + +u32 GetRealAddr(XQspiPs *QspiPtr, u32 Address); + + +/************************** Variable Definitions *****************************/ + +FlashInfo Flash_Config_Table[24] = { + /* Spansion */ + {0x10000, 0x100, 256, 0x10000, 0x1000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x10000, 0x200, 256, 0x20000, 0x1000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x20000, 0x100, 512, 0x10000, 0x1000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_128, 0xFFFE0000, 1}, + {0x10000, 0x200, 256, 0x20000, 0x2000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_256, 0xFFFF0000, 1}, + {0x10000, 0x400, 256, 0x40000, 0x2000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_256, 0xFFFF0000, 1}, + {0x20000, 0x200, 512, 0x20000, 0x2000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_256, 0xFFFE0000, 1}, + {0x40000, 0x100, 512, 0x20000, 0x4000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_512, 0xFFFC0000, 1}, + {0x40000, 0x200, 512, 0x40000, 0x4000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_512, 0xFFFC0000, 1}, + {0x80000, 0x100, 1024, 0x20000, 0x4000000, + SPANSION_ID_BYTE0, SPANSION_ID_BYTE2_512, 0xFFF80000, 1}, + /* Spansion 1Gbit is handled as 512Mbit stacked */ + /* Micron */ + {0x10000, 0x100, 256, 0x10000, 0x1000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x10000, 0x200, 256, 0x20000, 0x1000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x20000, 0x100, 512, 0x10000, 0x1000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_128, 0xFFFE0000, 1}, + {0x10000, 0x200, 256, 0x20000, 0x2000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_256, 0xFFFF0000, 1}, + {0x10000, 0x400, 256, 0x40000, 0x2000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_256, 0xFFFF0000, 1}, + {0x20000, 0x200, 512, 0x20000, 0x2000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_256, 0xFFFE0000, 1}, + {0x10000, 0x400, 256, 0x40000, 0x4000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_512, 0xFFFF0000, 2}, + {0x10000, 0x800, 256, 0x80000, 0x4000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_512, 0xFFFF0000, 2}, + {0x20000, 0x400, 512, 0x40000, 0x4000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_512, 0xFFFE0000, 2}, + {0x10000, 0x800, 256, 0x80000, 0x8000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_1G, 0xFFFF0000, 4}, + {0x10000, 0x1000, 256, 0x100000, 0x8000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_1G, 0xFFFF0000, 4}, + {0x20000, 0x800, 512, 0x80000, 0x8000000, + MICRON_ID_BYTE0, MICRON_ID_BYTE2_1G, 0xFFFE0000, 4}, + /* Winbond */ + {0x10000, 0x100, 256, 0x10000, 0x1000000, + WINBOND_ID_BYTE0, WINBOND_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x10000, 0x200, 256, 0x20000, 0x1000000, + WINBOND_ID_BYTE0, WINBOND_ID_BYTE2_128, 0xFFFF0000, 1}, + {0x20000, 0x100, 512, 0x10000, 0x1000000, + WINBOND_ID_BYTE0, WINBOND_ID_BYTE2_128, 0xFFFE0000, 1} +}; + +u32 FlashMake; +u32 FCTIndex; /* Flash configuration table index */ + + +/* + * The instances to support the device drivers are global such that they + * are initialized to zero each time the program runs. They could be local + * but should at least be static so they are zeroed. + */ +static XQspiPs QspiInstance; + +/* + * The following variable allows a test value to be added to the values that + * are written to the Flash such that unique values can be generated to + * guarantee the writes to the Flash were successful + */ +int Test = 1; + +/* + * The following variables are used to read and write to the flash and they + * are global to avoid having large buffers on the stack + * The buffer size accounts for maximum page size and maximum banks - + * for each bank separate read will be performed leading to that many + * (overhead+dummy) bytes + */ +u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8]; +u8 WriteBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + DATA_OFFSET]; + +/* + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the Flash. Initialized to single flash page size. + */ +u32 MaxData = PAGE_COUNT*256; + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPI Greater than 128Mb Flash Example Test \r\n"); + + /* + * Run the Qspi Interrupt example. + */ + Status = QspiG128FlashExample(&QspiInstance, QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPI Greater than 128Mb Flash Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPI Greater than 128Mb Flash Ex Test\r\n"); + return XST_SUCCESS; +} + +/***************************************************************************** +* +* The purpose of this function is to illustrate how to use the XQspiPs +* device driver in single, parallel and stacked modes using +* flash devices greater than 128Mb. +* This function reads and writes data in I/O mode. +* +* @param None. +* +* @return XST_SUCCESS if successful, else XST_FAILURE. +* +* @note None. +* +*****************************************************************************/ +int QspiG128FlashExample(XQspiPs *QspiInstancePtr, u16 QspiDeviceId) +{ + int Status; + u8 UniqueValue; + int Count; + int Page; + XQspiPs_Config *QspiConfig; + + /* + * Initialize the QSPI driver so that it's ready to use + */ + QspiConfig = XQspiPs_LookupConfig(QspiDeviceId); + if (NULL == QspiConfig) { + return XST_FAILURE; + } + + Status = XQspiPs_CfgInitialize(QspiInstancePtr, QspiConfig, + QspiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XQspiPs_SelfTest(QspiInstancePtr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + + + /* + * Set the pre-scaler for QSPI clock + */ + XQspiPs_SetClkPrescaler(QspiInstancePtr, XQSPIPS_CLK_PRESCALE_8); + + /* + * Set Manual Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_MANUAL_START_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + if(QspiConfig->ConnectionMode == XQSPIPS_CONNECTION_MODE_STACKED) { + /* + * Enable two flash memories, Shared bus (NOT separate bus), + * L_PAGE selected by default + */ + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_STACK_CONFIG_WRITE); + } + + if(QspiConfig->ConnectionMode == XQSPIPS_CONNECTION_MODE_PARALLEL) { + /* + * Enable two flash memories on separate buses + */ + XQspiPs_SetLqspiConfigReg(QspiInstancePtr, DUAL_QSPI_CONFIG_WRITE); + } + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiInstancePtr); + + /* + * Read flash ID and obtain all flash related information + * It is important to call the read id function before + * performing proceeding to any operation, including + * preparing the WriteBuffer + */ + FlashReadID(QspiInstancePtr, WriteBuffer, ReadBuffer); + + /* + * Initialize MaxData according to page size. + */ + MaxData = PAGE_COUNT * (Flash_Config_Table[FCTIndex].PageSize); + + + /* + * Initialize the write buffer for a pattern to write to the Flash + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; + Count < Flash_Config_Table[FCTIndex].PageSize; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MaxData, WriteBuffer); + + /* + * Write the data in the write buffer to the serial Flash a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, + (Page * Flash_Config_Table[FCTIndex].PageSize) + TEST_ADDRESS, + Flash_Config_Table[FCTIndex].PageSize, WRITE_CMD, WriteBuffer); + } + + /* + * I/O Read - for any flash size + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MaxData, QUAD_READ_CMD, + WriteBuffer, ReadBuffer); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MaxData; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + /* + * Initialize the write buffer for a pattern to write to the Flash + * and the read buffer to zero so it can be verified after the read, the + * test value that is added to the unique value allows the value to be + * changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; + Count < Flash_Config_Table[FCTIndex].PageSize; + Count++, UniqueValue++) { + WriteBuffer[DATA_OFFSET + Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + /* + * Set Auto Start and Manual Chip select options and drive the + * HOLD_B high. + */ + XQspiPs_SetOptions(QspiInstancePtr, XQSPIPS_FORCE_SSELECT_OPTION | + XQSPIPS_HOLD_B_DRIVE_OPTION); + + /* + * Erase the flash. + */ + FlashErase(QspiInstancePtr, TEST_ADDRESS, MaxData, WriteBuffer); + + /* + * Write the data in the write buffer to the serial Flash a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(QspiInstancePtr, + (Page * Flash_Config_Table[FCTIndex].PageSize) + TEST_ADDRESS, + Flash_Config_Table[FCTIndex].PageSize, WRITE_CMD, WriteBuffer); + } + + /* + * I/O Read - for any flash size + */ + FlashRead(QspiInstancePtr, TEST_ADDRESS, MaxData, QUAD_READ_CMD, + WriteBuffer, ReadBuffer); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MaxData; + Count++, UniqueValue++) { + if (ReadBuffer[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* +* This function writes to the serial Flash connected to the QSPI interface. +* All the data put into the buffer must be in the same page of the device with +* page boundaries being on 256 byte boundaries. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address to write data to in the Flash. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. QSPI +* device supports only Page Program command to write data to the +* flash. +* @param Pointer to the write buffer (which is to be transmitted) +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashWrite(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command, + u8 *WriteBfrPtr) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* Must send 2 bytes */ + u8 FlashStatus[2]; + u32 RealAddr; + u32 BankSel; + u8 ReadFlagSRCmd[] = {READ_FLAG_STATUS_CMD, 0}; + u8 FlagStatus[2]; + + /* + * Translate address based on type of connection + * If stacked assert the slave select based on address + */ + RealAddr = GetRealAddr(QspiPtr, Address); + /* + * Bank Select + */ + if(Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { + /* + * Calculate bank + */ + BankSel = RealAddr/SIXTEENMB; + /* + * Select bank + */ + SendBankSelect(QspiPtr, WriteBfrPtr, BankSel); + } + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer before + * the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + + /* + * Setup the write command with the specified address and data for the + * Flash + */ + /* + * This will ensure a 3B address is transferred even when address + * is greater than 128Mb. + */ + WriteBfrPtr[COMMAND_OFFSET] = Command; + WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); + WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); + WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + /* + * Send the write command, address, and data to the Flash to be + * written, no receive buffer is specified since there is nothing to + * receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + ByteCount + OVERHEAD_SIZE); + + if((Flash_Config_Table[FCTIndex].NumDie > 1) && + (FlashMake == MICRON_ID_BYTE0)) { + XQspiPs_PolledTransfer(QspiPtr, ReadFlagSRCmd, FlagStatus, + sizeof(ReadFlagSRCmd)); + } + /* + * Wait for the write command to the Flash to be completed, it takes + * some time for the data to be written + */ + while (1) { + /* + * Poll the status register of the Flash to determine when it + * completes, by sending a read status command and receiving the + * status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop waiting, + * if a value of 0xFF in the status byte is read from the + * device and this loop never exits, the device slave select is + * possibly incorrect such that the device status is not being + * read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + if((Flash_Config_Table[FCTIndex].NumDie > 1) && + (FlashMake == MICRON_ID_BYTE0)) { + XQspiPs_PolledTransfer(QspiPtr, ReadFlagSRCmd, FlagStatus, + sizeof(ReadFlagSRCmd)); + } + +} + +/****************************************************************************** +* +* +* This function erases the sectors in the serial Flash connected to the +* QSPI interface. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* @param Pointer to the write buffer (which is to be transmitted) +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void FlashErase(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 *WriteBfrPtr) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* Must send 2 bytes */ + u8 FlashStatus[2]; + int Sector; + u32 RealAddr; + u32 LqspiCr; + u32 NumSect; + u32 BankSel; + u8 BankInitFlag = 1; + u8 ReadFlagSRCmd[] = { READ_FLAG_STATUS_CMD, 0 }; + u8 FlagStatus[2]; + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command or die erase command multiple times as required + */ + if (ByteCount == ((Flash_Config_Table[FCTIndex]).NumSect * + (Flash_Config_Table[FCTIndex]).SectSize) ) { + + if(QspiPtr->Config.ConnectionMode == XQSPIPS_CONNECTION_MODE_STACKED){ + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + } + + if(Flash_Config_Table[FCTIndex].NumDie == 1) { + /* + * Call Bulk erase + */ + BulkErase(QspiPtr, WriteBfrPtr); + } + + if(Flash_Config_Table[FCTIndex].NumDie > 1) { + /* + * Call Die erase + */ + DieErase(QspiPtr, WriteBfrPtr); + } + /* + * If stacked mode, bulk erase second flash + */ + if(QspiPtr->Config.ConnectionMode == XQSPIPS_CONNECTION_MODE_STACKED){ + + /* + * Get the current LQSPI configuration register value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + /* + * Set selection to U_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr | XQSPIPS_LQSPI_CR_U_PAGE_MASK); + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + + if(Flash_Config_Table[FCTIndex].NumDie == 1) { + /* + * Call Bulk erase + */ + BulkErase(QspiPtr, WriteBfrPtr); + } + + if(Flash_Config_Table[FCTIndex].NumDie > 1) { + /* + * Call Die erase + */ + DieErase(QspiPtr, WriteBfrPtr); + } + } + + return; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + + /* + * Calculate no. of sectors to erase based on byte count + */ + NumSect = ByteCount/(Flash_Config_Table[FCTIndex].SectSize) + 1; + + /* + * If ByteCount to k sectors, + * but the address range spans from N to N+k+1 sectors, then + * increment no. of sectors to be erased + */ + + if( ((Address + ByteCount) & Flash_Config_Table[FCTIndex].SectMask) == + ((Address + (NumSect * Flash_Config_Table[FCTIndex].SectSize)) & + Flash_Config_Table[FCTIndex].SectMask) ) { + NumSect++; + } + + for (Sector = 0; Sector < NumSect; Sector++) { + + /* + * Translate address based on type of connection + * If stacked assert the slave select based on address + */ + RealAddr = GetRealAddr(QspiPtr, Address); + + /* + * Initial bank selection + */ + if((BankInitFlag) && + (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB)) { + /* + * Reset initial bank select flag + */ + BankInitFlag = 0; + /* + * Calculate initial bank + */ + BankSel = RealAddr/SIXTEENMB; + /* + * Select bank + */ + SendBankSelect(QspiPtr, WriteBfrPtr, BankSel); + } + /* + * Check bank and send bank select if new bank + */ + if((BankSel != RealAddr/SIXTEENMB) && + (Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB)) { + /* + * Calculate initial bank + */ + BankSel = RealAddr/SIXTEENMB; + /* + * Select bank + */ + SendBankSelect(QspiPtr, WriteBfrPtr, BankSel); + } + + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a separate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the write command with the specified address and data + * for the Flash + */ + /* + * This ensures 3B address is sent to flash even with address + * greater than 128Mb. + */ + WriteBfrPtr[COMMAND_OFFSET] = SEC_ERASE_CMD; + WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)(RealAddr >> 16); + WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)(RealAddr >> 8); + WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + SEC_ERASE_SIZE); + + if((Flash_Config_Table[FCTIndex].NumDie > 1) && + (FlashMake == MICRON_ID_BYTE0)) { + XQspiPs_PolledTransfer(QspiPtr, ReadFlagSRCmd, FlagStatus, + sizeof(ReadFlagSRCmd)); + } + /* + * Wait for the sector erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } + + if((Flash_Config_Table[FCTIndex].NumDie > 1) && + (FlashMake == MICRON_ID_BYTE0)) { + XQspiPs_PolledTransfer(QspiPtr, ReadFlagSRCmd, FlagStatus, + sizeof(ReadFlagSRCmd)); + } + + Address += Flash_Config_Table[FCTIndex].SectSize; + + } +} + +/****************************************************************************** +* +* This function reads serial Flash ID connected to the SPI interface. +* It then deduces the make and size of the flash and obtains the +* connection mode to point to corresponding parameters in the flash +* configuration table. The flash driver will function based on this and +* it presently supports Micron and Spansion - 128, 256 and 512Mbit and +* Winbond 128Mbit +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Pointer to the write buffer (which is to be transmitted) +* @param Pointer to the read buffer to which valid received data should be +* written +* +* @return XST_SUCCESS if read id, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +int FlashReadID(XQspiPs *QspiPtr, u8 *WriteBfrPtr, u8 *ReadBfrPtr) +{ + int Status; + int StartIndex; + + /* + * Read ID in Auto mode. + */ + WriteBfrPtr[COMMAND_OFFSET] = READ_ID; + WriteBfrPtr[ADDRESS_1_OFFSET] = 0x23; /* 3 dummy bytes */ + WriteBfrPtr[ADDRESS_2_OFFSET] = 0x08; + WriteBfrPtr[ADDRESS_3_OFFSET] = 0x09; + + Status = XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, ReadBfrPtr, + RD_ID_SIZE); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Deduce flash make + */ + if(ReadBfrPtr[1] == MICRON_ID_BYTE0) { + FlashMake = MICRON_ID_BYTE0; + StartIndex = MICRON_INDEX_START; + }else if(ReadBfrPtr[1] == SPANSION_ID_BYTE0) { + FlashMake = SPANSION_ID_BYTE0; + StartIndex = SPANSION_INDEX_START; + }else if(ReadBfrPtr[1] == WINBOND_ID_BYTE0) { + FlashMake = WINBOND_ID_BYTE0; + StartIndex = WINBOND_INDEX_START; + } + + + /* + * If valid flash ID, then check connection mode & size and + * assign corresponding index in the Flash configuration table + */ + if(((FlashMake == MICRON_ID_BYTE0) || (FlashMake = SPANSION_ID_BYTE0)|| + (FlashMake == WINBOND_ID_BYTE0)) && + (ReadBfrPtr[3] == MICRON_ID_BYTE2_128)) { + + switch(QspiPtr->Config.ConnectionMode) + { + case XQSPIPS_CONNECTION_MODE_SINGLE: + FCTIndex = FLASH_CFG_TBL_SINGLE_128_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_PARALLEL: + FCTIndex = FLASH_CFG_TBL_PARALLEL_128_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_STACKED: + FCTIndex = FLASH_CFG_TBL_STACKED_128_SP + StartIndex; + break; + default: + FCTIndex = 0; + break; + } + } + /* 256 and 512Mbit supported only for Micron and Spansion, not Winbond */ + if(((FlashMake == MICRON_ID_BYTE0) || (FlashMake = SPANSION_ID_BYTE0)) && + (ReadBfrPtr[3] == MICRON_ID_BYTE2_256)) { + + switch(QspiPtr->Config.ConnectionMode) + { + case XQSPIPS_CONNECTION_MODE_SINGLE: + FCTIndex = FLASH_CFG_TBL_SINGLE_256_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_PARALLEL: + FCTIndex = FLASH_CFG_TBL_PARALLEL_256_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_STACKED: + FCTIndex = FLASH_CFG_TBL_STACKED_256_SP + StartIndex; + break; + default: + FCTIndex = 0; + break; + } + } + if(((FlashMake == MICRON_ID_BYTE0) || (FlashMake = SPANSION_ID_BYTE0)) && + (ReadBfrPtr[3] == MICRON_ID_BYTE2_512)) { + + switch(QspiPtr->Config.ConnectionMode) + { + case XQSPIPS_CONNECTION_MODE_SINGLE: + FCTIndex = FLASH_CFG_TBL_SINGLE_512_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_PARALLEL: + FCTIndex = FLASH_CFG_TBL_PARALLEL_512_SP + StartIndex; + break; + case XQSPIPS_CONNECTION_MODE_STACKED: + FCTIndex = FLASH_CFG_TBL_STACKED_512_SP + StartIndex; + break; + default: + FCTIndex = 0; + break; + } + } + /* + * 1Gbit Single connection supported for Spansion. + * The ConnectionMode will indicate stacked as this part has 2 SS + * The device ID will indicate 512Mbit. + * This configuration is handled as the above 512Mbit stacked configuration + */ + /* 1Gbit single, parallel and stacked supported for Micron */ + if((FlashMake == MICRON_ID_BYTE0) && + (ReadBfrPtr[3] == MICRON_ID_BYTE2_1G)) { + + switch(QspiPtr->Config.ConnectionMode) + { + case XQSPIPS_CONNECTION_MODE_SINGLE: + FCTIndex = FLASH_CFG_TBL_SINGLE_1GB_MC; + break; + case XQSPIPS_CONNECTION_MODE_PARALLEL: + FCTIndex = FLASH_CFG_TBL_PARALLEL_1GB_MC; + break; + case XQSPIPS_CONNECTION_MODE_STACKED: + FCTIndex = FLASH_CFG_TBL_STACKED_1GB_MC; + break; + default: + FCTIndex = 0; + break; + } + } + + xil_printf("FlashID=0x%x 0x%x 0x%x\n\r", ReadBfrPtr[1], ReadBfrPtr[2], + ReadBfrPtr[3]); + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* This function performs an I/O read. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* @param Command is the command used to read data from the flash. Supports +* normal, fast, dual and quad read commands. +* @param Pointer to the write buffer which contains data to be transmitted +* @param Pointer to the read buffer to which valid received data should be +* written +* +* @return none. +* +* @note None. +* +******************************************************************************/ +void FlashRead(XQspiPs *QspiPtr, u32 Address, u32 ByteCount, u8 Command, + u8 *WriteBfrPtr, u8 *ReadBfrPtr) +{ + u32 RealAddr; + u32 RealByteCnt; + u32 BankSel; + u32 BufferIndex; + u32 TotalByteCnt; + u8 ShiftSize; + + /* + * Retain the actual byte count + */ + TotalByteCnt = ByteCount; + + while(((signed long)(ByteCount)) > 0) { + + /* + * Translate address based on type of connection + * If stacked assert the slave select based on address + */ + RealAddr = GetRealAddr(QspiPtr, Address); + + /* + * Select bank + */ + if(Flash_Config_Table[FCTIndex].FlashDeviceSize > SIXTEENMB) { + BankSel = RealAddr/SIXTEENMB; + SendBankSelect(QspiPtr, WriteBfrPtr, BankSel); + } + + /* + * If data to be read spans beyond the current bank, then + * calculate RealByteCnt in current bank. Else + * RealByteCnt is the same as ByteCount + */ + if((Address & BANKMASK) != ((Address+ByteCount) & BANKMASK)) { + RealByteCnt = (Address & BANKMASK) + SIXTEENMB - Address; + }else { + RealByteCnt = ByteCount; + } + + + /* + * Setup the write command with the specified address and data for the + * Flash + */ + WriteBfrPtr[COMMAND_OFFSET] = Command; + WriteBfrPtr[ADDRESS_1_OFFSET] = (u8)((RealAddr & 0xFF0000) >> 16); + WriteBfrPtr[ADDRESS_2_OFFSET] = (u8)((RealAddr & 0xFF00) >> 8); + WriteBfrPtr[ADDRESS_3_OFFSET] = (u8)(RealAddr & 0xFF); + + if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || + (Command == QUAD_READ_CMD)) { + RealByteCnt += DUMMY_SIZE; + } + /* + * Send the read command to the Flash to read the specified number + * of bytes from the Flash, send the read command and address and + * receive the specified number of bytes of data in the data buffer + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, + &(ReadBfrPtr[TotalByteCnt - ByteCount]), + RealByteCnt + OVERHEAD_SIZE); + + /* + * To discard the first 5 dummy bytes, shift the data in read buffer + */ + if((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || + (Command == QUAD_READ_CMD)){ + ShiftSize = OVERHEAD_SIZE + DUMMY_SIZE; + }else{ + ShiftSize = OVERHEAD_SIZE; + } + + for(BufferIndex = (TotalByteCnt - ByteCount); + BufferIndex < (TotalByteCnt - ByteCount) + RealByteCnt; + BufferIndex++) { + ReadBfrPtr[BufferIndex] = ReadBfrPtr[BufferIndex + ShiftSize]; + } + + /* + * Increase address to next bank + */ + Address = (Address & BANKMASK) + SIXTEENMB; + /* + * Decrease byte count by bytes already read. + */ + if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) || + (Command == QUAD_READ_CMD)) { + ByteCount = ByteCount - (RealByteCnt - DUMMY_SIZE); + }else { + ByteCount = ByteCount - RealByteCnt; + } + + } + +} + +/****************************************************************************** +* +* This functions selects the current bank +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Pointer to the write buffer which contains data to be transmitted +* @param BankSel is the bank to be selected in the flash device(s). +* +* @return XST_SUCCESS if bank selected, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +int SendBankSelect(XQspiPs *QspiPtr, u8 *WriteBfrPtr, u32 BankSel) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + + /* + * Bank select commands for Micron and Spansion are different + */ + if(FlashMake == MICRON_ID_BYTE0) { + /* + * For Micron command WREN should be sent first + * except for some specific feature set + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + WriteBfrPtr[COMMAND_OFFSET] = EXTADD_REG_WR; + WriteBfrPtr[ADDRESS_1_OFFSET] = BankSel; + + /* + * Send the Extended address register write command + * written, no receive buffer required + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + BANK_SEL_SIZE); + + } + if(FlashMake == SPANSION_ID_BYTE0) { + WriteBfrPtr[COMMAND_OFFSET] = BANK_REG_WR; + WriteBfrPtr[ADDRESS_1_OFFSET] = BankSel; + + /* + * Send the Extended address register write command + * written, no receive buffer required + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + BANK_SEL_SIZE); + } + + /* Winbond can be added here */ + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* This functions performs a bulk erase operation when the +* flash device has a single die. Works for both Spansion and Micron +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param WritBfrPtr is the pointer to command+address to be sent +* +* @return None +* +* @note None. +* +******************************************************************************/ +void BulkErase(XQspiPs *QspiPtr, u8 *WriteBfrPtr) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 }; /* Must send 2 bytes */ + u8 FlashStatus[2]; + + /* + * Send the write enable command to the Flash so that it can be + * written to, this needs to be sent as a separate transfer + * before the erase + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the bulk erase command + */ + WriteBfrPtr[COMMAND_OFFSET] = BULK_ERASE_CMD; + + /* + * Send the bulk erase command; no receive buffer is specified + * since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + BULK_ERASE_SIZE); + + /* + * Wait for the erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, + FlashStatus, + sizeof(ReadStatusCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting; if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlashStatus[1] & 0x01) == 0) { + break; + } + } +} + +/****************************************************************************** +* +* This functions performs a die erase operation on all the die in +* the flash device. This function uses the die erase command for +* Micron 512Mbit and 1Gbit +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param WritBfrPtr is the pointer to command+address to be sent +* +* @return None +* +* @note None. +* +******************************************************************************/ +void DieErase(XQspiPs *QspiPtr, u8 *WriteBfrPtr) +{ + u8 WriteEnableCmd = { WRITE_ENABLE_CMD }; + u8 DieCnt; + u8 ReadFlagSRCmd[] = { READ_FLAG_STATUS_CMD, 0 }; + u8 FlagStatus[2]; + + for(DieCnt = 0; DieCnt < Flash_Config_Table[FCTIndex].NumDie; DieCnt++) { + /* + * Select bank - the lower of the 2 banks in each die + * This is specific to Micron flash + */ + SendBankSelect(QspiPtr, WriteBfrPtr, DieCnt*2); + + /* + * Send the write enable command to the SEEPOM so that it can be + * written to, this needs to be sent as a separate transfer + * before the write + */ + XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL, + sizeof(WriteEnableCmd)); + + /* + * Setup the write command with the specified address and data + * for the Flash + */ + /* + * This ensures 3B address is sent to flash even with address + * greater than 128Mb. + * The address is the start address of die - MSB bits will be + * derived from bank select by the flash + */ + WriteBfrPtr[COMMAND_OFFSET] = DIE_ERASE_CMD; + WriteBfrPtr[ADDRESS_1_OFFSET] = 0x00; + WriteBfrPtr[ADDRESS_2_OFFSET] = 0x00; + WriteBfrPtr[ADDRESS_3_OFFSET] = 0x00; + + /* + * Send the sector erase command and address; no receive buffer + * is specified since there is nothing to receive + */ + XQspiPs_PolledTransfer(QspiPtr, WriteBfrPtr, NULL, + DIE_ERASE_SIZE); + + /* + * Wait for the sector erase command to the Flash to be completed + */ + while (1) { + /* + * Poll the status register of the device to determine + * when it completes, by sending a read status command + * and receiving the status byte + */ + XQspiPs_PolledTransfer(QspiPtr, ReadFlagSRCmd, FlagStatus, + sizeof(ReadFlagSRCmd)); + + /* + * If the status indicates the write is done, then stop + * waiting, if a value of 0xFF in the status byte is + * read from the device and this loop never exits, the + * device slave select is possibly incorrect such that + * the device status is not being read + */ + if ((FlagStatus[1] & 0x80) == 0x80) { + break; + } + } + + } +} + +/****************************************************************************** +* +* This functions translates the address based on the type of interconnection. +* In case of stacked, this function asserts the corresponding slave select. +* +* @param QspiPtr is a pointer to the QSPI driver component to use. +* @param Address which is to be accessed (for erase, write or read) +* +* @return RealAddr is the translated address - for single it is unchanged; +* for stacked, the lower flash size is subtracted; +* for parallel the address is divided by 2. +* +* @note None. +* +******************************************************************************/ +u32 GetRealAddr(XQspiPs *QspiPtr, u32 Address) +{ + u32 LqspiCr; + u32 RealAddr; + + switch(QspiPtr->Config.ConnectionMode) { + case XQSPIPS_CONNECTION_MODE_SINGLE: + RealAddr = Address; + break; + case XQSPIPS_CONNECTION_MODE_STACKED: + /* + * Get the current LQSPI Config reg value + */ + LqspiCr = XQspiPs_GetLqspiConfigReg(QspiPtr); + + /* Select lower or upper Flash based on sector address */ + if(Address & Flash_Config_Table[FCTIndex].FlashDeviceSize) { + /* + * Set selection to U_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr | XQSPIPS_LQSPI_CR_U_PAGE_MASK); + + /* + * Subtract first flash size when accessing second flash + */ + RealAddr = Address & + (~Flash_Config_Table[FCTIndex].FlashDeviceSize); + + }else{ + + /* + * Set selection to L_PAGE + */ + XQspiPs_SetLqspiConfigReg(QspiPtr, + LqspiCr & (~XQSPIPS_LQSPI_CR_U_PAGE_MASK)); + + RealAddr = Address; + + } + + /* + * Assert the Flash chip select. + */ + XQspiPs_SetSlaveSelect(QspiPtr); + break; + case XQSPIPS_CONNECTION_MODE_PARALLEL: + /* + * The effective address in each flash is the actual + * address / 2 + */ + RealAddr = Address / 2; + break; + default: + /* RealAddr wont be assigned in this case; */ + break; + + } + + return(RealAddr); + +} diff --git a/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_selftest_example.c b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_selftest_example.c new file mode 100644 index 00000000..ce48bfbb --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/examples/xqspips_selftest_example.c @@ -0,0 +1,157 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/******************************************************************************/ +/** +* @file xqspips_selftest_example.c +* +* This file contains an example for using the QSPI Hardware, it does a simple +* hardware connection check. +* +* @note +* +* None +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- ------ -------- ----------------------------------------------- +* 1.00 drg/jz 01/25/10 First release. +*+* +*******************************************************************************/ + +/***************************** Include Files **********************************/ + +#include "xparameters.h" +#include "xqspips.h" +#include "xil_printf.h" + +/************************** Constant Definitions ******************************/ + +/* + * The following constants map to the XPAR parameters created in the + * xparameters.h file. They are defined here such that a user can easily + * change all the needed parameters in one place. + */ +#define QSPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID + +/**************************** Type Definitions ********************************/ + +/***************** Macros (Inline Functions) Definitions **********************/ + +/************************** Function Prototypes *******************************/ + +int QspiPsSelfTestExample(u16 DeviceId); + +/************************** Variable Definitions ******************************/ + +XQspiPs Qspi; /* The instance of the QSPI device */ + +/******************************************************************************/ +/** +* Main function to call the Qspi Selftest example. +* +* @param None +* +* @return XST_SUCCESS if successful, XST_FAILURE if unsuccessful +* +* @note None +* +*******************************************************************************/ +#ifndef TESTAPP_GEN +int main(void) +{ + int Status; + + xil_printf("QSPI Selftest Example \r\n"); + + /* + * Call the example , specify the device ID that is generated in + * xparameters.h + */ + Status = QspiPsSelfTestExample(QSPI_DEVICE_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPI Selftest Example Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPI Selftest Example\r\n"); + return XST_SUCCESS; +} +#endif + +/*****************************************************************************/ +/** +* +* This function does a selftest on the QSPI device and XQspiPs driver as an +* example. The purpose of this function is to illustrate the usage of the +* XQspiPs driver. +* +* +* @param DeviceId is the XPAR_
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.00a kka 07/25/12 Removed XQspiPs_GetWriteData API. +* The XQspiPs_SetSlaveSelect has been modified to remove +* the argument of the slave select as the QSPI controller +* only supports one slave. +* XQspiPs_GetSlaveSelect API has been removed +* Added logic to XQspiPs_GetReadData to handle data +* shift for normal data reads and instruction/status +* reads differently based on the ShiftReadData flag. +* Removed the selection for the following options: +* Master mode (XQSPIPS_MASTER_OPTION) and +* Flash interface mode (XQSPIPS_FLASH_MODE_OPTION) option +* as the QSPI driver supports the Master mode +* and Flash Interface mode and doesnot support +* Slave mode or the legacy mode. +* Modified the XQspiPs_PolledTransfer and XQspiPs_Transfer +* APIs so that the last argument (IsInst) specifying whether +* it is instruction or data has been removed. The first byte +* in the SendBufPtr argument of these APIs specify the +* instruction to be sent to the Flash Device. +* The XQspiPs_PolledTransfer function has been updated +* to fill the data to fifo depth. +* This version of the driver fixes CRs 670197/663787. +* 2.01a sg 02/03/13 Added flash opcodes for DUAL_IO_READ,QUAD_IO_READ. +* Created macros XQspiPs_IsManualStart and +* XQspiPs_IsManualChipSelect. +* Changed QSPI transfer logic for polled and interrupt +* modes to be based on filled tx fifo count and receive +* based on it. RXNEMPTY interrupt is not used. +* Added assertions to XQspiPs_LqspiRead function. +* +* 2.02a hk 05/14/13 Added enable and disable to the XQspiPs_LqspiRead() +* function +* Added instructions for bank selection, die erase and +* flag status register to the flash instruction table +* Handling for instructions not in flash instruction +* table added. Checking for Tx FIFO empty when switching from +* TXD1/2/3 to TXD0 added. If WRSR instruction is sent with +* byte count 3 (spansion), instruction size and TXD register +* changed accordingly. CR# 712502 and 703869. +* Added (#ifdef linear base address) in the Linear read function. +* Changed XPAR_XQSPIPS_0_LINEAR_BASEADDR to +* XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR in +* XQspiPs_LqspiRead function. Fix for CR#718141 +* +* 2.03a hk 09/05/13 Modified polled and interrupt transfers to make use of +* thresholds. This is to improve performance. +* Added RX and TX threshold reset to one in XQspiPs_Abort. +* Added RX threshold reset(1) after transfer in polled and +* interrupt transfers. Made changes to make sure threshold +* change is done only when no transfer is in progress. +* 3.1 hk 08/13/14 When writing to the configuration register, set/reset +* required bits leaving reserved bits untouched. CR# 796813. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xqspips.h" + +/************************** Constant Definitions *****************************/ + + +/**************************** Type Definitions *******************************/ + +/** + * This typedef defines qspi flash instruction format + */ +typedef struct { + u8 OpCode; /**< Operational code of the instruction */ + u8 InstSize; /**< Size of the instruction including address bytes */ + u8 TxOffset; /**< Register address where instruction has to be + written */ +} XQspiPsInstFormat; + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define ARRAY_SIZE(Array) (sizeof(Array) / sizeof((Array)[0])) + +/************************** Function Prototypes ******************************/ +static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size); +static void StubStatusHandler(void *CallBackRef, u32 StatusEvent, + unsigned ByteCount); + +/************************** Variable Definitions *****************************/ + +/* + * List of all the QSPI instructions and its format + */ +static XQspiPsInstFormat FlashInst[] = { + { XQSPIPS_FLASH_OPCODE_WREN, 1, XQSPIPS_TXD_01_OFFSET }, + { XQSPIPS_FLASH_OPCODE_WRDS, 1, XQSPIPS_TXD_01_OFFSET }, + { XQSPIPS_FLASH_OPCODE_RDSR1, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_RDSR2, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_WRSR, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_PP, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_SE, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_BE_32K, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_BE_4K, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_BE, 1, XQSPIPS_TXD_01_OFFSET }, + { XQSPIPS_FLASH_OPCODE_ERASE_SUS, 1, XQSPIPS_TXD_01_OFFSET }, + { XQSPIPS_FLASH_OPCODE_ERASE_RES, 1, XQSPIPS_TXD_01_OFFSET }, + { XQSPIPS_FLASH_OPCODE_RDID, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_NORM_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_FAST_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_DUAL_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_QUAD_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_DUAL_IO_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_QUAD_IO_READ, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_BRWR, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_BRRD, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_EARWR, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_EARRD, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_DIE_ERASE, 4, XQSPIPS_TXD_00_OFFSET }, + { XQSPIPS_FLASH_OPCODE_READ_FLAG_SR, 2, XQSPIPS_TXD_10_OFFSET }, + { XQSPIPS_FLASH_OPCODE_CLEAR_FLAG_SR, 1, XQSPIPS_TXD_01_OFFSET }, + /* Add all the instructions supported by the flash device */ +}; + +/*****************************************************************************/ +/** +* +* Initializes a specific XQspiPs instance such that the driver is ready to use. +* +* The state of the device after initialization is: +* - Master mode +* - Active high clock polarity +* - Clock phase 0 +* - Baud rate divisor 2 +* - Transfer width 32 +* - Master reference clock = pclk +* - No chip select active +* - Manual CS and Manual Start disabled +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param ConfigPtr is a reference to a structure containing information +* about a specific QSPI device. This function initializes an +* InstancePtr object for a specific device specified by the +* contents of Config. +* @param EffectiveAddr is the device base address in the virtual memory +* address space. The caller is responsible for keeping the address +* mapping from EffectiveAddr to the device physical base address +* unchanged once this function is invoked. Unexpected errors may +* occur if the address mapping changes after this function is +* called. If address translation is not used, use +* ConfigPtr->Config.BaseAddress for this device. +* +* @return +* - XST_SUCCESS if successful. +* - XST_DEVICE_IS_STARTED if the device is already started. +* It must be stopped to re-initialize. +* +* @note None. +* +******************************************************************************/ +int XQspiPs_CfgInitialize(XQspiPs *InstancePtr, XQspiPs_Config *ConfigPtr, + u32 EffectiveAddr) +{ + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(ConfigPtr != NULL); + + /* + * If the device is busy, disallow the initialize and return a status + * indicating it is already started. This allows the user to stop the + * device and re-initialize, but prevents a user from inadvertently + * initializing. This assumes the busy flag is cleared at startup. + */ + if (InstancePtr->IsBusy == TRUE) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Set some default values. + */ + InstancePtr->IsBusy = FALSE; + + InstancePtr->Config.BaseAddress = EffectiveAddr; + InstancePtr->StatusHandler = StubStatusHandler; + + InstancePtr->SendBufferPtr = NULL; + InstancePtr->RecvBufferPtr = NULL; + InstancePtr->RequestedBytes = 0; + InstancePtr->RemainingBytes = 0; + InstancePtr->IsReady = XIL_COMPONENT_IS_READY; + + InstancePtr->Config.ConnectionMode = ConfigPtr->ConnectionMode; + + /* + * Reset the QSPI device to get it into its initial state. It is + * expected that device configuration will take place after this + * initialization is done, but before the device is started. + */ + XQspiPs_Reset(InstancePtr); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Resets the QSPI device. Reset must only be called after the driver has been +* initialized. Any data transfer that is in progress is aborted. +* +* The upper layer software is responsible for re-configuring (if necessary) +* and restarting the QSPI device after the reset. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void XQspiPs_Reset(XQspiPs *InstancePtr) +{ + u32 ConfigReg; + + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Abort any transfer that is in progress + */ + XQspiPs_Abort(InstancePtr); + + /* + * Write default value to configuration register. + * Do not modify reserved bits. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_RESET_MASK_SET; + ConfigReg &= ~XQSPIPS_CR_RESET_MASK_CLR; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_CR_OFFSET, + ConfigReg); +} + +/*****************************************************************************/ +/** +* +* Aborts a transfer in progress by disabling the device and flush the RxFIFO. +* The byte counts are cleared, the busy flag is cleared. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return None. +* +* @note +* +* This function does a read/modify/write of the config register. The user of +* this function needs to take care of critical sections. +* +******************************************************************************/ +void XQspiPs_Abort(XQspiPs *InstancePtr) +{ + u32 ConfigReg; + + XQspiPs_Disable(InstancePtr); + + /* + * De-assert slave select lines. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= (XQSPIPS_CR_SSCTRL_MASK | XQSPIPS_CR_SSFORCE_MASK); + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + + /* + * Set the RX and TX FIFO threshold to reset value (one) + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE); + + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_TXWR_OFFSET, XQSPIPS_TXWR_RESET_VALUE); + + /* + * Clear the RX FIFO and drop any data. + */ + while ((XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_SR_OFFSET) & XQSPIPS_IXR_RXNEMPTY_MASK) != 0) { + XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + } + + InstancePtr->RemainingBytes = 0; + InstancePtr->RequestedBytes = 0; + InstancePtr->IsBusy = FALSE; +} + +/*****************************************************************************/ +/** +* +* Transfers specified data on the QSPI bus. Initiates bus communication and +* sends/receives data to/from the selected QSPI slave. For every byte sent, +* a byte is received. +* +* The caller has the option of providing two different buffers for send and +* receive, or one buffer for both send and receive, or no buffer for receive. +* The receive buffer must be at least as big as the send buffer to prevent +* unwanted memory writes. This implies that the byte count passed in as an +* argument must be the smaller of the two buffers if they differ in size. +* Here are some sample usages: +*
+* XQspiPs_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount) +* The caller wishes to send and receive, and provides two different +* buffers for send and receive. +* +* XQspiPs_Transfer(InstancePtr, SendBuf, NULL, ByteCount) +* The caller wishes only to send and does not care about the received +* data. The driver ignores the received data in this case. +* +* XQspiPs_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount) +* The caller wishes to send and receive, but provides the same buffer +* for doing both. The driver sends the data and overwrites the send +* buffer with received data as it transfers the data. +* +* XQspiPs_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount) +* The caller wishes to only receive and does not care about sending +* data. In this case, the caller must still provide a send buffer, but +* it can be the same as the receive buffer if the caller does not care +* what it sends. The device must send N bytes of data if it wishes to +* receive N bytes of data. +*+* Although this function takes entire buffers as arguments, the driver can only +* transfer a limited number of bytes at a time, limited by the size of the +* FIFO. A call to this function only starts the transfer, then subsequent +* transfers of the data is performed by the interrupt service routine until +* the entire buffer has been transferred. The status callback function is +* called when the entire buffer has been sent/received. +* +* This function is non-blocking. The SetSlaveSelect function must be called +* prior to this function. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param SendBufPtr is a pointer to a data buffer that needs to be +* transmitted. This buffer must not be NULL. +* @param RecvBufPtr is a pointer to a buffer for received data. +* This argument can be NULL if do not care about receiving. +* @param ByteCount contains the number of bytes to send/receive. +* The number of bytes received always equals the number of bytes +* sent. +* +* @return +* - XST_SUCCESS if the buffers are successfully handed off to the +* device for transfer. +* - XST_DEVICE_BUSY indicates that a data transfer is already in +* progress. This is determined by the driver. +* +* @note +* +* This function is not thread-safe. The higher layer software must ensure that +* no two threads are transferring data on the QSPI bus at the same time. +* +******************************************************************************/ +int XQspiPs_Transfer(XQspiPs *InstancePtr, u8 *SendBufPtr, u8 *RecvBufPtr, + unsigned ByteCount) +{ + u32 StatusReg; + u32 ConfigReg; + u8 Instruction; + u32 Data; + unsigned int Index; + u8 TransCount = 0; + XQspiPsInstFormat *CurrInst; + XQspiPsInstFormat NewInst[2]; + u8 SwitchFlag = 0; + + CurrInst = &NewInst[0]; + + /* + * The RecvBufPtr argument can be null + */ + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(SendBufPtr != NULL); + Xil_AssertNonvoid(ByteCount > 0); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Check whether there is another transfer in progress. Not thread-safe. + */ + if (InstancePtr->IsBusy) { + return XST_DEVICE_BUSY; + } + + /* + * Set the busy flag, which will be cleared in the ISR when the + * transfer is entirely done. + */ + InstancePtr->IsBusy = TRUE; + + /* + * Set up buffer pointers. + */ + InstancePtr->SendBufferPtr = SendBufPtr; + InstancePtr->RecvBufferPtr = RecvBufPtr; + + InstancePtr->RequestedBytes = ByteCount; + InstancePtr->RemainingBytes = ByteCount; + + /* + * The first byte with every chip-select assertion is always + * expected to be an instruction for flash interface mode + */ + Instruction = *InstancePtr->SendBufferPtr; + + for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) { + if (Instruction == FlashInst[Index].OpCode) { + break; + } + } + + /* + * Set the RX FIFO threshold + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT); + + /* + * If the slave select is "Forced" or under manual control, + * set the slave select now, before beginning the transfer. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, + ConfigReg); + } + + /* + * Enable the device. + */ + XQspiPs_Enable(InstancePtr); + + /* + * Clear all the interrrupts. + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_SR_OFFSET, + XQSPIPS_IXR_WR_TO_CLR_MASK); + + if (Index < ARRAY_SIZE(FlashInst)) { + CurrInst = &FlashInst[Index]; + /* + * Check for WRSR instruction which has different size for + * Spansion (3 bytes) and Micron (2 bytes) + */ + if( (CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) && + (ByteCount == 3) ) { + CurrInst->InstSize = 3; + CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET; + } + } + + /* + * If instruction not present in table + */ + if (Index == ARRAY_SIZE(FlashInst)) { + /* + * Assign current instruction, size and TXD register to be used + * The InstSize mentioned in case of instructions greater than + * 4 bytes is not the actual size, but is indicative of + * the TXD register used. + * The remaining bytes of the instruction will be transmitted + * through TXD0 below. + */ + switch(ByteCount%4) + { + case XQSPIPS_SIZE_ONE: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_ONE; + CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + case XQSPIPS_SIZE_TWO: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_TWO; + CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + case XQSPIPS_SIZE_THREE: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_THREE; + CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + default: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_FOUR; + CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET; + break; + } + } + + /* + * If the instruction size in not 4 bytes then the data received needs + * to be shifted + */ + if( CurrInst->InstSize != 4 ) { + InstancePtr->ShiftReadData = 1; + } else { + InstancePtr->ShiftReadData = 0; + } + + /* Get the complete command (flash inst + address/data) */ + Data = *((u32 *)InstancePtr->SendBufferPtr); + InstancePtr->SendBufferPtr += CurrInst->InstSize; + InstancePtr->RemainingBytes -= CurrInst->InstSize; + if (InstancePtr->RemainingBytes < 0) { + InstancePtr->RemainingBytes = 0; + } + + /* Write the command to the FIFO */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + CurrInst->TxOffset, Data); + TransCount++; + + /* + * If switching from TXD1/2/3 to TXD0, then start transfer and + * check for FIFO empty + */ + if(SwitchFlag == 1) { + SwitchFlag = 0; + /* + * If, in Manual Start mode, start the transfer. + */ + if (XQspiPs_IsManualStart(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_MANSTRT_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + /* + * Wait for the transfer to finish by polling Tx fifo status. + */ + do { + StatusReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_SR_OFFSET); + } while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0); + + } + + /* + * Fill the Tx FIFO with as many bytes as it takes (or as many as + * we have to send). + */ + while ((InstancePtr->RemainingBytes > 0) && + (TransCount < XQSPIPS_FIFO_DEPTH)) { + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_TXD_00_OFFSET, + *((u32 *)InstancePtr->SendBufferPtr)); + InstancePtr->SendBufferPtr += 4; + InstancePtr->RemainingBytes -= 4; + if (InstancePtr->RemainingBytes < 0) { + InstancePtr->RemainingBytes = 0; + } + TransCount++; + } + + /* + * Enable QSPI interrupts (connecting to the interrupt controller and + * enabling interrupts should have been done by the caller). + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_IER_OFFSET, XQSPIPS_IXR_RXNEMPTY_MASK | + XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXOVR_MASK | + XQSPIPS_IXR_TXUF_MASK); + + /* + * If, in Manual Start mode, Start the transfer. + */ + if (XQspiPs_IsManualStart(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_MANSTRT_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* Transfers specified data on the QSPI bus in polled mode. +* +* The caller has the option of providing two different buffers for send and +* receive, or one buffer for both send and receive, or no buffer for receive. +* The receive buffer must be at least as big as the send buffer to prevent +* unwanted memory writes. This implies that the byte count passed in as an +* argument must be the smaller of the two buffers if they differ in size. +* Here are some sample usages: +*
+* XQspiPs_PolledTransfer(InstancePtr, SendBuf, RecvBuf, ByteCount) +* The caller wishes to send and receive, and provides two different +* buffers for send and receive. +* +* XQspiPs_PolledTransfer(InstancePtr, SendBuf, NULL, ByteCount) +* The caller wishes only to send and does not care about the received +* data. The driver ignores the received data in this case. +* +* XQspiPs_PolledTransfer(InstancePtr, SendBuf, SendBuf, ByteCount) +* The caller wishes to send and receive, but provides the same buffer +* for doing both. The driver sends the data and overwrites the send +* buffer with received data as it transfers the data. +* +* XQspiPs_PolledTransfer(InstancePtr, RecvBuf, RecvBuf, ByteCount) +* The caller wishes to only receive and does not care about sending +* data. In this case, the caller must still provide a send buffer, but +* it can be the same as the receive buffer if the caller does not care +* what it sends. The device must send N bytes of data if it wishes to +* receive N bytes of data. +* +*+* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param SendBufPtr is a pointer to a data buffer that needs to be +* transmitted. This buffer must not be NULL. +* @param RecvBufPtr is a pointer to a buffer for received data. +* This argument can be NULL if do not care about receiving. +* @param ByteCount contains the number of bytes to send/receive. +* The number of bytes received always equals the number of bytes +* sent. +* @return +* - XST_SUCCESS if the buffers are successfully handed off to the +* device for transfer. +* - XST_DEVICE_BUSY indicates that a data transfer is already in +* progress. This is determined by the driver. +* +* @note +* +* This function is not thread-safe. The higher layer software must ensure that +* no two threads are transferring data on the QSPI bus at the same time. +* +******************************************************************************/ +int XQspiPs_PolledTransfer(XQspiPs *InstancePtr, u8 *SendBufPtr, + u8 *RecvBufPtr, unsigned ByteCount) +{ + u32 StatusReg; + u32 ConfigReg; + u8 Instruction; + u32 Data; + u8 TransCount; + unsigned int Index; + XQspiPsInstFormat *CurrInst; + XQspiPsInstFormat NewInst[2]; + u8 SwitchFlag = 0; + u8 IsManualStart = FALSE; + u32 RxCount = 0; + + CurrInst = &NewInst[0]; + /* + * The RecvBufPtr argument can be NULL. + */ + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(SendBufPtr != NULL); + Xil_AssertNonvoid(ByteCount > 0); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Check whether there is another transfer in progress. Not thread-safe. + */ + if (InstancePtr->IsBusy) { + return XST_DEVICE_BUSY; + } + + /* + * Set the busy flag, which will be cleared when the transfer is + * entirely done. + */ + InstancePtr->IsBusy = TRUE; + + /* + * Set up buffer pointers. + */ + InstancePtr->SendBufferPtr = SendBufPtr; + InstancePtr->RecvBufferPtr = RecvBufPtr; + + InstancePtr->RequestedBytes = ByteCount; + InstancePtr->RemainingBytes = ByteCount; + + /* + * The first byte with every chip-select assertion is always + * expected to be an instruction for flash interface mode + */ + Instruction = *InstancePtr->SendBufferPtr; + + for (Index = 0 ; Index < ARRAY_SIZE(FlashInst); Index++) { + if (Instruction == FlashInst[Index].OpCode) { + break; + } + } + + /* + * Set the RX FIFO threshold + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXFIFO_THRESHOLD_OPT); + + /* + * If the slave select is "Forced" or under manual control, + * set the slave select now, before beginning the transfer. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, + ConfigReg); + } + + /* + * Enable the device. + */ + XQspiPs_Enable(InstancePtr); + + if (Index < ARRAY_SIZE(FlashInst)) { + + CurrInst = &FlashInst[Index]; + /* + * Check for WRSR instruction which has different size for + * Spansion (3 bytes) and Micron (2 bytes) + */ + if( (CurrInst->OpCode == XQSPIPS_FLASH_OPCODE_WRSR) && + (ByteCount == 3) ) { + CurrInst->InstSize = 3; + CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET; + } + } + + /* + * If instruction not present in table + */ + if (Index == ARRAY_SIZE(FlashInst)) { + /* + * Assign current instruction, size and TXD register to be used. + * The InstSize mentioned in case of instructions greater than 4 bytes + * is not the actual size, but is indicative of the TXD register used. + * The remaining bytes of the instruction will be transmitted + * through TXD0 below. + */ + switch(ByteCount%4) + { + case XQSPIPS_SIZE_ONE: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_ONE; + CurrInst->TxOffset = XQSPIPS_TXD_01_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + case XQSPIPS_SIZE_TWO: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_TWO; + CurrInst->TxOffset = XQSPIPS_TXD_10_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + case XQSPIPS_SIZE_THREE: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_THREE; + CurrInst->TxOffset = XQSPIPS_TXD_11_OFFSET; + if(ByteCount > 4) { + SwitchFlag = 1; + } + break; + default: + CurrInst->OpCode = Instruction; + CurrInst->InstSize = XQSPIPS_SIZE_FOUR; + CurrInst->TxOffset = XQSPIPS_TXD_00_OFFSET; + break; + } + } + + /* + * If the instruction size in not 4 bytes then the data received needs + * to be shifted + */ + if( CurrInst->InstSize != 4 ) { + InstancePtr->ShiftReadData = 1; + } else { + InstancePtr->ShiftReadData = 0; + } + TransCount = 0; + /* Get the complete command (flash inst + address/data) */ + Data = *((u32 *)InstancePtr->SendBufferPtr); + InstancePtr->SendBufferPtr += CurrInst->InstSize; + InstancePtr->RemainingBytes -= CurrInst->InstSize; + if (InstancePtr->RemainingBytes < 0) { + InstancePtr->RemainingBytes = 0; + } + + /* Write the command to the FIFO */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + CurrInst->TxOffset, Data); + ++TransCount; + + /* + * If switching from TXD1/2/3 to TXD0, then start transfer and + * check for FIFO empty + */ + if(SwitchFlag == 1) { + SwitchFlag = 0; + /* + * If, in Manual Start mode, start the transfer. + */ + if (XQspiPs_IsManualStart(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_MANSTRT_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + /* + * Wait for the transfer to finish by polling Tx fifo status. + */ + do { + StatusReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_SR_OFFSET); + } while ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0); + + } + + /* + * Check if manual start is selected and store it in a + * local varibale for reference. This is to avoid reading + * the config register everytime. + */ + IsManualStart = XQspiPs_IsManualStart(InstancePtr); + + /* + * Fill the DTR/FIFO with as many bytes as it will take (or as + * many as we have to send). + */ + while ((InstancePtr->RemainingBytes > 0) && + (TransCount < XQSPIPS_FIFO_DEPTH)) { + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_TXD_00_OFFSET, + *((u32 *)InstancePtr->SendBufferPtr)); + InstancePtr->SendBufferPtr += 4; + InstancePtr->RemainingBytes -= 4; + if (InstancePtr->RemainingBytes < 0) { + InstancePtr->RemainingBytes = 0; + } + ++TransCount; + } + + while((InstancePtr->RemainingBytes > 0) || + (InstancePtr->RequestedBytes > 0)) { + + /* + * Fill the TX FIFO with RX threshold no. of entries (or as + * many as we have to send, in case that's less). + */ + while ((InstancePtr->RemainingBytes > 0) && + (TransCount < XQSPIPS_RXFIFO_THRESHOLD_OPT)) { + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_TXD_00_OFFSET, + *((u32 *)InstancePtr->SendBufferPtr)); + InstancePtr->SendBufferPtr += 4; + InstancePtr->RemainingBytes -= 4; + if (InstancePtr->RemainingBytes < 0) { + InstancePtr->RemainingBytes = 0; + } + ++TransCount; + } + + /* + * If, in Manual Start mode, start the transfer. + */ + if (IsManualStart == TRUE) { + ConfigReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_MANSTRT_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + + /* + * Reset TransCount - this is only used to fill TX FIFO + * in the above loop; + * RxCount is used to keep track of data received + */ + TransCount = 0; + + /* + * Wait for RX FIFO to reach threshold (or) + * TX FIFO to become empty. + * The latter check is required for + * small transfers (<32 words) and + * when the last chunk in a large data transfer is < 32 words. + */ + + do { + StatusReg = XQspiPs_ReadReg( + InstancePtr->Config.BaseAddress, + XQSPIPS_SR_OFFSET); + } while ( ((StatusReg & XQSPIPS_IXR_TXOW_MASK) == 0) && + ((StatusReg & XQSPIPS_IXR_RXNEMPTY_MASK) == 0) ); + + /* + * A transmit has just completed. Process received data + * and check for more data to transmit. + * First get the data received as a result of the + * transmit that just completed. Receive data based on the + * count obtained while filling tx fifo. Always get + * the received data, but only fill the receive + * buffer if it points to something (the upper layer + * software may not care to receive data). + */ + while ((InstancePtr->RequestedBytes > 0) && + (RxCount < XQSPIPS_RXFIFO_THRESHOLD_OPT )) { + u32 Data; + + RxCount++; + + if (InstancePtr->RecvBufferPtr != NULL) { + if (InstancePtr->RequestedBytes < 4) { + Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + XQspiPs_GetReadData(InstancePtr, Data, + InstancePtr->RequestedBytes); + } else { + (*(u32 *)InstancePtr->RecvBufferPtr) = + XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + InstancePtr->RecvBufferPtr += 4; + InstancePtr->RequestedBytes -= 4; + if (InstancePtr->RequestedBytes < 0) { + InstancePtr->RequestedBytes = 0; + } + } + } else { + Data = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + InstancePtr->RequestedBytes -= 4; + } + } + RxCount = 0; + } + + /* + * If the Slave select lines are being manually controlled, disable + * them because the transfer is complete. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + + /* + * Clear the busy flag. + */ + InstancePtr->IsBusy = FALSE; + + /* + * Disable the device. + */ + XQspiPs_Disable(InstancePtr); + + /* + * Reset the RX FIFO threshold to one + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Read the flash in Linear QSPI mode. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param RecvBufPtr is a pointer to a buffer for received data. +* @param Address is the starting address within the flash from +* from where data needs to be read. +* @param ByteCount contains the number of bytes to receive. +* +* @return +* - XST_SUCCESS if read is performed +* - XST_FAILURE if Linear mode is not set +* +* @note None. +* +* +******************************************************************************/ +int XQspiPs_LqspiRead(XQspiPs *InstancePtr, u8 *RecvBufPtr, + u32 Address, unsigned ByteCount) +{ + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(RecvBufPtr != NULL); + Xil_AssertNonvoid(ByteCount > 0); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + +#ifndef XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR +#define XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR 0xFC000000 +#endif + /* + * Enable the controller + */ + XQspiPs_Enable(InstancePtr); + + if (XQspiPs_GetLqspiConfigReg(InstancePtr) & + XQSPIPS_LQSPI_CR_LINEAR_MASK) { + memcpy((void*)RecvBufPtr, + (const void*)(XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR + + Address), + (size_t)ByteCount); + return XST_SUCCESS; + } else { + return XST_FAILURE; + } + + /* + * Disable the controller + */ + XQspiPs_Disable(InstancePtr); + +} + +/*****************************************************************************/ +/** +* +* Selects the slave with which the master communicates. +* +* The user is not allowed to select the slave while a transfer is in progress. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return +* - XST_SUCCESS if the slave is selected or deselected +* successfully. +* - XST_DEVICE_BUSY if a transfer is in progress, slave cannot be +* changed. +* +* @note +* +* This function only sets the slave which will be selected when a transfer +* occurs. The slave is not selected when the QSPI is idle. +* +******************************************************************************/ +int XQspiPs_SetSlaveSelect(XQspiPs *InstancePtr) +{ + u32 ConfigReg; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Do not allow the slave select to change while a transfer is in + * progress. Not thread-safe. + */ + if (InstancePtr->IsBusy) { + return XST_DEVICE_BUSY; + } + + /* + * Select the slave + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg &= ~XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Sets the status callback function, the status handler, which the driver +* calls when it encounters conditions that should be reported to upper +* layer software. The handler executes in an interrupt context, so it must +* minimize the amount of processing performed. One of the following status +* events is passed to the status handler. +* +*
+* +* XST_SPI_TRANSFER_DONE The requested data transfer is done +* +* XST_SPI_TRANSMIT_UNDERRUN As a slave device, the master clocked data +* but there were none available in the transmit +* register/FIFO. This typically means the slave +* application did not issue a transfer request +* fast enough, or the processor/driver could not +* fill the transmit register/FIFO fast enough. +* +* XST_SPI_RECEIVE_OVERRUN The QSPI device lost data. Data was received +* but the receive data register/FIFO was full. +* +*+* @param InstancePtr is a pointer to the XQspiPs instance. +* @param CallBackRef is the upper layer callback reference passed back +* when the callback function is invoked. +* @param FuncPtr is the pointer to the callback function. +* +* @return None. +* +* @note +* +* The handler is called within interrupt context, so it should do its work +* quickly and queue potentially time-consuming work to a task-level thread. +* +******************************************************************************/ +void XQspiPs_SetStatusHandler(XQspiPs *InstancePtr, void *CallBackRef, + XQspiPs_StatusHandler FuncPtr) +{ + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(FuncPtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + InstancePtr->StatusHandler = FuncPtr; + InstancePtr->StatusRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* This is a stub for the status callback. The stub is here in case the upper +* layers forget to set the handler. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param StatusEvent is the event that just occurred. +* @param ByteCount is the number of bytes transferred up until the event +* occurred. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +static void StubStatusHandler(void *CallBackRef, u32 StatusEvent, + unsigned ByteCount) +{ + (void) CallBackRef; + (void) StatusEvent; + (void) ByteCount; + + Xil_AssertVoidAlways(); +} + +/*****************************************************************************/ +/** +* +* The interrupt handler for QSPI interrupts. This function must be connected +* by the user to an interrupt controller. +* +* The interrupts that are handled are: +* +* +* - Data Transmit Register (FIFO) Empty. This interrupt is generated when the +* transmit register or FIFO is empty. The driver uses this interrupt during a +* transmission to continually send/receive data until the transfer is done. +* +* - Data Transmit Register (FIFO) Underflow. This interrupt is generated when +* the QSPI device, when configured as a slave, attempts to read an empty +* DTR/FIFO. An empty DTR/FIFO usually means that software is not giving the +* device data in a timely manner. No action is taken by the driver other than +* to inform the upper layer software of the error. +* +* - Data Receive Register (FIFO) Overflow. This interrupt is generated when the +* QSPI device attempts to write a received byte to an already full DRR/FIFO. +* A full DRR/FIFO usually means software is not emptying the data in a timely +* manner. No action is taken by the driver other than to inform the upper +* layer software of the error. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return None. +* +* @note +* +* The slave select register is being set to deselect the slave when a transfer +* is complete. +* +******************************************************************************/ +void XQspiPs_InterruptHandler(void *InstancePtr) +{ + XQspiPs *QspiPtr = (XQspiPs *)InstancePtr; + u32 IntrStatus; + u32 ConfigReg; + u32 Data; + u32 TransCount; + u32 Count = 0; + unsigned BytesDone; /* Number of bytes done so far. */ + + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(QspiPtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Immediately clear the interrupts in case the ISR causes another + * interrupt to be generated. If we clear at the end of the ISR, + * we may miss newly generated interrupts. This occurs because we + * transmit from within the ISR, which could potentially cause another + * TX_EMPTY interrupt. + */ + IntrStatus = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress, + XQSPIPS_SR_OFFSET); + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_SR_OFFSET, + (IntrStatus & XQSPIPS_IXR_WR_TO_CLR_MASK)); + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, XQSPIPS_IDR_OFFSET, + XQSPIPS_IXR_TXOW_MASK | XQSPIPS_IXR_RXNEMPTY_MASK | + XQSPIPS_IXR_RXOVR_MASK | XQSPIPS_IXR_TXUF_MASK); + + if ((IntrStatus & XQSPIPS_IXR_TXOW_MASK) || + (IntrStatus & XQSPIPS_IXR_RXNEMPTY_MASK)) { + + /* + * Rx FIFO has just reached threshold no. of entries. + * Read threshold no. of entries from RX FIFO + * Another possiblity of entering this loop is when + * the last byte has been transmitted and TX FIFO is empty, + * in which case, read all the data from RX FIFO. + * Always get the received data, but only fill the + * receive buffer if it is not null (it can be null when + * the device does not care to receive data). + */ + TransCount = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes; + if (TransCount % 4) { + TransCount = TransCount/4 + 1; + } else { + TransCount = TransCount/4; + } + + while ((Count < TransCount) && + (Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) { + + if (QspiPtr->RecvBufferPtr != NULL) { + if (QspiPtr->RequestedBytes < 4) { + Data = XQspiPs_ReadReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + XQspiPs_GetReadData(QspiPtr, Data, + QspiPtr->RequestedBytes); + } else { + (*(u32 *)QspiPtr->RecvBufferPtr) = + XQspiPs_ReadReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + QspiPtr->RecvBufferPtr += 4; + QspiPtr->RequestedBytes -= 4; + if (QspiPtr->RequestedBytes < 0) { + QspiPtr->RequestedBytes = 0; + } + } + } else { + XQspiPs_ReadReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXD_OFFSET); + QspiPtr->RequestedBytes -= 4; + if (QspiPtr->RequestedBytes < 0) { + QspiPtr->RequestedBytes = 0; + } + + } + Count++; + } + Count = 0; + /* + * Interrupt asserted as TX_OW got asserted + * See if there is more data to send. + * Fill TX FIFO with RX threshold no. of entries or + * remaining entries (in case that is less than threshold) + */ + while ((QspiPtr->RemainingBytes > 0) && + (Count < XQSPIPS_RXFIFO_THRESHOLD_OPT)) { + /* + * Send more data. + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_TXD_00_OFFSET, + *((u32 *)QspiPtr->SendBufferPtr)); + QspiPtr->SendBufferPtr += 4; + QspiPtr->RemainingBytes -= 4; + if (QspiPtr->RemainingBytes < 0) { + QspiPtr->RemainingBytes = 0; + } + + Count++; + } + + if ((QspiPtr->RemainingBytes == 0) && + (QspiPtr->RequestedBytes == 0)) { + /* + * No more data to send. Disable the interrupt + * and inform the upper layer software that the + * transfer is done. The interrupt will be re-enabled + * when another transfer is initiated. + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_IDR_OFFSET, + XQSPIPS_IXR_RXNEMPTY_MASK | + XQSPIPS_IXR_TXOW_MASK | + XQSPIPS_IXR_RXOVR_MASK | + XQSPIPS_IXR_TXUF_MASK); + + /* + * If the Slave select is being manually controlled, + * disable it because the transfer is complete. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg( + QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, + ConfigReg); + } + + /* + * Clear the busy flag. + */ + QspiPtr->IsBusy = FALSE; + + /* + * Disable the device. + */ + XQspiPs_Disable(QspiPtr); + + /* + * Reset the RX FIFO threshold to one + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE); + + QspiPtr->StatusHandler(QspiPtr->StatusRef, + XST_SPI_TRANSFER_DONE, + QspiPtr->RequestedBytes); + } else { + /* + * Enable the TXOW interrupt. + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_IER_OFFSET, + XQSPIPS_IXR_RXNEMPTY_MASK | + XQSPIPS_IXR_TXOW_MASK | + XQSPIPS_IXR_RXOVR_MASK | + XQSPIPS_IXR_TXUF_MASK); + /* + * If, in Manual Start mode, start the transfer. + */ + if (XQspiPs_IsManualStart(QspiPtr)) { + ConfigReg = XQspiPs_ReadReg( + QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_MANSTRT_MASK; + XQspiPs_WriteReg( + QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + } + } + + /* + * Check for overflow and underflow errors. + */ + if (IntrStatus & XQSPIPS_IXR_RXOVR_MASK) { + BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes; + QspiPtr->IsBusy = FALSE; + + /* + * If the Slave select lines is being manually controlled, + * disable it because the transfer is complete. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg( + QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + + /* + * Disable the device. + */ + XQspiPs_Disable(QspiPtr); + + /* + * Reset the RX FIFO threshold to one + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE); + + QspiPtr->StatusHandler(QspiPtr->StatusRef, + XST_SPI_RECEIVE_OVERRUN, BytesDone); + } + + if (IntrStatus & XQSPIPS_IXR_TXUF_MASK) { + BytesDone = QspiPtr->RequestedBytes - QspiPtr->RemainingBytes; + + QspiPtr->IsBusy = FALSE; + /* + * If the Slave select lines is being manually controlled, + * disable it because the transfer is complete. + */ + if (XQspiPs_IsManualChipSelect(InstancePtr)) { + ConfigReg = XQspiPs_ReadReg( + QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_SSCTRL_MASK; + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, ConfigReg); + } + + /* + * Disable the device. + */ + XQspiPs_Disable(QspiPtr); + + /* + * Reset the RX FIFO threshold to one + */ + XQspiPs_WriteReg(QspiPtr->Config.BaseAddress, + XQSPIPS_RXWR_OFFSET, XQSPIPS_RXWR_RESET_VALUE); + + QspiPtr->StatusHandler(QspiPtr->StatusRef, + XST_SPI_TRANSMIT_UNDERRUN, BytesDone); + } +} + + +/*****************************************************************************/ +/** +* +* Copies data from Data to the Receive buffer. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param Data is the data which needs to be copied to the Rx buffer. +* @param Size is the number of bytes to be copied to the Receive buffer. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +static void XQspiPs_GetReadData(XQspiPs *InstancePtr, u32 Data, u8 Size) +{ + u8 DataByte3; + + if (InstancePtr->RecvBufferPtr) { + switch (Size) { + case 1: + if (InstancePtr->ShiftReadData == 1) { + *((u8 *)InstancePtr->RecvBufferPtr) = + ((Data & 0xFF000000) >> 24); + } else { + *((u8 *)InstancePtr->RecvBufferPtr) = + (Data & 0xFF); + } + InstancePtr->RecvBufferPtr += 1; + break; + case 2: + if (InstancePtr->ShiftReadData == 1) { + *((u16 *)InstancePtr->RecvBufferPtr) = + ((Data & 0xFFFF0000) >> 16); + } else { + *((u16 *)InstancePtr->RecvBufferPtr) = + (Data & 0xFFFF); + } + InstancePtr->RecvBufferPtr += 2; + break; + case 3: + if (InstancePtr->ShiftReadData == 1) { + *((u16 *)InstancePtr->RecvBufferPtr) = + ((Data & 0x00FFFF00) >> 8); + InstancePtr->RecvBufferPtr += 2; + DataByte3 = ((Data & 0xFF000000) >> 24); + *((u8 *)InstancePtr->RecvBufferPtr) = DataByte3; + } else { + *((u16 *)InstancePtr->RecvBufferPtr) = + (Data & 0xFFFF); + InstancePtr->RecvBufferPtr += 2; + DataByte3 = ((Data & 0x00FF0000) >> 16); + *((u8 *)InstancePtr->RecvBufferPtr) = DataByte3; + } + InstancePtr->RecvBufferPtr += 1; + break; + default: + /* This will never execute */ + break; + } + } + InstancePtr->ShiftReadData = 0; + InstancePtr->RequestedBytes -= Size; + if (InstancePtr->RequestedBytes < 0) { + InstancePtr->RequestedBytes = 0; + } +} diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips.h b/XilinxProcessorIPLib/drivers/qspips/src/xqspips.h new file mode 100644 index 00000000..e93f879d --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips.h @@ -0,0 +1,782 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips.h +* +* This file contains the implementation of the XQspiPs driver. It supports only +* master mode. User documentation for the driver functions is contained in this +* file in the form of comment blocks at the front of each function. +* +* A QSPI device connects to an QSPI bus through a 4-wire serial interface. +* The QSPI bus is a full-duplex, synchronous bus that facilitates communication +* between one master and one slave. The device is always full-duplex, +* which means that for every byte sent, one is received, and vice-versa. +* The master controls the clock, so it can regulate when it wants to +* send or receive data. The slave is under control of the master, it must +* respond quickly since it has no control of the clock and must send/receive +* data as fast or as slow as the master does. +* +* Linear Mode +* The Linear Quad-SPI Controller extends the existing Quad-SPI Controller�s +* functionality by adding a linear addressing scheme that allows the SPI flash +* memory subsystem to behave like a typical ROM device. The new feature hides +* the normal SPI protocol from a master reading from the SPI flash memory. The +* feature improves both the user friendliness and the overall read memory +* throughput over that of the current Quad-SPI Controller by lessening the +* amount of software overheads required and by the use of the faster AXI +* interface. +* +* Initialization & Configuration +* +* The XQspiPs_Config structure is used by the driver to configure itself. This +* configuration structure is typically created by the tool-chain based on HW +* build properties. +* +* To support multiple runtime loading and initialization strategies employed by +* various operating systems, the driver instance can be initialized in the +* following way: +* - XQspiPs_LookupConfig(DeviceId) - Use the device identifier to find +* static configuration structure defined in xqspips_g.c. This is setup +* by the tools. For some operating systems the config structure will be +* initialized by the software and this call is not needed. +* - XQspiPs_CfgInitialize(InstancePtr, CfgPtr, EffectiveAddr) - Uses a +* configuration structure provided by the caller. If running in a system +* with address translation, the provided virtual memory base address +* replaces the physical address present in the configuration structure. +* +* Multiple Masters +* +* More than one master can exist, but arbitration is the responsibility of +* the higher layer software. The device driver does not perform any type of +* arbitration. +* +* Modes of Operation +* +* There are four modes to perform a data transfer and the selection of a mode +* is based on Chip Select(CS) and Start. These two options individually, can +* be controlled either by software(Manual) or hardware(Auto). +* - Auto CS: Chip select is automatically asserted as soon as the first word +* is written into the TXFIFO and de asserted when the TXFIFO becomes +* empty +* - Manual CS: Software must assert and de assert CS. +* - Auto Start: Data transmission starts as soon as there is data in the +* TXFIFO and stalls when the TXFIFO is empty +* - Manual Start: Software must start data transmission at the beginning of +* the transaction or whenever the TXFIFO has become empty +* +* The preferred combination is Manual CS and Auto Start. +* In this combination, the software asserts CS before loading any data into +* TXFIFO. In Auto Start mode, whenever data is in TXFIFO, controller sends it +* out until TXFIFO becomes empty. The software reads the RXFIFO whenever the +* data is available. If no further data, software disables CS. +* +* Risks/challenges of other combinations: +* - Manual CS and Manual Start: Manual Start bit should be set after each +* TXFIFO write otherwise there could be a race condition where the TXFIFO +* becomes empty before the new word is written. In that case the +* transmission stops. +* - Auto CS with Manual or Auto Start: It is very difficult for software to +* keep the TXFIFO filled. Whenever the TXFIFO runs empty, CS is de asserted. +* This results in a single transaction to be split into multiple pieces each +* with its own chip select. This will result in garbage data to be sent. +* +* Interrupts +* +* The user must connect the interrupt handler of the driver, +* XQspiPs_InterruptHandler, to an interrupt system such that it will be +* called when an interrupt occurs. This function does not save and restore +* the processor context such that the user must provide this processing. +* +* The driver handles the following interrupts: +* - Data Transmit Register/FIFO Underflow +* - Data Receive Register/FIFO Not Empty +* - Data Transmit Register/FIFO Overwater +* - Data Receive Register/FIFO Overrun +* +* The Data Transmit Register/FIFO Overwater interrupt -- indicates that the +* QSPI device has transmitted the data available to transmit, and now its data +* register and FIFO is ready to accept more data. The driver uses this +* interrupt to indicate progress while sending data. The driver may have +* more data to send, in which case the data transmit register and FIFO is +* filled for subsequent transmission. When this interrupt arrives and all +* the data has been sent, the driver invokes the status callback with a +* value of XST_SPI_TRANSFER_DONE to inform the upper layer software that +* all data has been sent. +* +* The Data Transmit Register/FIFO Underflow interrupt -- indicates that, +* as slave, the QSPI device was required to transmit but there was no data +* available to transmit in the transmit register (or FIFO). This may not +* be an error if the master is not expecting data. But in the case where +* the master is expecting data, this serves as a notification of such a +* condition. The driver reports this condition to the upper layer +* software through the status handler. +* +* The Data Receive Register/FIFO Overrun interrupt -- indicates that the QSPI +* device received data and subsequently dropped the data because the data +* receive register and FIFO was full. The driver reports this condition to the +* upper layer software through the status handler. This likely indicates a +* problem with the higher layer protocol, or a problem with the slave +* performance. +* +* +* Polled Operation +* +* Transfer in polled mode is supported through a separate interface function +* XQspiPs_PolledTransfer(). Unlike the transfer function in the interrupt mode, +* this function blocks until all data has been sent/received. +* +* Device Busy +* +* Some operations are disallowed when the device is busy. The driver tracks +* whether a device is busy. The device is considered busy when a data transfer +* request is outstanding, and is considered not busy only when that transfer +* completes (or is aborted with a mode fault error). +* +* Device Configuration +* +* The device can be configured in various ways during the FPGA implementation +* process. Configuration parameters are stored in the xqspips_g.c file or +* passed in via XQspiPs_CfgInitialize(). A table is defined where each entry +* contains configuration information for an QSPI device, including the base +* address for the device. +* +* RTOS Independence +* +* This driver is intended to be RTOS and processor independent. It works with +* physical addresses only. Any needs for dynamic memory management, threads or +* thread mutual exclusion, virtual memory, or cache control must be satisfied +* by the layer above this driver. +* +* NOTE: This driver was always tested with endianess set to little-endian. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00a sdm 11/25/10 First release, based on the PS SPI driver... +* 1.01a sdm 11/22/11 Added TCL file for generating QSPI parameters +* in xparameters.h +* 2.00a kka 07/25/12 Added a few register defines for CR 670297 +* Removed code related to mode fault for CR 671468 +* The XQspiPs_SetSlaveSelect has been modified to remove +* the argument of the slave select as the QSPI controller +* only supports one slave. +* XQspiPs_GetSlaveSelect API has been removed +* Added a flag ShiftReadData to the instance structure +*. and is used in the XQspiPs_GetReadData API. +* The ShiftReadData Flag indicates whether the data +* read from the Rx FIFO needs to be shifted +* in cases where the data is less than 4 bytes +* Removed the selection for the following options: +* Master mode (XQSPIPS_MASTER_OPTION) and +* Flash interface mode (XQSPIPS_FLASH_MODE_OPTION) option +* as the QSPI driver supports the Master mode +* and Flash Interface mode and doesnot support +* Slave mode or the legacy mode. +* Modified the XQspiPs_PolledTransfer and XQspiPs_Transfer +* APIs so that the last argument (IsInst) specifying whether +* it is instruction or data has been removed. The first byte +* in the SendBufPtr argument of these APIs specify the +* instruction to be sent to the Flash Device. +* This version of the driver fixes CRs 670197/663787/ +* 670297/671468. +* Added the option for setting the Holdb_dr bit in the +* configuration options, XQSPIPS_HOLD_B_DRIVE_OPTION +* is the option to be used for setting this bit in the +* configuration register. +* The XQspiPs_PolledTransfer function has been updated +* to fill the data to fifo depth. +* 2.01a sg 02/03/13 Added flash opcodes for DUAL_IO_READ,QUAD_IO_READ. +* Added macros for Set/Get Rx Watermark. Changed QSPI +* Enable/Disable macro argument from BaseAddress to +* Instance Pointer. Added DelayNss argument to SetDelays +* and GetDelays API's. +* Created macros XQspiPs_IsManualStart and +* XQspiPs_IsManualChipSelect. +* Changed QSPI transfer logic for polled and interrupt +* modes to be based on filled tx fifo count and receive +* based on it. RXNEMPTY interrupt is not used. +* Added assertions to XQspiPs_LqspiRead function. +* SetDelays and GetDelays API's include DelayNss parameter. +* Added defines for DelayNss,Rx Watermark,Interrupts +* which need write to clear. Removed Read zeros mask from +* LQSPI Config register. Renamed Fixed burst error to +* data FSM error in LQSPI Status register. +* +* 2.02a hk 05/07/13 Added ConnectionMode to config structure. +* Corresponds to C_QSPI_MODE - 0:Single, 1:Stacked, 2:Parallel +* Added enable and disable to the XQspiPs_LqspiRead() function +* Removed XQspi_Reset() in Set_Options() function when +* LQSPI_MODE_OPTION is set. +* Added instructions for bank selection, die erase and +* flag status register to the flash instruction table +* Handling for instructions not in flash instruction +* table added. Checking for Tx FIFO empty when switching from +* TXD1/2/3 to TXD0 added. If WRSR instruction is sent with +* byte count 3 (spansion), instruction size and TXD register +* changed accordingly. CR# 712502 and 703869. +* Added prefix to constant definitions for ConnectionMode +* Added (#ifdef linear base address) in the Linear read function. +* Changed XPAR_XQSPIPS_0_LINEAR_BASEADDR to +* XPAR_PS7_QSPI_LINEAR_0_S_AXI_BASEADDR in +* XQspiPs_LqspiRead function. Fix for CR#718141. +* +* 2.03a hk 09/17/13 Modified polled and interrupt transfers to make use of +* thresholds. This is to improve performance. +* Added API's for QSPI reset and +* linear mode initialization for boot. +* Added RX and TX threshold reset to one in XQspiPs_Abort. +* Added RX threshold reset(1) after transfer in polled and +* interrupt transfers. Made changes to make sure threshold +* change is done only when no transfer is in progress. +* Updated linear init API for parallel and stacked modes. +* CR#737760. +* 3.1 hk 08/13/14 When writing to the configuration register, set/reset +* required bits leaving reserved bits untouched. CR# 796813. +* +*+* +******************************************************************************/ +#ifndef XQSPIPS_H /* prevent circular inclusions */ +#define XQSPIPS_H /* by using protection macros */ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#include "xstatus.h" +#include "xqspips_hw.h" +#include
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xqspips.h" +#include "xparameters.h" + +/************************** Constant Definitions *****************************/ + + +/**************************** Type Definitions *******************************/ + + +/***************** Macros (Inline Functions) Definitions *********************/ + + +/************************** Function Prototypes ******************************/ + + +/************************** Variable Prototypes ******************************/ + +/** + * This table contains configuration information for each QSPI device + * in the system. + */ +XQspiPs_Config XQspiPs_ConfigTable[XPAR_XQSPIPS_NUM_INSTANCES] = { + { + XPAR_XQSPIPS_0_DEVICE_ID, /* Device ID for instance */ + XPAR_XQSPIPS_0_BASEADDR, /* Device base address */ + XPAR_XQSPIPS_0_QSPI_CLK_FREQ_HZ, + XPAR_XQSPIPS_0_QSPI_MODE + }, +}; diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.c b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.c new file mode 100644 index 00000000..78d98644 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.c @@ -0,0 +1,221 @@ +/****************************************************************************** +* +* Copyright (C) 2013 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_hw.c +* +* Contains low level functions, primarily reset related. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 2.03a hk 09/17/13 First release +* 3.1 hk 06/19/14 When writing to the configuration register, set/reset +* required bits leaving reserved bits untouched. CR# 796813. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xqspips_hw.h" +#include "xqspips.h" + +/************************** Constant Definitions *****************************/ + +/** @name Pre-scaler value for divided by 4 + * + * Pre-scaler value for divided by 4 + * + * @{ + */ +#define XQSPIPS_CR_PRESC_DIV_BY_4 0x01 +/* @} */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + + +/*****************************************************************************/ +/** +* +* Resets QSPI by disabling the device and bringing it to reset state through +* register writes. +* +* @param None +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void XQspiPs_ResetHw(u32 BaseAddress) +{ + u32 ConfigReg; + + /* + * Disable interrupts + */ + XQspiPs_WriteReg(BaseAddress, XQSPIPS_IDR_OFFSET, + XQSPIPS_IXR_DISABLE_ALL); + + /* + * Disable device + */ + XQspiPs_WriteReg(BaseAddress, XQSPIPS_ER_OFFSET, + 0); + + /* + * De-assert slave select lines. + */ + ConfigReg = XQspiPs_ReadReg(BaseAddress, XQSPIPS_CR_OFFSET); + ConfigReg |= (XQSPIPS_CR_SSCTRL_MASK | XQSPIPS_CR_SSFORCE_MASK); + XQspiPs_WriteReg(BaseAddress, XQSPIPS_CR_OFFSET, ConfigReg); + + /* + * Write default value to RX and TX threshold registers + * RX threshold should be set to 1 here because the corresponding + * status bit is used next to clear the RXFIFO + */ + XQspiPs_WriteReg(BaseAddress, XQSPIPS_TXWR_OFFSET, + (XQSPIPS_TXWR_RESET_VALUE & XQSPIPS_TXWR_MASK)); + XQspiPs_WriteReg(BaseAddress, XQSPIPS_RXWR_OFFSET, + (XQSPIPS_RXWR_RESET_VALUE & XQSPIPS_RXWR_MASK)); + + /* + * Clear RXFIFO + */ + while ((XQspiPs_ReadReg(BaseAddress,XQSPIPS_SR_OFFSET) & + XQSPIPS_IXR_RXNEMPTY_MASK) != 0) { + XQspiPs_ReadReg(BaseAddress, XQSPIPS_RXD_OFFSET); + } + + /* + * Clear status register by reading register and + * writing 1 to clear the write to clear bits + */ + XQspiPs_ReadReg(BaseAddress, XQSPIPS_SR_OFFSET); + XQspiPs_WriteReg(BaseAddress, XQSPIPS_SR_OFFSET, + XQSPIPS_IXR_WR_TO_CLR_MASK); + + /* + * Write default value to configuration register + */ + ConfigReg = XQspiPs_ReadReg(BaseAddress, XQSPIPS_CR_OFFSET); + ConfigReg |= XQSPIPS_CR_RESET_MASK_SET; + ConfigReg &= ~XQSPIPS_CR_RESET_MASK_CLR; + XQspiPs_WriteReg(BaseAddress, XQSPIPS_CR_OFFSET, ConfigReg); + + /* + * De-select linear mode + */ + XQspiPs_WriteReg(BaseAddress, XQSPIPS_LQSPI_CR_OFFSET, + 0x0); + +} + +/*****************************************************************************/ +/** +* +* Initializes QSPI to Linear mode with default QSPI boot settings. +* +* @param None +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void XQspiPs_LinearInit(u32 BaseAddress) +{ + u32 BaudRateDiv; + u32 LinearCfg; + u32 ConfigReg; + + /* + * Baud rate divisor for dividing by 4. Value of CR bits [5:3] + * should be set to 0x001; hence shift the value and use the mask. + */ + BaudRateDiv = ( (XQSPIPS_CR_PRESC_DIV_BY_4) << + XQSPIPS_CR_PRESC_SHIFT) & XQSPIPS_CR_PRESC_MASK; + /* + * Write configuration register with default values, slave selected & + * pre-scaler value for divide by 4 + */ + ConfigReg = XQspiPs_ReadReg(BaseAddress, XQSPIPS_CR_OFFSET); + ConfigReg |= (XQSPIPS_CR_RESET_MASK_SET | BaudRateDiv); + ConfigReg &= ~(XQSPIPS_CR_RESET_MASK_CLR | XQSPIPS_CR_SSCTRL_MASK); + XQspiPs_WriteReg(BaseAddress, XQSPIPS_CR_OFFSET, ConfigReg); + + /* + * Write linear configuration register with default value - + * enable linear mode and use fast read. + */ + + if(XPAR_XQSPIPS_0_QSPI_MODE == XQSPIPS_CONNECTION_MODE_SINGLE){ + + LinearCfg = XQSPIPS_LQSPI_CR_RST_STATE; + + }else if(XPAR_XQSPIPS_0_QSPI_MODE == + XQSPIPS_CONNECTION_MODE_STACKED){ + + LinearCfg = XQSPIPS_LQSPI_CR_RST_STATE | + XQSPIPS_LQSPI_CR_TWO_MEM_MASK; + + }else if(XPAR_XQSPIPS_0_QSPI_MODE == + XQSPIPS_CONNECTION_MODE_PARALLEL){ + + LinearCfg = XQSPIPS_LQSPI_CR_RST_STATE | + XQSPIPS_LQSPI_CR_TWO_MEM_MASK | + XQSPIPS_LQSPI_CR_SEP_BUS_MASK; + + } + + XQspiPs_WriteReg(BaseAddress, XQSPIPS_LQSPI_CR_OFFSET, + LinearCfg); + + /* + * Enable device + */ + XQspiPs_WriteReg(BaseAddress, XQSPIPS_ER_OFFSET, + XQSPIPS_ER_ENABLE_MASK); + +} diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.h b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.h new file mode 100644 index 00000000..08dc53c1 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_hw.h @@ -0,0 +1,385 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_hw.h +* +* This header file contains the identifiers and basic HW access driver +* functions (or macros) that can be used to access the device. Other driver +* functions are defined in xqspips.h. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.00a ka 07/25/12 Added a few register defines for CR 670297 +* and removed some defines of reserved fields for +* CR 671468 +* Added define XQSPIPS_CR_HOLD_B_MASK for Holdb_dr +* bit in Configuration register. +* 2.01a sg 02/03/13 Added defines for DelayNss,Rx Watermark,Interrupts +* which need write to clear. Removed Read zeros mask from +* LQSPI Config register. +* 2.03a hk 08/22/13 Added prototypes of API's for QSPI reset and +* linear mode initialization for boot. Added related +* constant definitions. +* 3.1 hk 08/13/14 Changed definition of CR reset value masks to set/reset +* required bits leaving reserved bits untouched. CR# 796813. +* +*+* +******************************************************************************/ +#ifndef XQSPIPS_HW_H /* prevent circular inclusions */ +#define XQSPIPS_HW_H /* by using protection macros */ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#include "xil_types.h" +#include "xil_assert.h" +#include "xil_io.h" +#include "xparameters.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an QSPI device. + * @{ + */ +#define XQSPIPS_CR_OFFSET 0x00 /**< Configuration Register */ +#define XQSPIPS_SR_OFFSET 0x04 /**< Interrupt Status */ +#define XQSPIPS_IER_OFFSET 0x08 /**< Interrupt Enable */ +#define XQSPIPS_IDR_OFFSET 0x0c /**< Interrupt Disable */ +#define XQSPIPS_IMR_OFFSET 0x10 /**< Interrupt Enabled Mask */ +#define XQSPIPS_ER_OFFSET 0x14 /**< Enable/Disable Register */ +#define XQSPIPS_DR_OFFSET 0x18 /**< Delay Register */ +#define XQSPIPS_TXD_00_OFFSET 0x1C /**< Transmit 4-byte inst/data */ +#define XQSPIPS_RXD_OFFSET 0x20 /**< Data Receive Register */ +#define XQSPIPS_SICR_OFFSET 0x24 /**< Slave Idle Count */ +#define XQSPIPS_TXWR_OFFSET 0x28 /**< Transmit FIFO Watermark */ +#define XQSPIPS_RXWR_OFFSET 0x2C /**< Receive FIFO Watermark */ +#define XQSPIPS_GPIO_OFFSET 0x30 /**< GPIO Register */ +#define XQSPIPS_LPBK_DLY_ADJ_OFFSET 0x38 /**< Loopback Delay Adjust Reg */ +#define XQSPIPS_TXD_01_OFFSET 0x80 /**< Transmit 1-byte inst */ +#define XQSPIPS_TXD_10_OFFSET 0x84 /**< Transmit 2-byte inst */ +#define XQSPIPS_TXD_11_OFFSET 0x88 /**< Transmit 3-byte inst */ +#define XQSPIPS_LQSPI_CR_OFFSET 0xA0 /**< Linear QSPI config register */ +#define XQSPIPS_LQSPI_SR_OFFSET 0xA4 /**< Linear QSPI status register */ +#define XQSPIPS_MOD_ID_OFFSET 0xFC /**< Module ID register */ + +/* @} */ + +/** @name Configuration Register + * + * This register contains various control bits that + * affect the operation of the QSPI device. Read/Write. + * @{ + */ + +#define XQSPIPS_CR_IFMODE_MASK 0x80000000 /**< Flash mem interface mode */ +#define XQSPIPS_CR_ENDIAN_MASK 0x04000000 /**< Tx/Rx FIFO endianness */ +#define XQSPIPS_CR_MANSTRT_MASK 0x00010000 /**< Manual Transmission Start */ +#define XQSPIPS_CR_MANSTRTEN_MASK 0x00008000 /**< Manual Transmission Start + Enable */ +#define XQSPIPS_CR_SSFORCE_MASK 0x00004000 /**< Force Slave Select */ +#define XQSPIPS_CR_SSCTRL_MASK 0x00000400 /**< Slave Select Decode */ +#define XQSPIPS_CR_SSCTRL_SHIFT 10 /**< Slave Select Decode shift */ +#define XQSPIPS_CR_DATA_SZ_MASK 0x000000C0 /**< Size of word to be + transferred */ +#define XQSPIPS_CR_PRESC_MASK 0x00000038 /**< Prescaler Setting */ +#define XQSPIPS_CR_PRESC_SHIFT 3 /**< Prescaler shift */ +#define XQSPIPS_CR_PRESC_MAXIMUM 0x07 /**< Prescaler maximum value */ + +#define XQSPIPS_CR_CPHA_MASK 0x00000004 /**< Phase Configuration */ +#define XQSPIPS_CR_CPOL_MASK 0x00000002 /**< Polarity Configuration */ + +#define XQSPIPS_CR_MSTREN_MASK 0x00000001 /**< Master Mode Enable */ + +#define XQSPIPS_CR_HOLD_B_MASK 0x00080000 /**< HOLD_B Pin Drive Enable */ + +#define XQSPIPS_CR_REF_CLK_MASK 0x00000100 /**< Ref clk bit - should be 0 */ + +/* Deselect the Slave select line and set the transfer size to 32 at reset */ +#define XQSPIPS_CR_RESET_MASK_SET XQSPIPS_CR_IFMODE_MASK | \ + XQSPIPS_CR_SSCTRL_MASK | \ + XQSPIPS_CR_DATA_SZ_MASK | \ + XQSPIPS_CR_MSTREN_MASK | \ + XQSPIPS_CR_SSFORCE_MASK | \ + XQSPIPS_CR_HOLD_B_MASK +#define XQSPIPS_CR_RESET_MASK_CLR XQSPIPS_CR_CPOL_MASK | \ + XQSPIPS_CR_CPHA_MASK | \ + XQSPIPS_CR_PRESC_MASK | \ + XQSPIPS_CR_MANSTRTEN_MASK | \ + XQSPIPS_CR_MANSTRT_MASK | \ + XQSPIPS_CR_ENDIAN_MASK | \ + XQSPIPS_CR_REF_CLK_MASK +/* @} */ + + +/** @name QSPI Interrupt Registers + * + * QSPI Status Register + * + * This register holds the interrupt status flags for an QSPI device. Some + * of the flags are level triggered, which means that they are set as long + * as the interrupt condition exists. Other flags are edge triggered, + * which means they are set once the interrupt condition occurs and remain + * set until they are cleared by software. The interrupts are cleared by + * writing a '1' to the interrupt bit position in the Status Register. + * Read/Write. + * + * QSPI Interrupt Enable Register + * + * This register is used to enable chosen interrupts for an QSPI device. + * Writing a '1' to a bit in this register sets the corresponding bit in the + * QSPI Interrupt Mask register. Write only. + * + * QSPI Interrupt Disable Register + * + * This register is used to disable chosen interrupts for an QSPI device. + * Writing a '1' to a bit in this register clears the corresponding bit in the + * QSPI Interrupt Mask register. Write only. + * + * QSPI Interrupt Mask Register + * + * This register shows the enabled/disabled interrupts of an QSPI device. + * Read only. + * + * All four registers have the same bit definitions. They are only defined once + * for each of the Interrupt Enable Register, Interrupt Disable Register, + * Interrupt Mask Register, and Channel Interrupt Status Register + * @{ + */ + +#define XQSPIPS_IXR_TXUF_MASK 0x00000040 /**< QSPI Tx FIFO Underflow */ +#define XQSPIPS_IXR_RXFULL_MASK 0x00000020 /**< QSPI Rx FIFO Full */ +#define XQSPIPS_IXR_RXNEMPTY_MASK 0x00000010 /**< QSPI Rx FIFO Not Empty */ +#define XQSPIPS_IXR_TXFULL_MASK 0x00000008 /**< QSPI Tx FIFO Full */ +#define XQSPIPS_IXR_TXOW_MASK 0x00000004 /**< QSPI Tx FIFO Overwater */ +#define XQSPIPS_IXR_RXOVR_MASK 0x00000001 /**< QSPI Rx FIFO Overrun */ +#define XQSPIPS_IXR_DFLT_MASK 0x00000025 /**< QSPI default interrupts + mask */ +#define XQSPIPS_IXR_WR_TO_CLR_MASK 0x00000041 /**< Interrupts which + need write to clear */ +#define XQSPIPS_ISR_RESET_STATE 0x00000004 /**< Default to tx/rx empty */ +#define XQSPIPS_IXR_DISABLE_ALL 0x0000007D /**< Disable all interrupts */ +/* @} */ + + +/** @name Enable Register + * + * This register is used to enable or disable an QSPI device. + * Read/Write + * @{ + */ +#define XQSPIPS_ER_ENABLE_MASK 0x00000001 /**< QSPI Enable Bit Mask */ +/* @} */ + + +/** @name Delay Register + * + * This register is used to program timing delays in + * slave mode. Read/Write + * @{ + */ +#define XQSPIPS_DR_NSS_MASK 0xFF000000 /**< Delay to de-assert slave select + between two words mask */ +#define XQSPIPS_DR_NSS_SHIFT 24 /**< Delay to de-assert slave select + between two words shift */ +#define XQSPIPS_DR_BTWN_MASK 0x00FF0000 /**< Delay Between Transfers + mask */ +#define XQSPIPS_DR_BTWN_SHIFT 16 /**< Delay Between Transfers shift */ +#define XQSPIPS_DR_AFTER_MASK 0x0000FF00 /**< Delay After Transfers mask */ +#define XQSPIPS_DR_AFTER_SHIFT 8 /**< Delay After Transfers shift */ +#define XQSPIPS_DR_INIT_MASK 0x000000FF /**< Delay Initially mask */ +/* @} */ + +/** @name Slave Idle Count Registers + * + * This register defines the number of pclk cycles the slave waits for a the + * QSPI clock to become stable in quiescent state before it can detect the start + * of the next transfer in CPHA = 1 mode. + * Read/Write + * + * @{ + */ +#define XQSPIPS_SICR_MASK 0x000000FF /**< Slave Idle Count Mask */ +/* @} */ + + +/** @name Transmit FIFO Watermark Register + * + * This register defines the watermark setting for the Transmit FIFO. + * + * @{ + */ +#define XQSPIPS_TXWR_MASK 0x0000003F /**< Transmit Watermark Mask */ +#define XQSPIPS_TXWR_RESET_VALUE 0x00000001 /**< Transmit Watermark + * register reset value */ + +/* @} */ + +/** @name Receive FIFO Watermark Register + * + * This register defines the watermark setting for the Receive FIFO. + * + * @{ + */ +#define XQSPIPS_RXWR_MASK 0x0000003F /**< Receive Watermark Mask */ +#define XQSPIPS_RXWR_RESET_VALUE 0x00000001 /**< Receive Watermark + * register reset value */ + +/* @} */ + +/** @name FIFO Depth + * + * This macro provides the depth of transmit FIFO and receive FIFO. + * + * @{ + */ +#define XQSPIPS_FIFO_DEPTH 63 /**< FIFO depth (words) */ +/* @} */ + + +/** @name Linear QSPI Configuration Register + * + * This register contains various control bits that + * affect the operation of the Linear QSPI controller. Read/Write. + * + * @{ + */ +#define XQSPIPS_LQSPI_CR_LINEAR_MASK 0x80000000 /**< LQSPI mode enable */ +#define XQSPIPS_LQSPI_CR_TWO_MEM_MASK 0x40000000 /**< Both memories or one */ +#define XQSPIPS_LQSPI_CR_SEP_BUS_MASK 0x20000000 /**< Seperate memory bus */ +#define XQSPIPS_LQSPI_CR_U_PAGE_MASK 0x10000000 /**< Upper memory page */ +#define XQSPIPS_LQSPI_CR_MODE_EN_MASK 0x02000000 /**< Enable mode bits */ +#define XQSPIPS_LQSPI_CR_MODE_ON_MASK 0x01000000 /**< Mode on */ +#define XQSPIPS_LQSPI_CR_MODE_BITS_MASK 0x00FF0000 /**< Mode value for dual I/O + or quad I/O */ +#define XQSPIPS_LQSPI_CR_DUMMY_MASK 0x00000700 /**< Number of dummy bytes + between addr and return + read data */ +#define XQSPIPS_LQSPI_CR_INST_MASK 0x000000FF /**< Read instr code */ +#define XQSPIPS_LQSPI_CR_RST_STATE 0x8000016B /**< Default CR value */ +/* @} */ + +/** @name Linear QSPI Status Register + * + * This register contains various status bits of the Linear QSPI controller. + * Read/Write. + * + * @{ + */ +#define XQSPIPS_LQSPI_SR_D_FSM_ERR_MASK 0x00000004 /**< AXI Data FSM Error + received */ +#define XQSPIPS_LQSPI_SR_WR_RECVD_MASK 0x00000002 /**< AXI write command + received */ +/* @} */ + + +/** @name Loopback Delay Adjust Register + * + * This register contains various bit masks of Loopback Delay Adjust Register. + * + * @{ + */ + +#define XQSPIPS_LPBK_DLY_ADJ_USE_LPBK_MASK 0x00000020 /**< Loopback Bit */ + +/* @} */ + + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define XQspiPs_In32 Xil_In32 +#define XQspiPs_Out32 Xil_Out32 + +/****************************************************************************/ +/** +* Read a register. +* +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to the target register. +* +* @return The value read from the register. +* +* @note C-Style signature: +* u32 XQspiPs_ReadReg(u32 BaseAddress. int RegOffset) +* +******************************************************************************/ +#define XQspiPs_ReadReg(BaseAddress, RegOffset) \ + XQspiPs_In32((BaseAddress) + (RegOffset)) + +/***************************************************************************/ +/** +* Write to a register. +* +* @param BaseAddress contains the base address of the device. +* @param RegOffset contains the offset from the 1st register of the +* device to target register. +* @param RegisterValue is the value to be written to the register. +* +* @return None. +* +* @note C-Style signature: +* void XQspiPs_WriteReg(u32 BaseAddress, int RegOffset, +* u32 RegisterValue) +* +******************************************************************************/ +#define XQspiPs_WriteReg(BaseAddress, RegOffset, RegisterValue) \ + XQspiPs_Out32((BaseAddress) + (RegOffset), (RegisterValue)) + +/************************** Function Prototypes ******************************/ + +/* + * Functions implemented in xqspips_hw.c + */ +void XQspiPs_ResetHw(u32 BaseAddress); +void XQspiPs_LinearInit(u32 BaseAddress); + +/************************** Variable Definitions *****************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* end of protection macro */ diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips_options.c b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_options.c new file mode 100644 index 00000000..6476931c --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_options.c @@ -0,0 +1,425 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_options.c +* +* Contains functions for the configuration of the XQspiPs driver component. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.00a kka 07/25/12 Removed the selection for the following options: +* Master mode (XQSPIPS_MASTER_OPTION) and +* Flash interface mode (XQSPIPS_FLASH_MODE_OPTION) option +* as the QSPI driver supports the Master mode +* and Flash Interface mode. The driver doesnot support +* Slave mode or the legacy mode. +* Added the option for setting the Holdb_dr bit in the +* configuration options, XQSPIPS_HOLD_B_DRIVE_OPTION +* is the option to be used for setting this bit in the +* configuration register. +* 2.01a sg 02/03/13 SetDelays and GetDelays API's include DelayNss parameter. +* +* 2.02a hk 26/03/13 Removed XQspi_Reset() in Set_Options() function when +* LQSPI_MODE_OPTION is set. Moved Enable() to XQpsiPs_LqspiRead(). +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xqspips.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; + u32 Mask; +} OptionsMap; + +static OptionsMap OptionsTable[] = { + {XQSPIPS_CLK_ACTIVE_LOW_OPTION, XQSPIPS_CR_CPOL_MASK}, + {XQSPIPS_CLK_PHASE_1_OPTION, XQSPIPS_CR_CPHA_MASK}, + {XQSPIPS_FORCE_SSELECT_OPTION, XQSPIPS_CR_SSFORCE_MASK}, + {XQSPIPS_MANUAL_START_OPTION, XQSPIPS_CR_MANSTRTEN_MASK}, + {XQSPIPS_HOLD_B_DRIVE_OPTION, XQSPIPS_CR_HOLD_B_MASK}, +}; + +#define XQSPIPS_NUM_OPTIONS (sizeof(OptionsTable) / sizeof(OptionsMap)) + +/*****************************************************************************/ +/** +* +* This function sets the options for the QSPI device driver. The options control +* how the device behaves relative to the QSPI bus. The device must be idle +* rather than busy transferring data before setting these device options. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param Options contains the specified options to be set. This is a bit +* mask where a 1 means to turn the option on, and a 0 means to +* turn the option off. One or more bit values may be contained in +* the mask. See the bit definitions named XQSPIPS_*_OPTIONS in +* the file xqspips.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. +* +******************************************************************************/ +int XQspiPs_SetOptions(XQspiPs *InstancePtr, u32 Options) +{ + u32 ConfigReg; + unsigned int Index; + u32 QspiOptions; + + 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) { + return XST_DEVICE_BUSY; + } + + QspiOptions = Options & XQSPIPS_LQSPI_MODE_OPTION; + Options &= ~XQSPIPS_LQSPI_MODE_OPTION; + + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + + /* + * Loop through the options table, turning the option on or off + * depending on whether the bit is set in the incoming options flag. + */ + for (Index = 0; Index < XQSPIPS_NUM_OPTIONS; Index++) { + if (Options & 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. + */ + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, XQSPIPS_CR_OFFSET, + ConfigReg); + + /* + * Check for the LQSPI configuration options. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_LQSPI_CR_OFFSET); + + + if (QspiOptions & XQSPIPS_LQSPI_MODE_OPTION) { + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_LQSPI_CR_OFFSET, + XQSPIPS_LQSPI_CR_RST_STATE); + XQspiPs_SetSlaveSelect(InstancePtr); + } else { + ConfigReg &= ~XQSPIPS_LQSPI_CR_LINEAR_MASK; + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_LQSPI_CR_OFFSET, ConfigReg); + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function gets the options for the QSPI device. The options control how +* the device behaves relative to the QSPI bus. +* +* @param InstancePtr is a pointer to the XQspiPs 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 XQSPIPS_*_OPTIONS in file xqspips.h. +* +* @note None. +* +******************************************************************************/ +u32 XQspiPs_GetOptions(XQspiPs *InstancePtr) +{ + u32 OptionsFlag = 0; + u32 ConfigReg; + unsigned int Index; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Get the current options from QSPI configuration register. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + + /* + * Loop through the options table to grab options + */ + for (Index = 0; Index < XQSPIPS_NUM_OPTIONS; Index++) { + if (ConfigReg & OptionsTable[Index].Mask) { + OptionsFlag |= OptionsTable[Index].Option; + } + } + + /* + * Check for the LQSPI configuration options. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_LQSPI_CR_OFFSET); + + if ((ConfigReg & XQSPIPS_LQSPI_CR_LINEAR_MASK) != 0) { + OptionsFlag |= XQSPIPS_LQSPI_MODE_OPTION; + } + + return OptionsFlag; +} + +/*****************************************************************************/ +/** +* +* This function sets the clock prescaler for an QSPI device. The device +* must be idle rather than busy transferring data before setting these device +* options. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param Prescaler is the value that determine how much the clock should +* be divided by. Use the XQSPIPS_CLK_PRESCALE_* constants defined +* in xqspips.h for this setting. +* +* @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. +* +******************************************************************************/ +int XQspiPs_SetClkPrescaler(XQspiPs *InstancePtr, u8 Prescaler) +{ + u32 ConfigReg; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + Xil_AssertNonvoid(Prescaler <= XQSPIPS_CR_PRESC_MAXIMUM); + + /* + * Do not allow the slave select to change while a transfer is in + * progress. Not thread-safe. + */ + if (InstancePtr->IsBusy) { + return XST_DEVICE_BUSY; + } + + /* + * Read the configuration register, mask out the interesting bits, and set + * them with the shifted value passed into the function. Write the + * results back to the configuration register. + */ + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + + ConfigReg &= ~XQSPIPS_CR_PRESC_MASK; + ConfigReg |= (u32) (Prescaler & XQSPIPS_CR_PRESC_MAXIMUM) << + XQSPIPS_CR_PRESC_SHIFT; + + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET, + ConfigReg); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function gets the clock prescaler of an QSPI device. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return The prescaler value. +* +* @note None. +* +* +******************************************************************************/ +u8 XQspiPs_GetClkPrescaler(XQspiPs *InstancePtr) +{ + u32 ConfigReg; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + ConfigReg = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_CR_OFFSET); + + ConfigReg &= XQSPIPS_CR_PRESC_MASK; + + return (u8)(ConfigReg >> XQSPIPS_CR_PRESC_SHIFT); +} + +/*****************************************************************************/ +/** +* +* This function sets the delay register for the QSPI device driver. +* The delay register controls the Delay Between Transfers, Delay After +* Transfers, and the Delay Initially. The default value is 0x0. The range of +* each delay value is 0-255. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param DelayNss is the delay to de-assert slave select between +* two word transfers. +* @param DelayBtwn is the delay between one Slave Select being +* de-activated and the activation of another slave. The delay is +* the number of master clock periods given by DelayBtwn + 2. +* @param DelayAfter define the delay between the last bit of the current +* byte transfer and the first bit of the next byte transfer. +* The delay in number of master clock periods is given as: +* CHPA=0:DelayInit+DelayAfter+3 +* CHPA=1:DelayAfter+1 +* @param DelayInit is the delay between asserting the slave select signal +* and the first bit transfer. The delay int number of master clock +* periods is DelayInit+1. +* +* @return +* - XST_SUCCESS if delays are successfully set. +* - XST_DEVICE_BUSY if the device is currently transferring data. +* The transfer must complete or be aborted before setting options. +* +* @note None. +* +******************************************************************************/ +int XQspiPs_SetDelays(XQspiPs *InstancePtr, u8 DelayNss, u8 DelayBtwn, + u8 DelayAfter, u8 DelayInit) +{ + u32 DelayRegister; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Do not allow the delays to change while a transfer is in + * progress. Not thread-safe. + */ + if (InstancePtr->IsBusy) { + return XST_DEVICE_BUSY; + } + + /* Shift, Mask and OR the values to build the register settings */ + DelayRegister = (u32) DelayNss << XQSPIPS_DR_NSS_SHIFT; + DelayRegister |= (u32) DelayBtwn << XQSPIPS_DR_BTWN_SHIFT; + DelayRegister |= (u32) DelayAfter << XQSPIPS_DR_AFTER_SHIFT; + DelayRegister |= (u32) DelayInit; + + XQspiPs_WriteReg(InstancePtr->Config.BaseAddress, + XQSPIPS_DR_OFFSET, DelayRegister); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function gets the delay settings for an QSPI device. +* The delay register controls the Delay Between Transfers, Delay After +* Transfers, and the Delay Initially. The default value is 0x0. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* @param DelayNss is a pointer to the Delay to de-assert slave select +* between two word transfers. +* @param DelayBtwn is a pointer to the Delay Between transfers value. +* This is a return parameter. +* @param DelayAfter is a pointer to the Delay After transfer value. +* This is a return parameter. +* @param DelayInit is a pointer to the Delay Initially value. This is +* a return parameter. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +void XQspiPs_GetDelays(XQspiPs *InstancePtr, u8 *DelayNss, u8 *DelayBtwn, + u8 *DelayAfter, u8 *DelayInit) +{ + u32 DelayRegister; + + Xil_AssertVoid(InstancePtr != NULL); + Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + DelayRegister = XQspiPs_ReadReg(InstancePtr->Config.BaseAddress, + XQSPIPS_DR_OFFSET); + + *DelayInit = (u8)(DelayRegister & XQSPIPS_DR_INIT_MASK); + + *DelayAfter = (u8)((DelayRegister & XQSPIPS_DR_AFTER_MASK) >> + XQSPIPS_DR_AFTER_SHIFT); + + *DelayBtwn = (u8)((DelayRegister & XQSPIPS_DR_BTWN_MASK) >> + XQSPIPS_DR_BTWN_SHIFT); + + *DelayNss = (u8)((DelayRegister & XQSPIPS_DR_NSS_MASK) >> + XQSPIPS_DR_NSS_SHIFT); +} diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips_selftest.c b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_selftest.c new file mode 100644 index 00000000..49515de1 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_selftest.c @@ -0,0 +1,136 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_selftest.c +* +* This file contains the implementation of selftest function for the QSPI +* device. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +* 2.01a sg 02/03/13 Delay Register test is added with DelayNss parameter. +* 3.1 hk 06/19/14 Remove checks for CR and ISR register values as they are +* reset in the previous step. +* +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xqspips.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Runs a self-test on the driver/device. The self-test is destructive in that +* a reset of the device is performed in order to check the reset values of +* the registers and to get the device into a known state. +* +* Upon successful return from the self-test, the device is reset. +* +* @param InstancePtr is a pointer to the XQspiPs instance. +* +* @return +* - XST_SUCCESS if successful +* - XST_REGISTER_ERROR indicates a register did not read or write +* correctly. +* +* @note None. +* +******************************************************************************/ +int XQspiPs_SelfTest(XQspiPs *InstancePtr) +{ + int Status; + u8 DelayTestNss; + u8 DelayTestBtwn; + u8 DelayTestAfter; + u8 DelayTestInit; + + Xil_AssertNonvoid(InstancePtr != NULL); + Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); + + /* + * Reset the QSPI device to leave it in a known good state + */ + XQspiPs_Reset(InstancePtr); + + DelayTestNss = 0x5A; + DelayTestBtwn = 0xA5; + DelayTestAfter = 0xAA; + DelayTestInit = 0x55; + + /* + * Write and read the delay register, just to be sure there is some + * hardware out there. + */ + Status = XQspiPs_SetDelays(InstancePtr, DelayTestNss, DelayTestBtwn, + DelayTestAfter, DelayTestInit); + if (Status != XST_SUCCESS) { + return Status; + } + + XQspiPs_GetDelays(InstancePtr, &DelayTestNss, &DelayTestBtwn, + &DelayTestAfter, &DelayTestInit); + if ((0x5A != DelayTestNss) || (0xA5 != DelayTestBtwn) || + (0xAA != DelayTestAfter) || (0x55 != DelayTestInit)) { + return XST_REGISTER_ERROR; + } + + Status = XQspiPs_SetDelays(InstancePtr, 0, 0, 0, 0); + if (Status != XST_SUCCESS) { + return Status; + } + + /* + * Reset the QSPI device to leave it in a known good state + */ + XQspiPs_Reset(InstancePtr); + + return XST_SUCCESS; +} diff --git a/XilinxProcessorIPLib/drivers/qspips/src/xqspips_sinit.c b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_sinit.c new file mode 100644 index 00000000..bf463585 --- /dev/null +++ b/XilinxProcessorIPLib/drivers/qspips/src/xqspips_sinit.c @@ -0,0 +1,97 @@ +/****************************************************************************** +* +* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xqspips_sinit.c +* +* The implementation of the XQspiPs component's static initialization +* functionality. +* +*
+* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- --- -------- ----------------------------------------------- +* 1.00 sdm 11/25/10 First release +*+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xstatus.h" +#include "xqspips.h" +#include "xparameters.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +extern XQspiPs_Config XQspiPs_ConfigTable[]; + +/*****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. A table +* contains the configuration info for each device in the system. +* +* @param DeviceId contains the ID of the device to look up the +* configuration for. +* +* @return +* +* A pointer to the configuration found or NULL if the specified device ID was +* not found. See xqspips.h for the definition of XQspiPs_Config. +* +* @note None. +* +******************************************************************************/ +XQspiPs_Config *XQspiPs_LookupConfig(u16 DeviceId) +{ + XQspiPs_Config *CfgPtr = NULL; + int Index; + + for (Index = 0; Index < XPAR_XQSPIPS_NUM_INSTANCES; Index++) { + if (XQspiPs_ConfigTable[Index].DeviceId == DeviceId) { + CfgPtr = &XQspiPs_ConfigTable[Index]; + break; + } + } + return CfgPtr; +}