SAMV71 QSPI: Add methods to allocate properly aligned memory.

This commit is contained in:
Gregory Nutt 2015-09-06 09:34:51 -06:00
parent da3c05a898
commit b30e6a696e

View File

@ -54,6 +54,7 @@
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/clock.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spi/qspi.h>
#include "up_internal.h"
@ -127,6 +128,16 @@
#define MEMORY_SYNC() do { ARM_DSB();ARM_ISB(); } while (0)
/* The SAMV7x QSPI driver insists that transfers be performed in multiples
* of 32-bits.
*/
#define ALIGN_SHIFT 2
#define ALIGN_MASK 3
#define ALIGN_UP(n) (((n)+ALIGN_MASK) & ~ALIGN_MASK)
#define ALIGN_WORDS(n) (((n)+ALIGN_MASK) >> ALIGN_SHIFT)
#define IS_ALIGNED(n) (((uint32_t)(n) & ALIGN_MASK) == 0)
/* Debug *******************************************************************/
/* Check if QSPI debug is enabled (non-standard.. no support in
* include/debug.h
@ -268,6 +279,8 @@ static int qspi_memory_dma(struct sam_qspidev_s *priv,
static int qspi_memory_nodma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo);
static void qspi_memcpy(uint32_t *dest, const uint32_t *src,
size_t wordlen);
/* Interrupts */
@ -288,6 +301,8 @@ static int qspi_command(struct qspi_dev_s *dev,
struct qspi_cmdinfo_s *cmdinfo);
static int qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo);
static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen);
static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer);
/* Initialization */
@ -308,6 +323,8 @@ static const struct qspi_ops_s g_qspi0ops =
.setbits = qspi_setbits,
.command = qspi_command,
.memory = qspi_memory,
.alloc = qspi_alloc,
.free = qspi_free,
};
/* This is the overall state of the QSPI0 controller */
@ -971,6 +988,11 @@ static int qspi_memory_nodma(struct sam_qspidev_s *priv,
struct qspi_meminfo_s *meminfo)
{
uintptr_t qspimem = SAM_QSPIMEM_BASE + meminfo->addr;
uint16_t wordlen;
/* Get the length as an even multiple of 32-bit words. */
wordlen = ALIGN_WORDS(meminfo->buflen);
/* Enable the memory transfer */
@ -980,11 +1002,13 @@ static int qspi_memory_nodma(struct sam_qspidev_s *priv,
if (QSPIMEM_ISWRITE(meminfo->flags))
{
memcpy((void *)qspimem, meminfo->buffer, meminfo->buflen);
qspi_memcpy((uint32_t *)qspimem,
(const uint32_t *)meminfo->buffer, wordlen);
}
else
{
memcpy(meminfo->buffer, (void *)qspimem, meminfo->buflen);
qspi_memcpy((uint32_t *)meminfo->buffer,
(const uint32_t *)qspimem, wordlen);
}
MEMORY_SYNC();
@ -1007,6 +1031,30 @@ static int qspi_memory_nodma(struct sam_qspidev_s *priv,
return OK;
}
/****************************************************************************
* Name: qspi_memcpy
*
* Description:
* 32-bit version of memcpy.
*
* Input Parameters:
* dest - Destination address of the copy
* src - Source address of the copy
* wordlen - The number of 32-bit words to copy.
*
* Returned Value:
* None
*
****************************************************************************/
static void qspi_memcpy(uint32_t *dest, const uint32_t *src, size_t wordlen)
{
for (; wordlen > 0; wordlen--)
{
*dest++ = *src++;
}
}
/****************************************************************************
* Name: qspi_lock
*
@ -1373,16 +1421,14 @@ static int qspi_command(struct qspi_dev_s *dev,
if (QSPICMD_ISDATA(cmdinfo->flags))
{
uint16_t buflen;
uint16_t wordlen;
DEBUGASSERT(cmdinfo->buffer != NULL && cmdinfo->buflen > 0);
DEBUGASSERT(IS_ALIGNED(cmdinfo->buffer));
/* Make sure that the length is an even multiple of 32-bit words.
* REVISIT: This could cause access past the end of an allocated
* buffer.
*/
/* Get the length as an even multiple of 32-bit words. */
buflen = (cmdinfo->buflen + 3) & ~3;
wordlen = ALIGN_WORDS(cmdinfo->buflen);
/* Write Instruction Frame Register:
*
@ -1411,7 +1457,8 @@ static int qspi_command(struct qspi_dev_s *dev,
/* Copy the data to write to QSPI_RAM */
memcpy((void *)SAM_QSPIMEM_BASE, cmdinfo->buffer, buflen);
qspi_memcpy((uint32_t *)SAM_QSPIMEM_BASE,
(const uint32_t *)cmdinfo->buffer, wordlen);
}
else
{
@ -1421,7 +1468,9 @@ static int qspi_command(struct qspi_dev_s *dev,
(void)qspi_getreg(priv, SAM_QSPI_IFR_OFFSET);
/* Copy the data from QSPI memory into the user buffer */
memcpy(cmdinfo->buffer, (const void *)SAM_QSPIMEM_BASE, buflen);
qspi_memcpy((uint32_t *)cmdinfo->buffer,
(const uint32_t *)SAM_QSPIMEM_BASE, wordlen);
}
MEMORY_SYNC();
@ -1506,6 +1555,55 @@ static int qspi_memory(struct qspi_dev_s *dev,
}
}
/****************************************************************************
* Name: QSPI_ALLOC
*
* Description:
* Allocate a buffer suitable for DMA data transfer
*
* Input Parameters:
* dev - Device-specific state data
* buflen - Buffer length to allocate in bytes
*
* Returned Value:
* Address of tha allocated memory on success; NULL is returned on any
* failure.
*
****************************************************************************/
static FAR void *qspi_alloc(FAR struct qspi_dev_s *dev, size_t buflen)
{
/* Here we exploit the internal knowlege the kmm_malloc() will return memory
* aligned to 64-bit addresses. The buffer length must be large enough to
* hold the rested buflen in units a 32-bits.
*/
return kmm_malloc(ALIGN_UP(buflen));
}
/****************************************************************************
* Name: QSPI_FREE
*
* Description:
* Free memory returned by QSPI_ALLOC
*
* Input Parameters:
* dev - Device-specific state data
* buffer - Buffer previously allocated via QSPI_ALLOC
*
* Returned Value:
* None.
*
****************************************************************************/
static void qspi_free(FAR struct qspi_dev_s *dev, FAR void *buffer)
{
if (buffer)
{
kmm_free(buffer);
}
}
/****************************************************************************
* Name: qspi_hw_initialize
*