
This patch check for DAT inhibit mask rather than CMD inhibit mask in cmd transfer API. Signed-off-by: P L Sai Krishna <lakshmis@xilinx.com>
1116 lines
29 KiB
C
Executable file
1116 lines
29 KiB
C
Executable file
/******************************************************************************
|
|
*
|
|
* 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 xsdps.c
|
|
*
|
|
* Contains the interface functions of the XSdPs driver.
|
|
* See xsdps.h for a detailed description of the device and driver.
|
|
*
|
|
* <pre>
|
|
* MODIFICATION HISTORY:
|
|
*
|
|
* Ver Who Date Changes
|
|
* ----- --- -------- -----------------------------------------------
|
|
* 1.00a hk/sg 10/17/13 Initial release
|
|
* 2.0 hk 12/13/13 Added check for arm to use sleep.h and its API's
|
|
* 2.1 hk 04/18/14 Add sleep for microblaze designs. CR# 781117.
|
|
* 2.2 hk 07/28/14 Make changes to enable use of data cache.
|
|
* 2.3 sk 09/23/14 Send command for relative card address
|
|
* when re-initialization is done.CR# 819614.
|
|
* Use XSdPs_Change_ClkFreq API whenever changing
|
|
* clock.CR# 816586.
|
|
* 2.4 sk 12/04/14 Added support for micro SD without
|
|
* WP/CD. CR# 810655.
|
|
* Checked for DAT Inhibit mask instead of CMD
|
|
* Inhibit mask in Cmd Transfer API.
|
|
*
|
|
* </pre>
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
#include "xsdps.h"
|
|
/*
|
|
* The header sleep.h and API usleep() can only be used with an arm design.
|
|
* MB_Sleep() is used for microblaze design.
|
|
*/
|
|
#ifdef __arm__
|
|
|
|
#include "sleep.h"
|
|
|
|
#endif
|
|
|
|
#ifdef __MICROBLAZE__
|
|
|
|
#include "microblaze_sleep.h"
|
|
|
|
#endif
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
#define XSDPS_CMD8_VOL_PATTERN 0x1AA
|
|
#define XSDPS_RESPOCR_READY 0x80000000
|
|
#define XSDPS_ACMD41_HCS 0x40000000
|
|
#define XSDPS_ACMD41_3V3 0x00300000
|
|
#define XSDPS_CMD1_HIGH_VOL 0x00FF8000
|
|
#define XSDPS_CMD1_DUAL_VOL 0x00FF8010
|
|
|
|
/**************************** Type Definitions *******************************/
|
|
|
|
/***************** Macros (Inline Functions) Definitions *********************/
|
|
|
|
#define XSDPS_INIT_DELAY 2000
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
u32 XSdPs_FrameCmd(u32 Cmd);
|
|
int XSdPs_CmdTransfer(XSdPs *InstancePtr, u32 Cmd, u32 Arg, u32 BlkCnt);
|
|
void XSdPs_SetupADMA2DescTbl(XSdPs *InstancePtr, u32 BlkCnt, const u8 *Buff);
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Initializes a specific XSdPs instance such that the driver is ready to use.
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the XSdPs instance.
|
|
* @param ConfigPtr is a reference to a structure containing information
|
|
* about a specific SD 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 This function initializes the host controller.
|
|
* Initial clock of 400KHz is set.
|
|
* Voltage of 3.3V is selected as that is supported by host.
|
|
* Interrupts status is enabled and signal disabled by default.
|
|
* Default data direction is card to host and
|
|
* 32 bit ADMA2 is selected. Defualt Block size is 512 bytes.
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_CfgInitialize(XSdPs *InstancePtr, XSdPs_Config *ConfigPtr,
|
|
u32 EffectiveAddr)
|
|
{
|
|
u32 ClockReg;
|
|
u32 Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(ConfigPtr != NULL);
|
|
|
|
/*
|
|
* Set some default values.
|
|
*/
|
|
InstancePtr->Config.BaseAddress = EffectiveAddr;
|
|
InstancePtr->Config.InputClockHz = ConfigPtr->InputClockHz;
|
|
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
|
|
InstancePtr->Config.CardDetect = ConfigPtr->CardDetect;
|
|
InstancePtr->Config.WriteProtect = ConfigPtr->WriteProtect;
|
|
|
|
/*
|
|
* "Software reset for all" is initiated
|
|
*/
|
|
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress, XSDPS_SW_RST_OFFSET,
|
|
XSDPS_SWRST_ALL_MASK);
|
|
|
|
/*
|
|
* Proceed with initialization only after reset is complete
|
|
*/
|
|
while (XSdPs_ReadReg8(InstancePtr->Config.BaseAddress,
|
|
XSDPS_SW_RST_OFFSET) & XSDPS_SWRST_ALL_MASK);
|
|
|
|
/*
|
|
* Read capabilities register and update it in Instance pointer.
|
|
* It is sufficient to read this once on power on.
|
|
*/
|
|
InstancePtr->Host_Caps = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_CAPS_OFFSET);
|
|
|
|
/*
|
|
* Change the clock frequency to 400 KHz
|
|
*/
|
|
Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH ;
|
|
}
|
|
|
|
/*
|
|
* Select voltage and enable bus power.
|
|
*/
|
|
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
|
|
XSDPS_POWER_CTRL_OFFSET,
|
|
XSDPS_PC_BUS_VSEL_3V3_MASK | XSDPS_PC_BUS_PWR_MASK);
|
|
|
|
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
|
|
XSDPS_HOST_CTRL1_OFFSET,
|
|
XSDPS_HC_DMA_ADMA2_32_MASK);
|
|
|
|
/*
|
|
* Enable all interrupt status except card interrupt initially
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_EN_OFFSET,
|
|
XSDPS_NORM_INTR_ALL_MASK & (~XSDPS_INTR_CARD_MASK));
|
|
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_STS_EN_OFFSET,
|
|
XSDPS_ERROR_INTR_ALL_MASK);
|
|
|
|
/*
|
|
* Disable all interrupt signals by default.
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_SIG_EN_OFFSET, 0x0);
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_SIG_EN_OFFSET, 0x0);
|
|
|
|
/*
|
|
* Transfer mode register - default value
|
|
* DMA enabled, block count enabled, data direction card to host(read)
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_XFER_MODE_OFFSET,
|
|
XSDPS_TM_DMA_EN_MASK | XSDPS_TM_BLK_CNT_EN_MASK |
|
|
XSDPS_TM_DAT_DIR_SEL_MASK);
|
|
|
|
/*
|
|
* Set block size to 512 by default
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_BLK_SIZE_OFFSET, XSDPS_BLK_SIZE_512_MASK);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* SD initialization is done in this function
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the instance to be worked on.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if initialization was successful
|
|
* - XST_FAILURE if failure - could be because
|
|
* a) SD is already initialized
|
|
* b) There is no card inserted
|
|
* c) One of the steps (commands) in the
|
|
initialization cycle failed
|
|
*
|
|
* @note This function initializes the SD card by following its
|
|
* initialization and identification state diagram.
|
|
* CMD0 is sent to reset card.
|
|
* CMD8 and ACDM41 are sent to identify voltage and
|
|
* high capacity support
|
|
* CMD2 and CMD3 are sent to obtain Card ID and
|
|
* Relative card address respectively.
|
|
* CMD9 is sent to read the card specific data.
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_SdCardInitialize(XSdPs *InstancePtr)
|
|
{
|
|
u32 PresentStateReg;
|
|
u32 Status;
|
|
u32 RespOCR = 0x0;
|
|
u32 CSD[4];
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
if(InstancePtr->Config.CardDetect) {
|
|
/*
|
|
* Check the present state register to make sure
|
|
* card is inserted and detected by host controller
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 74 CLK delay after card is powered up, before the first command.
|
|
*/
|
|
|
|
#ifdef __arm__
|
|
|
|
usleep(XSDPS_INIT_DELAY);
|
|
|
|
#endif
|
|
|
|
#ifdef __MICROBLAZE__
|
|
|
|
/* 2 msec delay */
|
|
MB_Sleep(2);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* CMD0 no response expected
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD0, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* CMD8; response expected
|
|
* 0x1AA - Supply Voltage 2.7 - 3.6V and AA is pattern
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD8,
|
|
XSDPS_CMD8_VOL_PATTERN, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
RespOCR = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
if (RespOCR != XSDPS_CMD8_VOL_PATTERN) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
RespOCR = 0;
|
|
/*
|
|
* Send ACMD41 while card is still busy with power up
|
|
*/
|
|
while ((RespOCR & XSDPS_RESPOCR_READY) == 0) {
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD55, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* 0x40300000 - Host High Capacity support & 3.3V window
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, ACMD41,
|
|
(XSDPS_ACMD41_HCS | XSDPS_ACMD41_3V3), 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Response with card capacity
|
|
*/
|
|
RespOCR = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
|
|
}
|
|
|
|
/*
|
|
* Update HCS support flag based on card capacity response
|
|
*/
|
|
if (RespOCR & XSDPS_ACMD41_HCS)
|
|
InstancePtr->HCS = 1;
|
|
|
|
/*
|
|
* CMD2 for Card ID
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD2, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
InstancePtr->CardID[0] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
InstancePtr->CardID[1] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP1_OFFSET);
|
|
InstancePtr->CardID[2] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP2_OFFSET);
|
|
InstancePtr->CardID[3] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP3_OFFSET);
|
|
do {
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD3, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Relative card address is stored as the upper 16 bits
|
|
* This is to avoid shifting when sending commands
|
|
*/
|
|
InstancePtr->RelCardAddr =
|
|
XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET) & 0xFFFF0000;
|
|
} while (InstancePtr->RelCardAddr == 0);
|
|
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD9, (InstancePtr->RelCardAddr), 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Card specific data is read.
|
|
* Currently not used for any operation.
|
|
*/
|
|
CSD[0] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
CSD[1] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP1_OFFSET);
|
|
CSD[2] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP2_OFFSET);
|
|
CSD[3] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP3_OFFSET);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function does SD command generation.
|
|
*
|
|
* @param InstancePtr is a pointer to the instance to be worked on.
|
|
* @param Cmd is the command to be sent.
|
|
* @param Arg is the argument to be sent along with the command.
|
|
* This could be address or any other information
|
|
* @param BlkCnt - Block count passed by the user.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if initialization was successful
|
|
* - XST_FAILURE if failure - could be because another transfer
|
|
* is in progress or command or data inhibit is set
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_CmdTransfer(XSdPs *InstancePtr, u32 Cmd, u32 Arg, u32 BlkCnt)
|
|
{
|
|
u32 PresentStateReg;
|
|
u32 CommandReg;
|
|
u32 StatusReg;
|
|
u32 Status;
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
/*
|
|
* Check the command inhibit to make sure no other
|
|
* command transfer is in progress
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if (PresentStateReg & XSDPS_PSR_INHIBIT_CMD_MASK) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Write block count register
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_BLK_CNT_OFFSET, BlkCnt);
|
|
|
|
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
|
|
XSDPS_TIMEOUT_CTRL_OFFSET, 0xE);
|
|
|
|
/*
|
|
* Write argument register
|
|
*/
|
|
XSdPs_WriteReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ARGMT_OFFSET, Arg);
|
|
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_NORM_INTR_ALL_MASK);
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_STS_OFFSET, XSDPS_ERROR_INTR_ALL_MASK);
|
|
/*
|
|
* Command register is set to trigger transfer of command
|
|
*/
|
|
CommandReg = XSdPs_FrameCmd(Cmd);
|
|
|
|
/*
|
|
* Mask to avoid writing to reserved bits 31-30
|
|
* This is necessary because 0x80000000 is used by this software to
|
|
* distinguish between ACMD and CMD of same number
|
|
*/
|
|
CommandReg = CommandReg & 0x3FFF;
|
|
|
|
/*
|
|
* Check for data inhibit in case of command using DAT lines
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if ((PresentStateReg & XSDPS_PSR_INHIBIT_DAT_MASK) &&
|
|
(CommandReg & XSDPS_DAT_PRESENT_SEL_MASK)) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress, XSDPS_CMD_OFFSET,
|
|
CommandReg);
|
|
|
|
/*
|
|
* Polling for response for now
|
|
*/
|
|
do {
|
|
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET);
|
|
|
|
if (StatusReg & XSDPS_INTR_ERR_MASK) {
|
|
|
|
/*
|
|
* Write to clear error bits
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_STS_OFFSET,
|
|
XSDPS_ERROR_INTR_ALL_MASK);
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
} while((StatusReg & XSDPS_INTR_CC_MASK) == 0);
|
|
/*
|
|
* Write to clear bit
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET,
|
|
XSDPS_INTR_CC_MASK);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function frames the Command register for a particular command.
|
|
* Note that this generates only the command register value i.e.
|
|
* the upper 16 bits of the transfer mode and command register.
|
|
* This value is already shifted to be upper 16 bits and can be directly
|
|
* OR'ed with transfer mode register value.
|
|
*
|
|
* @param Command to be sent.
|
|
*
|
|
* @return Command register value complete with response type and
|
|
* data, CRC and index related flags.
|
|
*
|
|
******************************************************************************/
|
|
u32 XSdPs_FrameCmd(u32 Cmd)
|
|
{
|
|
u32 RetVal;
|
|
|
|
RetVal = Cmd;
|
|
|
|
switch(Cmd) {
|
|
case CMD0:
|
|
RetVal |= RESP_NONE;
|
|
break;
|
|
case CMD1:
|
|
RetVal |= RESP_R3;
|
|
break;
|
|
case CMD2:
|
|
RetVal |= RESP_R2;
|
|
break;
|
|
case CMD3:
|
|
RetVal |= RESP_R6;
|
|
break;
|
|
case CMD4:
|
|
RetVal |= RESP_NONE;
|
|
break;
|
|
case CMD5:
|
|
RetVal |= RESP_R1B;
|
|
break;
|
|
|
|
#ifndef MMC_CARD
|
|
case CMD6:
|
|
RetVal |= RESP_R1 | XSDPS_DAT_PRESENT_SEL_MASK;
|
|
break;
|
|
#else
|
|
case CMD6:
|
|
RetVal |= RESP_R1B;
|
|
break;
|
|
#endif
|
|
|
|
case ACMD6:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
case CMD7:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
|
|
#ifndef MMC_CARD
|
|
case CMD8:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
#else
|
|
case CMD8:
|
|
RetVal |= RESP_R1 | XSDPS_DAT_PRESENT_SEL_MASK;
|
|
break;
|
|
#endif
|
|
|
|
case CMD9:
|
|
RetVal |= RESP_R2;
|
|
break;
|
|
case CMD10:
|
|
case CMD12:
|
|
case ACMD13:
|
|
case CMD16:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
case CMD17:
|
|
case CMD18:
|
|
RetVal |= RESP_R1 | XSDPS_DAT_PRESENT_SEL_MASK;
|
|
break;
|
|
case CMD23:
|
|
case ACMD23:
|
|
case CMD24:
|
|
case CMD25:
|
|
RetVal |= RESP_R1 | XSDPS_DAT_PRESENT_SEL_MASK;
|
|
case ACMD41:
|
|
RetVal |= RESP_R3;
|
|
break;
|
|
case ACMD42:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
case ACMD51:
|
|
RetVal |= RESP_R1 | XSDPS_DAT_PRESENT_SEL_MASK;
|
|
break;
|
|
case CMD52:
|
|
case CMD55:
|
|
RetVal |= RESP_R1;
|
|
break;
|
|
case CMD58:
|
|
break;
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function performs SD read in polled mode.
|
|
*
|
|
* @param InstancePtr is a pointer to the instance to be worked on.
|
|
* @param Arg is the address passed by the user that is to be sent as
|
|
* argument along with the command.
|
|
* @param BlkCnt - Block count passed by the user.
|
|
* @param Buff - Pointer to the data buffer for a DMA transfer.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if initialization was successful
|
|
* - XST_FAILURE if failure - could be because another transfer
|
|
* is in progress or command or data inhibit is set
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_ReadPolled(XSdPs *InstancePtr, u32 Arg, u32 BlkCnt, u8 *Buff)
|
|
{
|
|
u32 Status;
|
|
u32 PresentStateReg;
|
|
u32 StatusReg;
|
|
|
|
if(InstancePtr->Config.CardDetect) {
|
|
/*
|
|
* Check status to ensure card is initialized
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0x0) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set block size to 512 if not already set
|
|
*/
|
|
if( XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_BLK_SIZE_OFFSET) != XSDPS_BLK_SIZE_512_MASK ) {
|
|
Status = XSdPs_SetBlkSize(InstancePtr,
|
|
XSDPS_BLK_SIZE_512_MASK);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
}
|
|
|
|
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, Buff);
|
|
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_XFER_MODE_OFFSET,
|
|
XSDPS_TM_AUTO_CMD12_EN_MASK |
|
|
XSDPS_TM_BLK_CNT_EN_MASK | XSDPS_TM_DAT_DIR_SEL_MASK |
|
|
XSDPS_TM_DMA_EN_MASK | XSDPS_TM_MUL_SIN_BLK_SEL_MASK);
|
|
|
|
Xil_DCacheInvalidateRange(Buff, BlkCnt * XSDPS_BLK_SIZE_512_MASK);
|
|
|
|
/*
|
|
* Send block read command
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD18, Arg, BlkCnt);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Check for transfer complete
|
|
*/
|
|
do {
|
|
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET);
|
|
if (StatusReg & XSDPS_INTR_ERR_MASK) {
|
|
/*
|
|
* Write to clear error bits
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_STS_OFFSET,
|
|
XSDPS_ERROR_INTR_ALL_MASK);
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
} while((StatusReg & XSDPS_INTR_TC_MASK) == 0);
|
|
|
|
/*
|
|
* Write to clear bit
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
|
|
Status = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* This function performs SD write in polled mode.
|
|
*
|
|
* @param InstancePtr is a pointer to the instance to be worked on.
|
|
* @param Arg is the address passed by the user that is to be sent as
|
|
* argument along with the command.
|
|
* @param BlkCnt - Block count passed by the user.
|
|
* @param Buff - Pointer to the data buffer for a DMA transfer.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if initialization was successful
|
|
* - XST_FAILURE if failure - could be because another transfer
|
|
* is in progress or command or data inhibit is set
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_WritePolled(XSdPs *InstancePtr, u32 Arg, u32 BlkCnt, const u8 *Buff)
|
|
{
|
|
u32 Status;
|
|
u32 PresentStateReg;
|
|
u32 StatusReg;
|
|
|
|
if(InstancePtr->Config.CardDetect) {
|
|
/*
|
|
* Check status to ensure card is initialized
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0x0) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set block size to 512 if not already set
|
|
*/
|
|
if( XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_BLK_SIZE_OFFSET) != XSDPS_BLK_SIZE_512_MASK ) {
|
|
Status = XSdPs_SetBlkSize(InstancePtr,
|
|
XSDPS_BLK_SIZE_512_MASK);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
}
|
|
|
|
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, Buff);
|
|
Xil_DCacheFlushRange(Buff, BlkCnt * XSDPS_BLK_SIZE_512_MASK);
|
|
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_XFER_MODE_OFFSET,
|
|
XSDPS_TM_AUTO_CMD12_EN_MASK |
|
|
XSDPS_TM_BLK_CNT_EN_MASK |
|
|
XSDPS_TM_MUL_SIN_BLK_SEL_MASK | XSDPS_TM_DMA_EN_MASK);
|
|
|
|
/*
|
|
* Send block write command
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD25, Arg, BlkCnt);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Check for transfer complete
|
|
* Polling for response for now
|
|
*/
|
|
do {
|
|
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET);
|
|
if (StatusReg & XSDPS_INTR_ERR_MASK) {
|
|
/*
|
|
* Write to clear error bits
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_ERR_INTR_STS_OFFSET,
|
|
XSDPS_ERROR_INTR_ALL_MASK);
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
} while((StatusReg & XSDPS_INTR_TC_MASK) == 0);
|
|
|
|
/*
|
|
* Write to clear bit
|
|
*/
|
|
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* Selects card and sets default block size
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the XSdPs instance.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if successful.
|
|
* - XST_FAILURE if fail.
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_Select_Card (XSdPs *InstancePtr)
|
|
{
|
|
u32 Status = 0;
|
|
|
|
/*
|
|
* Send CMD7 - Select card
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD7,
|
|
InstancePtr->RelCardAddr, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
Status = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
|
|
/*
|
|
* Set default block size
|
|
*/
|
|
Status = XSdPs_SetBlkSize(InstancePtr, XSDPS_BLK_SIZE_512_MASK);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
*
|
|
* API to setup ADMA2 descriptor table
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the XSdPs instance.
|
|
* @param BlkCnt - block count.
|
|
* @param Buff pointer to data buffer.
|
|
*
|
|
* @return None
|
|
*
|
|
* @note None.
|
|
*
|
|
******************************************************************************/
|
|
void XSdPs_SetupADMA2DescTbl(XSdPs *InstancePtr, u32 BlkCnt, const u8 *Buff)
|
|
{
|
|
u32 TotalDescLines = 0;
|
|
u32 DescNum = 0;
|
|
u32 BlkSize = 0;
|
|
|
|
/*
|
|
* Setup ADMA2 - Write descriptor table and point ADMA SAR to it
|
|
*/
|
|
BlkSize = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_BLK_SIZE_OFFSET);
|
|
BlkSize = BlkSize & XSDPS_BLK_SIZE_MASK;
|
|
|
|
if((BlkCnt*BlkSize) < XSDPS_DESC_MAX_LENGTH) {
|
|
|
|
TotalDescLines = 1;
|
|
|
|
}else {
|
|
|
|
TotalDescLines = ((BlkCnt*BlkSize) / XSDPS_DESC_MAX_LENGTH);
|
|
if ((BlkCnt * BlkSize) % XSDPS_DESC_MAX_LENGTH)
|
|
TotalDescLines += 1;
|
|
|
|
}
|
|
|
|
for (DescNum = 0; DescNum < (TotalDescLines-1); DescNum++) {
|
|
InstancePtr->Adma2_DescrTbl[DescNum].Address =
|
|
(u32)(Buff + (DescNum*XSDPS_DESC_MAX_LENGTH));
|
|
InstancePtr->Adma2_DescrTbl[DescNum].Attribute =
|
|
XSDPS_DESC_TRAN | XSDPS_DESC_VALID;
|
|
/*
|
|
* This will write '0' to length field which indicates 65536
|
|
*/
|
|
InstancePtr->Adma2_DescrTbl[DescNum].Length =
|
|
(u16)XSDPS_DESC_MAX_LENGTH;
|
|
}
|
|
|
|
InstancePtr->Adma2_DescrTbl[TotalDescLines-1].Address =
|
|
(u32)(Buff + (DescNum*XSDPS_DESC_MAX_LENGTH));
|
|
|
|
InstancePtr->Adma2_DescrTbl[TotalDescLines-1].Attribute =
|
|
XSDPS_DESC_TRAN | XSDPS_DESC_END | XSDPS_DESC_VALID;
|
|
|
|
InstancePtr->Adma2_DescrTbl[TotalDescLines-1].Length =
|
|
(BlkCnt*BlkSize) - (DescNum*XSDPS_DESC_MAX_LENGTH);
|
|
|
|
|
|
XSdPs_WriteReg(InstancePtr->Config.BaseAddress, XSDPS_ADMA_SAR_OFFSET,
|
|
(u32)&(InstancePtr->Adma2_DescrTbl[0]));
|
|
|
|
Xil_DCacheFlushRange(&(InstancePtr->Adma2_DescrTbl[0]),
|
|
sizeof(XSdPs_Adma2Descriptor) * 32);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Mmc initialization is done in this function
|
|
*
|
|
*
|
|
* @param InstancePtr is a pointer to the instance to be worked on.
|
|
*
|
|
* @return
|
|
* - XST_SUCCESS if initialization was successful
|
|
* - XST_FAILURE if failure - could be because
|
|
* a) MMC is already initialized
|
|
* b) There is no card inserted
|
|
* c) One of the steps (commands) in the initialization
|
|
* cycle failed
|
|
* @note This function initializes the SD card by following its
|
|
* initialization and identification state diagram.
|
|
* CMD0 is sent to reset card.
|
|
* CMD1 sent to identify voltage and high capacity support
|
|
* CMD2 and CMD3 are sent to obtain Card ID and
|
|
* Relative card address respectively.
|
|
* CMD9 is sent to read the card specific data.
|
|
*
|
|
******************************************************************************/
|
|
int XSdPs_MmcCardInitialize(XSdPs *InstancePtr)
|
|
{
|
|
u32 PresentStateReg;
|
|
u32 Status;
|
|
u32 RespOCR = 0x0;
|
|
u32 CSD[4];
|
|
|
|
Xil_AssertNonvoid(InstancePtr != NULL);
|
|
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
|
|
|
|
if(InstancePtr->Config.CardDetect) {
|
|
/*
|
|
* Check the present state register to make sure
|
|
* card is inserted and detected by host controller
|
|
*/
|
|
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_PRES_STATE_OFFSET);
|
|
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 74 CLK delay after card is powered up, before the first command.
|
|
*/
|
|
|
|
#ifdef __arm__
|
|
|
|
usleep(XSDPS_INIT_DELAY);
|
|
|
|
#endif
|
|
|
|
#ifdef __MICROBLAZE__
|
|
|
|
/* 2 msec delay */
|
|
MB_Sleep(2);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* CMD0 no response expected
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD0, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
RespOCR = 0;
|
|
/*
|
|
* Send CMD1 while card is still busy with power up
|
|
*/
|
|
while ((RespOCR & XSDPS_RESPOCR_READY) == 0) {
|
|
|
|
/*
|
|
* Host High Capacity support & High volage window
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD1,
|
|
XSDPS_ACMD41_HCS | XSDPS_CMD1_HIGH_VOL, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Response with card capacity
|
|
*/
|
|
RespOCR = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
|
|
}
|
|
|
|
/*
|
|
* Update HCS support flag based on card capacity response
|
|
*/
|
|
if (RespOCR & XSDPS_ACMD41_HCS)
|
|
InstancePtr->HCS = 1;
|
|
|
|
/*
|
|
* CMD2 for Card ID
|
|
*/
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD2, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
InstancePtr->CardID[0] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
InstancePtr->CardID[1] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP1_OFFSET);
|
|
InstancePtr->CardID[2] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP2_OFFSET);
|
|
InstancePtr->CardID[3] =
|
|
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP3_OFFSET);
|
|
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD3, 0, 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Relative card address is stored as the upper 16 bits
|
|
* This is to avoid shifting when sending commands
|
|
*/
|
|
InstancePtr->RelCardAddr =
|
|
XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET) & 0xFFFF0000;
|
|
|
|
Status = XSdPs_CmdTransfer(InstancePtr, CMD9, (InstancePtr->RelCardAddr), 0);
|
|
if (Status != XST_SUCCESS) {
|
|
Status = XST_FAILURE;
|
|
goto RETURN_PATH;
|
|
}
|
|
|
|
/*
|
|
* Card specific data is read.
|
|
* Currently not used for any operation.
|
|
*/
|
|
CSD[0] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP0_OFFSET);
|
|
CSD[1] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP1_OFFSET);
|
|
CSD[2] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP2_OFFSET);
|
|
CSD[3] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
|
|
XSDPS_RESP3_OFFSET);
|
|
|
|
Status = XST_SUCCESS;
|
|
|
|
RETURN_PATH:
|
|
return Status;
|
|
|
|
}
|
|
|
|
|