From 70be3bae161916fad9ea1fd77291d91b6b4ebeee Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 29 Jul 2014 21:13:28 -0600 Subject: [PATCH] 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 --- arch/arm/src/sama5/sam_hsmci.c | 264 +++++++++++++++++++++++++-------- 1 file changed, 200 insertions(+), 64 deletions(-) diff --git a/arch/arm/src/sama5/sam_hsmci.c b/arch/arm/src/sama5/sam_hsmci.c index b47833c77e..322032980d 100644 --- a/arch/arm/src/sama5/sam_hsmci.c +++ b/arch/arm/src/sama5/sam_hsmci.c @@ -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