SAMA5D HSMCI: Add method to do RX transfer without DMA. The 8-byte SCR transfer was failing silently with the DMA transfer, leaving the SD card in single bit mode

This commit is contained in:
Gregory Nutt 2014-07-29 21:13:28 -06:00
parent 8bbbc5b255
commit 059812c872

View File

@ -185,6 +185,7 @@
* I don't have any clue why at the moment. This option suppresses TX DMA (only).
*/
#undef HSCMI_NORXDMA
#define HSCMI_NOTXDMA 1
/* Timing */
@ -264,7 +265,7 @@
#define HSMCI_DATA_TIMEOUT_ERRORS \
( HSMCI_INT_CSTOE | HSMCI_INT_DTOE )
#define HSMCI_DATA_DMARECV_ERRORS \
#define HSMCI_DATA_RECV_ERRORS \
( HSMCI_INT_OVRE | HSMCI_INT_BLKOVRE | HSMCI_INT_CSTOE | HSMCI_INT_DTOE | \
HSMCI_INT_DCRCE )
@ -285,8 +286,10 @@
* 1: DMA buffer transfer has completed.
*/
#define HSMCI_RECV_INTS \
( HSMCI_DATA_RECV_ERRORS | HSMCI_INT_RXRDY)
#define HSMCI_DMARECV_INTS \
( HSMCI_DATA_DMARECV_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
( HSMCI_DATA_RECV_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
#define HSMCI_DMASEND_INTS \
( HSMCI_DATA_DMASEND_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
@ -405,6 +408,11 @@ struct sam_dev_s
uint32_t xfrmask; /* Interrupt enables for data transfer */
/* Interrupt mode data transfer support */
uint32_t *buffer; /* Address of current R/W buffer */
ssize_t remaining; /* Number of bytes remaining in the transfer */
/* DMA data transfer support */
bool widebus; /* Required for DMA support */
@ -542,6 +550,10 @@ static int sam_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
uint32_t arg);
static void sam_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
unsigned int nblocks);
static int sam_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
size_t nbytes);
static int sam_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
size_t nbytes);
static int sam_cancel(FAR struct sdio_dev_s *dev);
static int sam_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
static int sam_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
@ -567,10 +579,14 @@ static int sam_registercallback(FAR struct sdio_dev_s *dev,
#ifdef CONFIG_SDIO_DMA
static bool sam_dmasupported(FAR struct sdio_dev_s *dev);
#endif
#ifndef HSCMI_NORXDMA
static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev,
FAR uint8_t *buffer, size_t buflen);
#endif
#ifndef HSCMI_NOTXDMA
static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen);
#endif
/* Initialization/uninitialization/reset ************************************/
@ -590,8 +606,8 @@ static const struct sdio_dev_s g_callbacks =
.attach = sam_attach,
.sendcmd = sam_sendcmd,
.blocksetup = sam_blocksetup,
.recvsetup = sam_dmarecvsetup,
.sendsetup = sam_dmasendsetup,
.recvsetup = sam_recvsetup,
.sendsetup = sam_sendsetup,
.cancel = sam_cancel,
.waitresponse = sam_waitresponse,
.recvR1 = sam_recvshort,
@ -607,8 +623,16 @@ static const struct sdio_dev_s g_callbacks =
.registercallback = sam_registercallback,
#ifdef CONFIG_SDIO_DMA
.dmasupported = sam_dmasupported,
#ifndef HSCMI_NORXDMA
.dmarecvsetup = sam_dmarecvsetup,
#else
.dmarecvsetup = sam_recvsetup,
#endif
#ifndef HSCMI_NOTXDMA
.dmasendsetup = sam_dmasendsetup,
#else
.dmasendsetup = sam_sendsetup,
#endif
#endif
};
@ -1498,12 +1522,12 @@ static int sam_hsmci_interrupt(struct sam_dev_s *priv)
}
/* Handle in progress, interrupt driven data transfers ****************/
/* Do any of these interrupts signal the end a data transfer? */
/* Do any of these interrupts signal a data transfer event? */
pending = enabled & priv->xfrmask;
if (pending != 0)
{
/* Yes.. the transfer is complete. Did it complete with an error? */
/* Yes.. Did the transfer complete with an error? */
if ((pending & HSMCI_DATA_ERRORS) != 0)
{
@ -1523,9 +1547,40 @@ static int sam_hsmci_interrupt(struct sam_dev_s *priv)
sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE|SDIOWAIT_ERROR);
}
}
/* No, If RXRDY is enabled, then we are doing a non-DMA receive.
* We need to transfer word(s) from the RDR register to the user
* buffer.
*/
else if ((pending & HSMCI_INT_RXRDY) != 0)
{
/* Interrupt mode data transfer support */
DEBUGASSERT(!priv->dmabusy && priv->xfrbusy && !priv->txbusy);
DEBUGASSERT(priv->buffer && priv->remaining > 0);
*priv->buffer++ = sam_getreg(priv, SAM_HSMCI_RDR_OFFSET);
priv->remaining -= sizeof(uint32_t);
/* Are we finished? */
if (priv->remaining <= 0)
{
/* Yes.. End the transfer */
priv->buffer = NULL;
priv->remaining = 0;
sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
}
/* Otherwise it must be a DMA transfer that completed successfully */
else
{
/* No.. Then the transfer must have completed successfully */
/* End the transfer */
sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
}
@ -2076,6 +2131,138 @@ static void sam_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
sam_putreg(priv, regval, SAM_HSMCI_BLKR_OFFSET);
}
/****************************************************************************
* Name: sam_recvsetup
*
* Description:
* Setup hardware in preparation for data transfer from the card in non-DMA
* (interrupt driven mode). This method will do whatever controller setup
* is necessary. This would be called for SD memory just BEFORE sending
* CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18
* (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, SDIO_WAITEVENT
* will be called to receive the indication that the transfer is complete.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
* buffer - Address of the buffer in which to receive the data
* buflen - The number of bytes in the transfer
*
* Returned Value:
* Number of bytes sent on success; a negated errno on failure
*
****************************************************************************/
static int sam_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
size_t buflen)
{
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
/* Initialize register sampling */
sam_xfrsampleinit(priv);
sam_xfrsample(priv, SAMPLENDX_BEFORE_SETUP);
/* Disable DMA handshaking */
sam_putreg(priv, 0, SAM_HSMCI_DMA_OFFSET);
/* Setup of the transfer configuration */
priv->dmabusy = false;
priv->xfrbusy = true;
priv->txbusy = false;
/* Save the destination buffer information for use by the interrupt handler */
priv->buffer = (uint32_t*)buffer;
priv->remaining = buflen;
/* And enable interrupts */
sam_configxfrints(priv, HSMCI_RECV_INTS);
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
return OK;
}
/****************************************************************************
* Name: sam_sendsetup
*
* Description:
* Setup hardware in preparation for data transfer from the card. This method
* will do whatever controller setup is necessary. This would be called
* for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25
* (WRITE_MULTIPLE_BLOCK), ... and before SDIO_SENDDATA is called.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
* buffer - Address of the buffer containing the data to send
* buflen - The number of bytes in the transfer
*
* Returned Value:
* Number of bytes sent on success; a negated errno on failure
*
****************************************************************************/
static int sam_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
size_t buflen)
{
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
unsigned int nwords;
const uint32_t *ptr;
uint32_t sr;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
/* Disable DMA handshaking */
sam_putreg(priv, 0, SAM_HSMCI_DMA_OFFSET);
sam_configxfrints(priv, HSMCI_DMASEND_INTS);
priv->dmabusy = false;
priv->xfrbusy = true;
priv->txbusy = true;
/* Nullify register sampling */
sam_xfrsampleinit(priv);
/* Copy each word to the TX FIFO
*
* REVISIT: If TX data underruns occur, then it may be necessary to
* disable pre-emption around this loop.
*/
nwords = (buflen + 3) >> 2;
ptr = (const uint32_t *)buffer;
while (nwords > 0)
{
/* Check the HSMCI status */
sr = sam_getreg(priv, SAM_HSMCI_SR_OFFSET);
if ((sr & HSMCI_DATA_DMASEND_ERRORS) != 0)
{
/* Some fatal error has occurred */
fdbg("ERROR: sr %08x\n", sr);
return -EIO;
}
else if ((sr & HSMCI_INT_TXRDY) != 0)
{
/* TXRDY -- transfer another word */
sam_putreg(priv, *ptr++, SAM_HSMCI_TDR_OFFSET);
nwords--;
}
}
return OK;
}
/****************************************************************************
* Name: sam_cancel
*
@ -2703,6 +2890,7 @@ static bool sam_dmasupported(FAR struct sdio_dev_s *dev)
*
****************************************************************************/
#ifndef HSCMI_NORXDMA
static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
size_t buflen)
{
@ -2729,7 +2917,7 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
DEBUGASSERT(nblocks > 0 && blocksize > 0 && (blocksize & 3) == 0);
/* Physical address of the HSCMI source register, either the TDR (for
* single transfers) or the first FIFO register, and the physcal address
* single transfers) or the first FIFO register, and the physical address
* of the buffer in RAM.
*/
@ -2777,6 +2965,7 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
sam_configxfrints(priv, HSMCI_DMARECV_INTS);
return OK;
}
#endif
/****************************************************************************
* Name: sam_dmasendsetup
@ -2797,10 +2986,10 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
*
****************************************************************************/
#ifndef HSCMI_NOTXDMA
static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen)
{
#ifndef HSCMI_NOTXDMA
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
uint32_t paddr;
uint32_t maddr;
@ -2823,7 +3012,7 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
DEBUGASSERT(nblocks > 0 && blocksize > 0 && (blocksize & 3) == 0);
/* Physical address of the HSCMI source register, either the TDR (for
* single transfers) or the first FIFO register, and the physcal address
* single transfers) or the first FIFO register, and the physical address
* of the buffer in RAM.
*/
@ -2869,62 +3058,9 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
sam_configxfrints(priv, HSMCI_DMASEND_INTS);
#else
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
unsigned int nwords;
const uint32_t *ptr;
uint32_t sr;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
/* Disable DMA handshaking */
sam_putreg(priv, 0, SAM_HSMCI_DMA_OFFSET);
sam_configxfrints(priv, HSMCI_DMASEND_INTS);
priv->dmabusy = false;
priv->xfrbusy = true;
priv->txbusy = true;
/* Nullify register sampling */
sam_xfrsampleinit(priv);
/* Copy each word to the TX FIFO
*
* REVISIT: If TX data underruns occur, then it may be necessary to
* disable pre-emption around this loop.
*/
nwords = (buflen + 3) >> 2;
ptr = (const uint32_t *)buffer;
while (nwords > 0)
{
/* Check the HSMCI status */
sr = sam_getreg(priv, SAM_HSMCI_SR_OFFSET);
if ((sr & HSMCI_DATA_DMASEND_ERRORS) != 0)
{
/* Some fatal error has occurred */
fdbg("ERROR: sr %08x\n", sr);
return -EIO;
}
else if ((sr & HSMCI_INT_TXRDY) != 0)
{
/* TXRDY -- transfer another word */
sam_putreg(priv, *ptr++, SAM_HSMCI_TDR_OFFSET);
nwords--;
}
}
#endif
return OK;
}
#endif
/****************************************************************************
* Initialization/uninitialization/reset