📄 mci.c
字号:
/*****************************************************************************
* mci.c: SD/MMC module file for NXP LPC23xx/24xx Family Microprocessors
*
* Copyright(C) 2006, NXP Semiconductor
* All rights reserved.
*
* History
* 2006.07.20 ver 1.00 Prelimnary version, first Release
*
******************************************************************************/
#include "LPC2468.h" /* LPC23xx/24xx definitions */
#include "type.h"
#include "irq.h"
#include "mci.h"
#include "dma.h"
volatile DWORD MCI_DataErrorProcess_count = 0;
volatile DWORD MCI_DATA_END_InterruptService_count = 0;
volatile DWORD MCI_FIFOInterruptService_count = 0;
volatile DWORD MCI_CmdProcess_count = 0;
volatile DWORD CmdCRCErrCount = 0;
volatile DWORD CmdTimeoutErrCount = 0;
volatile DWORD CmdRespEndCount = 0;
volatile DWORD CmdSentCount = 0;
volatile DWORD CmdActiveCount = 0;
volatile DWORD DataCRCErrCount = 0;
volatile DWORD DataTimeoutErrCount = 0;
volatile DWORD DataTxUnderrunErrCount = 0;
volatile DWORD DataRxOverrunErrCount = 0;
volatile DWORD DataStartbitErrCount = 0;
volatile DWORD DataEndCount = 0;
volatile DWORD DataBlockEndCount = 0;
volatile DWORD MCI_Block_End_Flag = 0;
volatile DWORD DataTxActiveCount = 0;
volatile DWORD DataRxActiveCount = 0;
volatile DWORD DataFIFOCount = 0;
volatile DWORD DataRxFIFOCount = 0;
volatile DWORD DataTxFIFOCount = 0;
volatile DWORD CardRCA;
volatile DWORD MCI_CardType;
/* Assembly modules using the ARM block transfer instruction in readfifo.s. */
extern DWORD MCI_ReadFifo(DWORD * dest);
extern DWORD MCI_WriteFifo(DWORD * src);
volatile BYTE *WriteBlock = (BYTE *)(DMA_SRC); /* treat WriteBlock as a constant address */
volatile BYTE *ReadBlock = (BYTE *)(DMA_DST); /* treat ReadBlock as a constant address */
volatile DWORD TXBlockCounter=0, RXBlockCounter=0;
extern DWORD IntStatusBuffer[30];
extern BYTE IntIndex;
#if MCI_DMA_ENABLED
/******************************************************************************
** Function name: DMAHandler
**
** Descriptions: DMA interrupt handler for MCI
**
** parameters: None
** Returned value: None
**
******************************************************************************/
__irq __arm void DMAHandler (void)
{
DWORD regVal;
static DWORD DMATCCount, DMAErrCount;
regVal = GPDMA_INT_TCSTAT;
if ( regVal )
{
DMATCCount++;
GPDMA_INT_TCCLR |= regVal;
}
regVal = GPDMA_INT_ERR_STAT;
if ( regVal )
{
DMAErrCount++;
GPDMA_INT_ERR_CLR |= regVal;
}
VICVectAddr = 0; /* Acknowledge Interrupt */
}
/******************************************************************************
** Function name: DMA_Init
**
** Descriptions: Setup GPDMA clock and install interrupt handler
**
** parameters: None
** Returned value: TRUE or FALSE, FALSE if ISR can't be installed.
**
******************************************************************************/
DWORD DMA_Init(void)
{
PCONP |= (1 << 29); /* Enable GPDMA clock */
/* clear all interrupts on channel 0 and 1 */
GPDMA_INT_TCCLR = 0x03;
GPDMA_INT_ERR_CLR = 0x03;
GPDMA_CONFIG = 0x01; /* Enable DMA channels, little endian */
while ( !(GPDMA_CONFIG & 0x01) );
if ( install_irq( GPDMA_INT, (void *)DMAHandler, HIGHEST_PRIORITY ) == FALSE )
{
return( FALSE );
}
return( TRUE );
}
/******************************************************************************
** Function name: DMA_Move
**
** Descriptions: Setup GPDMA for MCI DMA transfer
** including mode, M2P or M2M, or P2M,
** src and dest. address, control reg. etc.
**
** parameters: Channel number, DMA mode
** Returned value: TRUE or FALSE
**
******************************************************************************/
DWORD DMA_Move( DWORD ChannelNum, DWORD DMAMode )
{
/* USB RAM is used for test.
Please note, Ethernet has its own SRAM, but GPDMA can't access
that. GPDMA can access USB SRAM and IRAM. Ethernet DMA controller can
access both IRAM and Ethernet SRAM. */
if ( ChannelNum == 0 )
{
GPDMA_INT_TCCLR = 0x01;
GPDMA_INT_ERR_CLR = 0x01;
if ( DMAMode == M2M )
{
/* Ch0 is set for M2M tranfer from AHB1 to AHB2 SRAM */
GPDMA_CH0_SRC = DMA_SRC;
GPDMA_CH0_DEST = DMA_DST;
/* Terminal Count Int enable */
GPDMA_CH0_CTRL = (DMA_SIZE & 0x0FFF) | (0x04 << 12) | (0x04 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 26) | (1 << 27) | 0x80000000;
}
else if ( DMAMode == M2P )
{
/* Ch0 set for M2P transfer from mempry to MCI FIFO. */
GPDMA_CH0_SRC = DMA_SRC;
GPDMA_CH0_DEST = DMA_MCIFIFO;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH0_CTRL = (DMA_SIZE & 0x0FFF) | (0x04 << 12) | (0x02 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 26) | 0x80000000;
}
else if ( DMAMode == P2M )
{
/* Ch0 set for P2M transfer from MCI FIFO to memory. */
GPDMA_CH0_SRC = DMA_MCIFIFO;
GPDMA_CH0_DEST = DMA_DST;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH0_CTRL = (DMA_SIZE & 0x0FFF) | (0x02 << 12) | (0x04 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 27) | 0x80000000;
}
else
{
return ( FALSE );
}
}
else if ( ChannelNum == 1 )
{
GPDMA_INT_TCCLR = 0x02;
GPDMA_INT_ERR_CLR = 0x02;
if ( DMAMode == M2M )
{
/* Ch1 is set for M2M tranfer */
GPDMA_CH1_SRC = DMA_SRC;
GPDMA_CH1_DEST = DMA_DST;
/* Terminal Count Int enable */
GPDMA_CH1_CTRL = (DMA_SIZE & 0x0FFF) | (0x04 << 12) | (0x04 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 26) | (1 << 27) | 0x80000000;
}
else if ( DMAMode == M2P )
{
/* Ch1 set for M2P transfer from memory to MCI FIFO. */
GPDMA_CH1_SRC = DMA_SRC;
GPDMA_CH1_DEST = DMA_MCIFIFO;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH1_CTRL = (DMA_SIZE & 0x0FFF) | (0x04 << 12) | (0x02 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 26) | 0x80000000;
}
else if ( DMAMode == P2M )
{
/* Ch1 set for P2M transfer from MCI_FIFO to memory. */
GPDMA_CH1_SRC = DMA_MCIFIFO;
GPDMA_CH1_DEST = DMA_DST;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH1_CTRL = (DMA_SIZE & 0x0FFF) | (0x02 << 12) | (0x04 << 15)
| (0x02 << 18) | (0x02 << 21) | (1 << 27) | 0x80000000;
}
else
{
return ( FALSE );
}
}
else
{
return ( FALSE );
}
GPDMA_CONFIG = 0x01; /* Enable DMA channels, little endian */
while ( !(GPDMA_CONFIG & 0x01) );
return (TRUE);
}
#endif
/******************************************************************************
** Function name: MCI_Interrupt related
**
** Descriptions: MCI interrupt handler and related APIs
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_TXEnable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_TXDisable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_RXEnable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable RX interrupts only */
MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable RX interrupts only */
#else
MCI_MASK0 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
MCI_MASK1 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
#endif
return;
}
/*****************************************************************/
void MCI_RXDisable( void )
{
#if MCI_DMA_ENABLED
MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable TX interrupts only */
MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* Enable TX interrupts only */
#else
MCI_MASK0 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
MCI_MASK1 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
#endif
return;
}
/******************************************************************************
** Function name: MCI_CheckStatus
**
** Descriptions: MCI Check status before and after the block read and
** write. Right after the block read and write, this routine
** is important that, even the FIFO is empty, complete
** block has been sent, but, data is still being written
** to the card, this routine is to ensure that the data
** has been written based on the state of the card, not
** by the length being set.
**
** parameters: None
** Returned value: TRUE or FALSE
**
******************************************************************************/
DWORD MCI_CheckStatus( void )
{
DWORD respValue;
while ( 1 )
{
if ( (respValue = MCI_Send_Status()) == INVALID_RESPONSE )
{
break;
}
else
{
/* The only valid state is TRANS per MMC and SD state diagram.
RCV state may be seen, but, I have found that it happens
only when TX_ACTIVE or RX_ACTIVE occurs before the WRITE_BLOCK and
READ_BLOCK cmds are being sent, which is not a valid sequence. */
if ( (respValue & (0x0F << 8)) == 0x0900 )
{
return ( TRUE );
}
}
}
return ( FALSE );
}
/******************************************************************************
** Function name: MCI_CmdProcess
**
** Descriptions: Called by MCI interrupt handler
** To simplify the process, for card initialization, the
** CMD interrupts are disabled.
**
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_CmdProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_CMD_CRC_FAIL )
{
CmdCRCErrCount++;
MCI_CLEAR = MCI_CMD_CRC_FAIL;
}
if ( MCIStatus & MCI_CMD_TIMEOUT )
{
CmdTimeoutErrCount++;
MCI_CLEAR = MCI_CMD_TIMEOUT;
}
/* Cmd Resp End or Cmd Sent */
if ( MCIStatus & MCI_CMD_RESP_END )
{
CmdRespEndCount++;
MCI_CLEAR = MCI_CMD_RESP_END;
}
if ( MCIStatus & MCI_CMD_SENT )
{
CmdSentCount++;
MCI_CLEAR = MCI_CMD_SENT;
}
if ( MCIStatus & MCI_CMD_ACTIVE )
{
CmdActiveCount++;
MCI_CLEAR = MCI_CMD_ACTIVE;
}
return;
}
/******************************************************************************
** Function name: MCI_DataErrorProcess
**
** Descriptions: Called by MCI interrupt handler
** Process data error.
**
** parameters: None
** Returned value: None
**
******************************************************************************/
void MCI_DataErrorProcess( void )
{
DWORD MCIStatus;
MCIStatus = MCI_STATUS;
if ( MCIStatus & MCI_DATA_CRC_FAIL )
{
DataCRCErrCount++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -