?? mmcdrv.c
字號:
// Copyright (c) David Vescovi. All rights reserved.
// Part of Project DrumStix
// Windows Embedded Developers Interest Group (WE-DIG) community project.
// http://www.we-dig.org
// Copyright (c) Microsoft Corporation. All rights reserved.
// Portions Copyright (c) SanDisk Corporation. All rights reserved.
//------------------------------------------------------------------------------
//
// File: mmcdrv.c
//
// MMC/SD driver
//
//
//------------------------------------------------------------------------------
#include "mmcsd.h"
volatile GPIO_REG_T *g_pGPIORegs = NULL;
volatile MMC_REG_T *g_pMMCRegs = NULL;
volatile OST_REG_T *g_pOSTRegs = NULL;
DEVICE_CONTROLLER DeviceController;
BOOL mmcBusStart( void )
{
// IDENTIFICATION STATE process in OPEN-DRAIN mode.
// This process is operated at low clock rate 250KHz
if ( !MMCSetClockRate(6) )
{
return FALSE;
}
// Enable MMC interface in open drain mode and turn on the clock.
MMCStart80Clocks();
return TRUE;
}
BOOL mmcInitSetup(void)
{
// Send 80 clocks to start the bus
if ( mmcBusStart() ) // Put device in idle mode
{
// Reset all devices on the bus and wait for ready.
if (MMC_NO_ERROR == mmcReset())
{
// Set max. clock rate
MMCSetClockRate(0);
return TRUE;
}
}
return (FALSE);
}
MMC_CC mmcReceiveData( UINT16 dLength, UINT16 noBlocks, UINT16 xferMode )
{
// Data is ready. Get it from the MMC BUFFER
return (MMCReceive((UCHAR *)DeviceController.user_address, dLength, noBlocks, xferMode));
}
MMC_CC mmcSendData( UINT16 dLength, UINT16 noBlocks, UINT16 xferMode )
{
// Write data to the device
return (MMCTransmit((UCHAR *)DeviceController.user_address, dLength, noBlocks, xferMode));
}
MMC_CC mmcReceiveResponse( RESP_TYPE Resp)
{
BYTE *respBuff;
UINT16 respLength;
if ( Resp == R0 )
return (MMC_NO_ERROR);
respLength = BYTE_LENGTH; // 6 bytes R1, R3 or R6 response
if ( Resp == R2 )
respLength = CID_BYTE_LENGTH -1;// (17-1) bytes R2 response
respBuff = (BYTE *)DeviceController.LastResponse;
// Read status and wait for response.
return (MMCResponseInfo(respBuff, respLength));
}
MMC_CC mmcAnalyzeResponse( RESP_TYPE respType )
{
UINT32 statusInfo;
BYTE *respBuff;
UINT16 ij, tt;
respBuff = (BYTE *)DeviceController.LastResponse;
if (respType == R6)
{
ij = (UINT16)respBuff[1];
ij <<= 8;
ij |= (UINT16)respBuff[2];
DeviceController.LastResponse[4] = ij;
// Clear bits 31-24 of Status register
DeviceController.LastResponse[0] &= 0x00FF;
// Extract bits 23, 22 and clear bits 23-16
ij = (DeviceController.LastResponse[1] & 0xC000);
// Extract bit 19
tt = (DeviceController.LastResponse[1] & 0x0800) << 2;
ij |= tt;
// Pack bits 23,22 and 19 to bits 15-13 of Status register
DeviceController.LastResponse[1] = ij;
ij = DeviceController.LastResponse[2] & 0x001F;
// Extract bits 12-8 then pack to bits 12-8
DeviceController.LastResponse[1] |= (ij << 8);
ij = DeviceController.LastResponse[2] >> 8;
// Extract bit 7-0 then pack to bit 7-0
DeviceController.LastResponse[2] = ij;
}
// Get the RAW MMC status
statusInfo = (UINT32)(respBuff[1] & 0xFF);
statusInfo <<= 8;
statusInfo |= (UINT32)(respBuff[2] & 0xFF);
statusInfo <<= 8;
statusInfo |= (UINT32)(respBuff[3] & 0xE0);
statusInfo <<= 8;
// Get the MMC state
DeviceController.currentState = (UINT16)(respBuff[3] & 0x1F);
DeviceController.mmcStatus = statusInfo;
DeviceController.mmcRdyState = DeviceController.currentState & 01;
DeviceController.currentState >>= 1;
// Check status for error. Convert RAW status to driver error code
if (statusInfo == 0)
return MMC_NO_ERROR;
if (statusInfo & OUT_OF_RANGE_ERROR)
return MMC_OUT_OF_RANGE;
if (statusInfo & ADDRESS_ERROR)
return MMC_ADDRESS_ERROR;
if (statusInfo & BLK_LENGTH_ERROR)
return MMC_DATA_LENGTH_ERROR;
if (statusInfo & ERASE_SEQ_ERROR)
return MMC_ERASE_SEQ_ERROR;
if (statusInfo & ERASE_PARAM)
return MMC_ERASE_PARAM;
if (statusInfo & WP_VIOLATION)
return MMC_WP_VIOLATION;
if (statusInfo & CMD_CRC_ERROR)
return MMC_CMD_CRC_ERROR;
if (statusInfo & COMUNC_ILLEG_COM)
return MMC_COMUNC_ILLEG_COM;
if (statusInfo & CARD_ECC_FAILED)
return MMC_CARD_ECC_FAILED;
if (statusInfo & CONTROLLER_ERROR)
return MMC_INTERFACE_ERROR;
if (statusInfo & EERROR)
return MMC_ERROR;
if (statusInfo & UNDERRUN)
return MMC_UNDERRUN;
if (statusInfo & OVERRUN)
return MMC_OVERRUN;
if (statusInfo & CIDCSD_OVERWRITE)
return MMC_CIDCSD_OVERWRITE;
if (statusInfo & WP_ERASE_SKIP)
return MMC_WP_ERASE_SKIP;
if (statusInfo & CARD_ECC_DISABLED)
return MMC_CARD_ECC_DISABLED;
if (statusInfo & ERASE_RESET)
return MMC_ERASE_RESET;
if (statusInfo & CARD_IS_LOCKED)
return MMC_CARD_IS_LOCKED;
if (statusInfo & LOCK_UNLOCK_FAILED)
return MMC_LOCK_UNLOCK_FAILED;
return MMC_CARD_IS_NOT_RESPONDING;
}
MMC_CC mmcCommandAndResponse( UINT32 Arg, UINT16 Cmd, UINT16 noBlocks, RESP_TYPE Resp )
{
MMC_CC resErr;
INT16 i;
// Setup the command
if ( Resp == R6 )
{
if ( !MMCPrepareAndSetup(Arg, Cmd, noBlocks, R1) )
return MMC_INTERFACE_ERROR;
}
else
{
if ( !MMCPrepareAndSetup(Arg, Cmd, noBlocks, (UINT16)Resp) )
return MMC_INTERFACE_ERROR;
}
if ( !Resp ) // Command without response (R0)
return MMC_NO_ERROR;
for (i = 0; i < (CID_BYTE_LENGTH>>1); i++)
DeviceController.LastResponse[i] = 0;
// Check the response
resErr = mmcReceiveResponse( Resp );
if ( resErr == MMC_NO_ERROR)
{
if ( (Resp == R1) || (Resp == R6) )
resErr = mmcAnalyzeResponse( Resp );
}
return resErr;
}
void mmcSetupDataRegister(UINT16 cmdIndex, UINT16 *cmdDatContReg, UINT16 *noBlocks, UINT16 *blkLength)
{
// For normal driver
switch ( cmdIndex )
{
case READ_BLOCK: // DATA TRANSFER
case READ_MULTIPLE_BLOCK: // DATA TRANSFER
case READ_DAT_UNTIL_STOP: // STREAM READ
*cmdDatContReg = DATA_ENABLE;
*blkLength = DEFAULT_BLK_LENGTH;
break;
case WRITE_BLOCK: // DATA TRANSFER
case WRITE_MULTIPLE_BLOCK: // DATA TRANSFER
case WRITE_DAT_UNTIL_STOP: // STREAM WRITE
*cmdDatContReg = (DATA_ENABLE + DATA_WRITE_SET);
// *cmdDatContReg |= BUSY_SET;
*blkLength = DEFAULT_BLK_LENGTH;
break;
case SEND_WRITE_PROT:
*cmdDatContReg = DATA_ENABLE;
*blkLength = FALSE;
break;
case SD_STATUS:
*cmdDatContReg = DATA_ENABLE;
*blkLength = (DEFAULT_BLK_LENGTH >> 3);
*noBlocks = 1;
break;
case SD_SEND_SCR:
*cmdDatContReg = DATA_ENABLE;
*blkLength = 8;
*noBlocks = 1;
break;
case PROGRAM_CSD:
case PROGRAM_CID:
*cmdDatContReg = (DATA_ENABLE + DATA_WRITE_SET);
*blkLength = 16;
*noBlocks = 1;
break;
case ERASE_SECTORS:
*cmdDatContReg = FALSE;
*blkLength = FALSE;
break;
case STOP_TRANSMISSION:
*cmdDatContReg = FALSE;
*blkLength = FALSE;
break;
/* NO DATA TRANSFER */
case GO_IDLE_STATE:
case SEND_OP_COND:
case ALL_SEND_CID:
case SEND_CID:
case SEND_CSD:
case SEND_STATUS:
*cmdDatContReg = FALSE;
*blkLength = FALSE;
*noBlocks = FALSE;
break;
}
}
void MMCHwInit(void)
{
g_pGPIORegs = (volatile GPIO_REG_T *) OALPAtoVA(PXA255_BASE_REG_PA_GPIO, FALSE);
g_pMMCRegs = (volatile MMC_REG_T *) OALPAtoVA(PXA255_BASE_REG_PA_MMC, FALSE);
g_pOSTRegs = (volatile OST_REG_T *) OALPAtoVA(PXA255_BASE_REG_PA_OST, FALSE);
if (!g_pGPIORegs || !g_pMMCRegs || !g_pOSTRegs)
{
return;
}
Stallms(20);
g_pMMCRegs->IMASK = 0x7F; // enable all interrupts
return;
}
BOOL MMCStopClock(void)
{
UINT32 timer;
UINT32 dStatus;
g_pMMCRegs->STRPC = STOP_CLOCK;
timer = 0x200;
dStatus = g_pMMCRegs->STAT;
while ( timer )
{
dStatus = g_pMMCRegs->STAT;
timer--;
if ( !(dStatus & CLOCK_ENABLE) )
return TRUE;
}
return FALSE;
}
void MMCStartClock(void)
{
g_pMMCRegs->STRPC = START_CLOCK;
}
BOOL MMCSetClockRate(UINT16 ClockRate)
{
if (!MMCStopClock())
{
return FALSE;
}
// Set the clock rate
g_pMMCRegs->CLKRT = ClockRate;
g_pMMCRegs->RESTO = 0x0000007f;
g_pMMCRegs->RDTO = 0xFFFF;
MMCStartClock();
return TRUE;
}
void MMCStart80Clocks(void)
{
UINT16 dStatus;
UINT32 timer;
// Send the 80 clocks to start all devices
if (!MMCStopClock())
return;
g_pMMCRegs->CMD = 0x0; // CMD0 go idle
g_pMMCRegs->ARGH = 0x00000000;
g_pMMCRegs->ARGL = 0x00000000;
g_pMMCRegs->CMDAT = RESPONSE_TYPE_R0 | SEND_80_CLOCKS; // set init bit for initial 80 clocks, expect response 3
g_pMMCRegs->STRPC = START_CLOCK; // start clock
MMCStartClock();
for (timer=0;timer<0x200;timer++ )
{
dStatus = (UINT16)g_pMMCRegs->STAT;
if ( dStatus & END_CMD_RES )
{
break;
}
}
// Ramp-up time
Stallms(200);
}
void MMCSendCommand( UINT32 Arg, UINT16 Cmd)
{
UINT16 dtmp;
// Set up the argument.
dtmp = (UINT16)(Arg >> 16);
g_pMMCRegs->ARGH = dtmp;
dtmp = (UINT16)(Arg & 0xFFFF);
g_pMMCRegs->ARGL = dtmp;
// Set up the command.
dtmp = (Cmd & 0x7F);
g_pMMCRegs->CMD = dtmp;
}
MMC_CC MMCResponseInfo(UCHAR *respBuff, UINT16 respLength)
{
UINT16 dtmp;
UINT32 timer;
UINT16 i;
// Timer count for an MMC response
timer = 0x1FFF;
dtmp = (UINT16)g_pMMCRegs->RXFIFO;
dtmp = (UINT16)g_pMMCRegs->TXFIFO;
while ( timer )
{
// Read status and wait for proper response
dtmp = (UINT16)g_pMMCRegs->STAT;
timer--;
// Check for TIME OUT on response
if ( dtmp & TIME_OUT_RESPONSE )
{
return MMC_CARD_IS_NOT_RESPONDING;
}
// Check for CRC on response
else if ( dtmp & RESP_CRC_ERR)
{
return (MMC_CMD_CRC_ERROR );
}
else if ( dtmp & CRC_WR_ERR )
{
return MMC_DATA_STATUS_CRC_ERROR;
}
// Check for CRC read error
else if ( dtmp & CRC_RD_ERR )
{
return MMC_DATA_STATUS_CRC_ERROR;
}
// Check for command response
else if ( dtmp & END_CMD_RES )
{
// Get the command response information
dtmp = (respLength + 1) & 0xFE;
respLength = dtmp;
i = 0;
while ( i < respLength )
{
dtmp = (UINT16)g_pMMCRegs->RES;
respBuff[i] = (UCHAR)(dtmp >> 8);
i++;
respBuff[i] = (UCHAR)(dtmp & 0xFF);
i++;
}
// No error return
return MMC_NO_ERROR;
}
}
return (MMC_CARD_IS_NOT_RESPONDING);
}
MMC_CC MMCTransmit(UCHAR *dBuf, UINT16 dataLength, UINT16 noBlocks, UINT16 xferMode)
{
UINT8 *dBufPtr;
UINT16 dLength;
UINT32 timer;
UINT16 y;
// Set up the buffer
dBufPtr = (UINT8 *)dBuf;
dLength = dataLength;
g_pMMCRegs->IMASK = MASK_ON_TXFIFO_WR_REQ;
while(dLength)
{
while(!(g_pMMCRegs->IREG & 0x40));
if(dLength%32==0)
{
for(y=0;y<32;y++)
{
g_pMMCRegs->TXFIFO = *dBufPtr;
dBufPtr++;
dLength--;
}
}
else
{
g_pMMCRegs->PRTBUF= BUFF_PARTIAL_FULL;
for(y=0;y<dLength;y++)
{
g_pMMCRegs->TXFIFO = *dBufPtr;
dBufPtr++;
dLength--;
}
}
}
g_pMMCRegs->IMASK = MASK_ON_DATA_TRAN_DONE; // Data Transfer Done
timer = 0xFFFF;
while((!(g_pMMCRegs->STAT & 0x00000800))&&(timer-->0))
{
if (timer<0xFF)
{
Stallms(1);
}
}
g_pMMCRegs->IMASK = MASK_ON_PRG_DONE;
if (timer>0)
{
timer = 0xFFFF;
}
while(((g_pMMCRegs->STAT & 0x00001000)==0x00000000)&&(timer-->0))
{
if (timer<0xFF)
{
Stallms(1);
}
}
g_pMMCRegs->IMASK = MASK_OFF_ALL;
if (timer==0 )
{
return MMC_CARD_IS_NOT_RESPONDING;
}
return MMC_NO_ERROR;
}
MMC_CC MMCReceive(UCHAR *dBuf, UINT16 dataLength, UINT16 noBlocks, UINT16 xferMode)
{
UINT8 *dBufPtr;
UINT16 dLength;
UINT16 y;
int i;
dBufPtr = (UINT8 *)dBuf;
g_pMMCRegs->IMASK = MASK_ON_RXFIFO_RD_REQ;
for (i=0; i<noBlocks; i++)
{
dLength = dataLength;
while(dLength)
{
if(dLength%32==0)
{
//wait for xmit_fifo_empty
while(!(g_pMMCRegs->IREG & 0x20));
for(y=0;y<32;y++)
{
*dBufPtr = (UINT8)(g_pMMCRegs->RXFIFO & 0x00FF);
dBufPtr++;
dLength--;
}
}
else
{
g_pMMCRegs->PRTBUF = BUFF_PARTIAL_FULL;
for(y=0;y<dLength;y++)
{
*dBufPtr = (UINT8)(g_pMMCRegs->RXFIFO & 0x00FF);
dBufPtr++;
dLength--;
}
}
}
}
g_pMMCRegs->IMASK = MASK_ON_DATA_TRAN_DONE; // Data Transfer Done
while(!(g_pMMCRegs->STAT & RD_DATA_AVAILABLE));
g_pMMCRegs->IMASK = MASK_OFF_ALL;
g_pMMCRegs->STRPC = STOP_CLOCK; // stop clock
while((g_pMMCRegs->STAT & CLOCK_ENABLE )); // wait for clock to stop
return MMC_NO_ERROR;
}
void MMCSetupXfer( UINT16 cmdIndex, UINT16 nBlocks, UINT16 respType)
{
UINT16 cmdDatContReg;
UINT16 blkLength;
UINT16 noBlocks = nBlocks;
cmdDatContReg = FALSE;
blkLength = 0;
mmcSetupDataRegister(cmdIndex, &cmdDatContReg, &noBlocks, &blkLength);
if (cmdIndex != SSTOP_TRANSMISSION)
{
// If None Data Transfer type, just skip
if ( cmdDatContReg != FALSE )
{
g_pMMCRegs->BLKLE = blkLength;
g_pMMCRegs->NOB = noBlocks;
}
}
// COMMAND DATA
cmdDatContReg |= respType;
g_pMMCRegs->CMDAT = cmdDatContReg;
}
BOOL MMCPrepareAndSetup(UINT32 Arg, UINT16 Cmd, UINT16 noBlocks, UINT16 Resp)
{
if (!MMCStopClock())
return FALSE;
MMCSendCommand( Arg, Cmd);
MMCSetupXfer( Cmd, noBlocks, Resp );
MMCStartClock();
return TRUE;
}
void Stallms(UINT32 msSeconds)
{
UINT32_T expireTime, time;
time = g_pOSTRegs->OSCR;
expireTime = time + (msSeconds * 1000 * OEM_TICKS_1US);
// Check if we wrapped on the expireTime
// and delay first part until wrap
if (expireTime < time)
{
while (time < g_pOSTRegs->OSCR);
}
while (g_pOSTRegs->OSCR <= expireTime);
}
void clear_controller_structure(void)
{
// Initialize internal data structure to a known state
// clear internal drive structure
DeviceController.mode = FALSE;
DeviceController.drive.card_type = FALSE;
DeviceController.drive.total_lba = 0L;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -