SAMV7 HSMCI: Add a configuration otpion to allow HSMCI to handle unaligned I/O buffers

This commit is contained in:
Gregory Nutt 2016-02-22 14:52:24 -06:00
parent 07bde1fd73
commit 9e9c50a1a3
2 changed files with 143 additions and 10 deletions

View File

@ -1493,6 +1493,16 @@ config SAMV7_HSMCI_WRPROOF
access if the internal FIFO is full. This will guarantee data access if the internal FIFO is full. This will guarantee data
integrity, not bandwidth. integrity, not bandwidth.
config SAMV7_HSMCI_UNALIGNED
bool "Unaligned I/O buffers"
default n
---help---
This option enables additional logic to handle unaligned buffers for
read and write SDIO operations. Normally this support is not
required because the HSCMI driver functions like a block driver and,
for example, when used with the FAT file system only transfers
aligned blocks of data.
config SAMV7_HSMCI_XFRDEBUG config SAMV7_HSMCI_XFRDEBUG
bool "HSMCI transfer debug" bool "HSMCI transfer debug"
depends on DEBUG_FS && DEBUG_VERBOSE depends on DEBUG_FS && DEBUG_VERBOSE

View File

@ -1491,13 +1491,63 @@ static int sam_hsmci_interrupt(struct sam_dev_s *priv)
else if ((pending & HSMCI_INT_RXRDY) != 0) else if ((pending & HSMCI_INT_RXRDY) != 0)
{ {
#ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
uint32_t value;
/* Interrupt mode data transfer support */ /* Interrupt mode data transfer support */
DEBUGASSERT(!priv->dmabusy && priv->xfrbusy && !priv->txbusy); DEBUGASSERT(!priv->dmabusy && priv->xfrbusy && !priv->txbusy);
DEBUGASSERT(priv->buffer && priv->remaining > 0); DEBUGASSERT(priv->buffer && priv->remaining > 0);
/* Is the receiving buffer aligned? */
value = sam_getreg(priv, SAM_HSMCI_RDR_OFFSET);
if (((uintptr_t)priv->buffer & 3) == 0 &&
priv->remaining >= sizeof(uint32_t))
{
/* Yes.. transfer 32-bits at a time */
*priv->buffer++ = value;
priv->remaining -= sizeof(uint32_t);
}
else
{
/* No.. transfer 8-bits at a time (little endian source) */
uint8_t *dest8 = (uint8_t *)priv->buffer;
/* Unpack little endian; write in native byte order */
*dest8++ = (value & 0xff);
if (--priv->remaining > 0)
{
*dest8++ = ((value >> 8) & 0xff);
if (--priv->remaining > 0)
{
*dest8++ = ((value >> 16) & 0xff);
if (--priv->remaining > 0)
{
*dest8++ = ((value >> 24) & 0xff);
}
}
}
priv->buffer = (uint32_t *)dest8;
}
#else
/* Interrupt mode data transfer support */
DEBUGASSERT(!priv->dmabusy && priv->xfrbusy && !priv->txbusy);
DEBUGASSERT(priv->buffer && priv->remaining > 0);
/* Transfer 32-bit aligned data */
*priv->buffer++ = sam_getreg(priv, SAM_HSMCI_RDR_OFFSET); *priv->buffer++ = sam_getreg(priv, SAM_HSMCI_RDR_OFFSET);
priv->remaining -= sizeof(uint32_t); priv->remaining -= sizeof(uint32_t);
#endif
/* Are we finished? */ /* Are we finished? */
@ -2080,7 +2130,9 @@ static int sam_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
struct sam_dev_s *priv = (struct sam_dev_s *)dev; struct sam_dev_s *priv = (struct sam_dev_s *)dev;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0); #ifndef CONFIG_SAMV7_HSMCI_UNALIGNED
DEBUGASSERT(((uint32_t)buffer & 3) == 0 && (buflen & 3) == 0);
#endif
/* Initialize register sampling */ /* Initialize register sampling */
@ -2132,12 +2184,14 @@ static int sam_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
size_t buflen) size_t buflen)
{ {
struct sam_dev_s *priv = (struct sam_dev_s *)dev; struct sam_dev_s *priv = (struct sam_dev_s *)dev;
unsigned int nwords; unsigned int remaining;
const uint32_t *ptr; const uint32_t *src;
uint32_t sr; uint32_t sr;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0); #ifndef CONFIG_SAMV7_HSMCI_UNALIGNED
DEBUGASSERT(((uintptr_t)buffer & 3) == 0 && (buflen & 3) == 0);
#endif
/* Disable DMA handshaking */ /* Disable DMA handshaking */
@ -2158,10 +2212,10 @@ static int sam_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
* disable pre-emption around this loop. * disable pre-emption around this loop.
*/ */
nwords = (buflen + 3) >> 2; src = (const uint32_t *)buffer;
ptr = (const uint32_t *)buffer; remaining = buflen;
while (nwords > 0) while (remaining > 0)
{ {
/* Check the HSMCI status */ /* Check the HSMCI status */
@ -2177,8 +2231,53 @@ static int sam_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
{ {
/* TXRDY -- transfer another word */ /* TXRDY -- transfer another word */
sam_putreg(priv, *ptr++, SAM_HSMCI_TDR_OFFSET); #ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
nwords--; /* First check: Do we we have 32-bit address alignment? Are we
* transferring a full 32-bits?
*/
if (((uintptr_t)priv->buffer & 3) == 0 &&
priv->remaining >= sizeof(uint32_t))
{
/* Yes.. transfer 32-bits at a time */
sam_putreg(priv, *src++, SAM_HSMCI_TDR_OFFSET);
remaining -= sizeof(uint32_t);
}
else
{
/* No.. transfer 8-bits at a time (little endian destination) */
const uint8_t *src8 = (const uint8_t *)src;
uint32_t value;
/* Read data in native byte order, pack little endian */
value = (uint32_t)*src8++;
if (--remaining > 0)
{
value |= ((uint32_t)*src8++ << 8);
if (--remaining > 0)
{
value |= ((uint32_t)*src8++ << 16);
if (--remaining > 0)
{
value |= ((uint32_t)*src8++ << 24);
}
}
}
src = (const uint32_t *)src8;
sam_putreg(priv, value, SAM_HSMCI_TDR_OFFSET);
}
#else
/* Transfer 32-bit aligned data */
sam_putreg(priv, *src++, SAM_HSMCI_TDR_OFFSET);
remaining -= sizeof(uint32_t);
#endif
} }
} }
@ -2832,7 +2931,19 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
unsigned int i; unsigned int i;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
#ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
/* 32-bit buffer alignment is required for DMA transfers */
if (((uintptr_t)buffer & 3) != 0 || (buflen & 3) != 0)
{
/* Fall back and do a non-DMA transfer */
return sam_recvsetup(dev, buffer, buflen);
}
#else
DEBUGASSERT(((uint32_t)buffer & 3) == 0); DEBUGASSERT(((uint32_t)buffer & 3) == 0);
#endif
/* How many blocks? That should have been saved by the sam_blocksetup() /* How many blocks? That should have been saved by the sam_blocksetup()
* method earlier. * method earlier.
@ -2928,7 +3039,19 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
unsigned int i; unsigned int i;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
#ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
/* 32-bit buffer alignment is required for DMA transfers */
if (((uintptr_t)buffer & 3) != 0 || (buflen & 3) != 0)
{
/* Fall back and do a non-DMA transfer */
return sam_sendsetup(dev, buffer, buflen);
}
#else
DEBUGASSERT(((uint32_t)buffer & 3) == 0 && (buflen & 3) == 0);
#endif
/* How many blocks? That should have been saved by the sam_blocksetup() /* How many blocks? That should have been saved by the sam_blocksetup()
* method earlier. * method earlier.