SAMV7 QSPI: Check if data to be transferred is unaligned; If unaligned, don't use DMA

This commit is contained in:
Gregory Nutt 2015-11-11 14:28:56 -06:00
parent 8554cdbff6
commit 003e022ce9
2 changed files with 79 additions and 51 deletions

View File

@ -42,11 +42,12 @@
#include <errno.h> #include <errno.h>
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <arch/samv7/chip.h> /* For SAMV7_FLASH_SIZE */ #include <arch/samv7/chip.h> /* For SAMV7_ALIGNED_FLASH_SIZE */
#include "sam_flash.h"
#include "up_arch.h" #include "up_arch.h"
#include "cache.h"
#include "sam_flash.h"
/**************************************************************************** /****************************************************************************
* Pre-processor Definitions * Pre-processor Definitions
@ -74,11 +75,14 @@
#define ALIGN_UP(v,m) (((v) + (m)) & ~(m)) #define ALIGN_UP(v,m) (((v) + (m)) & ~(m))
#define ALIGN_DOWN(v,m) ((v) & ~(m)) #define ALIGN_DOWN(v,m) ((v) & ~(m))
#define SAMV7_ALIGNED_FLASH_BASE ALIGN_UP(SAMV7_FREE_FLASH_BASE) #define SAMV7_ALIGNED_FLASH_BASE ALIGN_UP(SAMV7_FREE_FLASH_BASE)
#define SAMV7_FREE_FLASH_SIZE (SAMV7_FLASH_SIZE - SAMV7_ALIGNED_FLASH_BASE) #define SAMV7_FREE_FLASH_SIZE (SAMV7_ALIGNED_FLASH_SIZE - SAMV7_ALIGNED_FLASH_BASE)
#define SAMV7_ALIGNED_FLASH_SIZE ALIGN_DOWN(SAMV7_FREE_FLASH_SIZE) #define SAMV7_ALIGNED_FLASH_SIZE ALIGN_DOWN(SAMV7_FREE_FLASH_SIZE)
#define SAMV7_NSECTORS (SAMV7_ALIGNED_FLASH_SIZE >> SAMV7_SECTOR_SHIFT) #define SAMV7_NSECTORS (SAMV7_ALIGNED_FLASH_SIZE >> SAMV7_SECTOR_SHIFT)
#define SAMV7_NPAGES (SAMV7_ALIGNED_FLASH_SIZE >> SAMV7_PAGE_SHIFT) #define SAMV7_NPAGES (SAMV7_ALIGNED_FLASH_SIZE >> SAMV7_PAGE_SHIFT)
#define SAMV7_WRITE_ALIGN (16)
#define SAMV7_WRITE_ALIGN_MASK (15)
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -102,7 +106,7 @@ static void sam_flash_unlock(void)
size_t up_progmem_npages(void) size_t up_progmem_npages(void)
{ {
return SAMV7_FLASH_NPAGES; return SAMV7_NPAGES;
} }
/**************************************************************************** /****************************************************************************
@ -128,7 +132,7 @@ bool up_progmem_isuniform(void)
size_t up_progmem_pagesize(size_t page) size_t up_progmem_pagesize(size_t page)
{ {
return SAMV7_FLASH_PAGESIZE; return SAMV7_PAGE_SIZE;
} }
/**************************************************************************** /****************************************************************************
@ -138,7 +142,7 @@ size_t up_progmem_pagesize(size_t page)
* Address to page conversion * Address to page conversion
* *
* Input Parameters: * Input Parameters:
* addr - Address with of without flash offset (absolute or aligned to page0) * addr - Address with or without flash offset (absolute or aligned to page0)
* *
* Returned Value: * Returned Value:
* Page or negative value on error. The following errors are reported * Page or negative value on error. The following errors are reported
@ -150,17 +154,17 @@ size_t up_progmem_pagesize(size_t page)
ssize_t up_progmem_getpage(size_t addr) ssize_t up_progmem_getpage(size_t addr)
{ {
if (addr >= SAMV7_FLASH_BASE) if (addr >= SAMV7_ALIGNED_FLASH_BASE)
{ {
addr -= SAMV7_FLASH_BASE; addr -= SAMV7_ALIGNED_FLASH_BASE;
} }
if (addr >= SAMV7_FLASH_SIZE) if (addr >= SAMV7_ALIGNED_FLASH_SIZE)
{ {
return -EFAULT; return -EFAULT;
} }
return addr / SAMV7_FLASH_PAGESIZE; return addr >> SAMV7_PAGE_SHIFT;
} }
/**************************************************************************** /****************************************************************************
@ -179,12 +183,12 @@ ssize_t up_progmem_getpage(size_t addr)
size_t up_progmem_getaddress(size_t page) size_t up_progmem_getaddress(size_t page)
{ {
if (page >= SAMV7_FLASH_NPAGES) if (page >= SAMV7_NPAGES)
{ {
return SIZE_MAX; return SAMV7_ALIGNED_FLASH_SIZE;
} }
return page * SAMV7_FLASH_PAGESIZE + SAMV7_FLASH_BASE; return (page << SAMV7_PAGE_SHIFT) + SAMV7_ALIGNED_FLASH_BASE;
} }
/**************************************************************************** /****************************************************************************
@ -213,30 +217,28 @@ ssize_t up_progmem_erasepage(size_t page)
{ {
size_t page_address; size_t page_address;
if (page >= SAMV7_FLASH_NPAGES) if (page >= SAMV7_NPAGES)
{ {
return -EFAULT; return -EFAULT;
} }
/* Get flash ready and begin erasing single page */ /* Erase a single page */
if (!(getreg32(SAMV7_RCC_CR) & RCC_CR_HSION))
{
return -EPERM;
}
sam_flash_unlock(); sam_flash_unlock();
#warning Missing logic #warning Missing logic
/* Invalidate I- and D-Cache in this address range */
#warning Mising logic
/* Verify */ /* Verify */
if (up_progmem_ispageerased(page) == 0) if (up_progmem_ispageerased(page) == 0)
{ {
return up_progmem_pagesize(page); /* success */ return SAMV7_PAGE_SIZE; /* Success */
} }
else else
{ {
return -EIO; /* failure */ return -EIO; /* Failure */
} }
} }
@ -261,18 +263,24 @@ ssize_t up_progmem_erasepage(size_t page)
ssize_t up_progmem_ispageerased(size_t page) ssize_t up_progmem_ispageerased(size_t page)
{ {
size_t addr; size_t addr;
size_t count; size_t bwritten;
size_t bwritten = 0; int count;
if (page >= SAMV7_FLASH_NPAGES) if (page >= SAMV7_NPAGES)
{ {
return -EFAULT; return -EFAULT;
} }
/* Verify */ /* Invalidate D-Cache for this address range */
for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); addr = up_progmem_getaddress(page);
count; count--, addr++) arch_invalidate_dcache(addr, addr + SAMV7_PAGE_SIZE);
/* Verify that the page is erased (i.e., all 0xff) */
for (count = SAMV7_PAGE_SIZE, bwritten = 0;
count > 0;
count--, addr++)
{ {
if (getreg8(addr) != 0xff) if (getreg8(addr) != 0xff)
{ {
@ -295,13 +303,13 @@ ssize_t up_progmem_ispageerased(size_t page)
* Input Parameters: * Input Parameters:
* addr - Address with or without flash offset (absolute or aligned to page0) * addr - Address with or without flash offset (absolute or aligned to page0)
* buf - Pointer to buffer * buf - Pointer to buffer
* count - Number of bytes to write * * count - Number of bytes to write
* *
* Returned Value: * Returned Value:
* Bytes written or negative value on error. The following errors are * Bytes written or negative value on error. The following errors are
* reported (errno is not set!) * reported (errno is not set!)
* *
* EINVAL: if count is not aligned with the flash boundaries (i.e. * EINVAL: If count is not aligned with the flash boundaries (i.e.
* some MCU's require per half-word or even word access) * some MCU's require per half-word or even word access)
* EFAULT: On invalid address * EFAULT: On invalid address
* EIO: On unsuccessful write * EIO: On unsuccessful write
@ -314,42 +322,52 @@ ssize_t up_progmem_ispageerased(size_t page)
ssize_t up_progmem_write(size_t addr, const void *buf, size_t count) ssize_t up_progmem_write(size_t addr, const void *buf, size_t count)
{ {
uint16_t *hword = (uint16_t *)buf; uint8_t *src = (uint8_t *)buf;
size_t written = count; size_t written = count;
/* SAMV7 requires half-word access */ /* SAMV7 requires 128-bit/16-byte aligned access */
if (count & 1) if ((addr & SAMV7_WRITE_ALIGN_MASK) != 0 ||
(count & SAMV7_WRITE_ALIGN_MASK) != 0)
{ {
return -EINVAL; return -EINVAL;
} }
/* Check for valid address range */ /* Check for valid address range */
if (addr >= SAMV7_FLASH_BASE) if (addr >= SAMV7_ALIGNED_FLASH_BASE)
{ {
addr -= SAMV7_FLASH_BASE; /* Convert address to an offset relative to be beginning of the
* writable FLASH region.
*/
addr -= SAMV7_ALIGNED_FLASH_BASE;
} }
if ((addr+count) >= SAMV7_FLASH_SIZE) if ((addr + count) >= SAMV7_ALIGNED_FLASH_SIZE)
{ {
return -EFAULT; return -EFAULT;
} }
/* Get flash ready and begin flashing */ /* Write the data to FLASH */
sam_flash_unlock(); sam_flash_unlock();
#warning Missing logic #warning Missing logic
for (addr += SAMV7_FLASH_BASE; count; count -= 2, hword++, addr += 2) for (addr += SAMV7_ALIGNED_FLASH_BASE;
count;
count -= SAMV7_WRITE_ALIGN, src += SAMV7_WRITE_ALIGN, addr += SAMV7_WRITE_ALIGN)
{ {
/* Write half-word and wait to complete */ /* Write 128-bit/16-bytes block to FLASH and wait to complete */
#warning Mising logic
/* Invalidate I- and D-Caches for this address range */
#warning Mising logic
/* Verify */ /* Verify */
#warning Mising logic
} }
modifyreg32(SAMV7_FLASH_CR, FLASH_CR_PG, 0);
return written; return written;
} }

View File

@ -1569,6 +1569,9 @@ static int qspi_memory(struct qspi_dev_s *dev,
struct qspi_meminfo_s *meminfo) struct qspi_meminfo_s *meminfo)
{ {
struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev; struct sam_qspidev_s *priv = (struct sam_qspidev_s *)dev;
#ifdef CONFIG_SAMV7_QSPI_DMA
bool aligned;
#endif
DEBUGASSERT(priv != NULL && meminfo != NULL); DEBUGASSERT(priv != NULL && meminfo != NULL);
@ -1581,9 +1584,16 @@ static int qspi_memory(struct qspi_dev_s *dev,
qspivdbg(" buffer/length: %p/%d\n", meminfo->buffer, meminfo->buflen); qspivdbg(" buffer/length: %p/%d\n", meminfo->buffer, meminfo->buflen);
#ifdef CONFIG_SAMV7_QSPI_DMA #ifdef CONFIG_SAMV7_QSPI_DMA
/* Check for attempt to do unaligned read */
aligned = IS_ALIGNED((uintptr_t)meminfo->buffer) &&
IS_ALIGNED(meminfo->buflen);
/* Can we perform DMA? Should we perform DMA? */ /* Can we perform DMA? Should we perform DMA? */
if (priv->candma && meminfo->buflen > CONFIG_SAMV7_QSPI_DMATHRESHOLD) if (priv->candma &&
meminfo->buflen > CONFIG_SAMV7_QSPI_DMATHRESHOLD &&
aligned)
{ {
return qspi_memory_dma(priv, meminfo); return qspi_memory_dma(priv, meminfo);
} }