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
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
bool "HSMCI transfer debug"
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)
{
#ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
uint32_t value;
/* Interrupt mode data transfer support */
DEBUGASSERT(!priv->dmabusy && priv->xfrbusy && !priv->txbusy);
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->remaining -= sizeof(uint32_t);
#endif
/* 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;
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 */
@ -2132,12 +2184,14 @@ 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;
unsigned int remaining;
const uint32_t *src;
uint32_t sr;
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 */
@ -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.
*/
nwords = (buflen + 3) >> 2;
ptr = (const uint32_t *)buffer;
src = (const uint32_t *)buffer;
remaining = buflen;
while (nwords > 0)
while (remaining > 0)
{
/* 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 */
sam_putreg(priv, *ptr++, SAM_HSMCI_TDR_OFFSET);
nwords--;
#ifdef CONFIG_SAMV7_HSMCI_UNALIGNED
/* 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;
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);
#endif
/* How many blocks? That should have been saved by the sam_blocksetup()
* method earlier.
@ -2928,7 +3039,19 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
unsigned int i;
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()
* method earlier.