This commit adds a new function arch_invalidate_dcache_by_addr(). It takes the same parameters as arch_invalidate_dcache(), but performs invalidation of only the lines in cache that need to be invalidated. This new function could be used as a a direct replacement for arch_invalidate_dcache().

The user of this invalidation are mmcsd_sdio currently.  The mmcsd_sdio driver makes calls for dcache invalidation through the chip specific architecture function SDIO_DMARECVSETUP(). I changed the arch/arm/stm32f7 chips to use arch_invalidate_dcache_by_addr() instead of arch_invalidate_dcache().

This commit includes additional changes to mmcsd_sdio.c.  I created SDIO_DMADELYDINVLDT() (DMA delayed invalidate) to invalidate store-into mode dcaches after the DMA transfer.  I have been using SDIO_DMADELYDINVLDT() for several weeks now and it has fixed the problems that I previously reported regarding non-cache aligned buffer invalidation errors (for my store-through dcache). However, it does not permit use of unaligned DMA buffers for store-into mode dcaches.

SDIO_DMADELYDINVLDT() is a NoOp unless the chip specific Kconfig file selects CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT. I have modified all the stm32f7 chips to select it.
This commit is contained in:
Bob Feretich 2018-11-20 14:03:42 -06:00 committed by Gregory Nutt
parent 5499c884a5
commit c6851201c0
8 changed files with 232 additions and 16 deletions

View File

@ -1,8 +1,9 @@
/****************************************************************************
* arch/arm/src/armv7-m/arch_invalidate_dcache.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2015, 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Bob Feretich <bob.feretich@rafresearch.com>
*
* Some logic in this header file derives from the ARM CMSIS core_cm7.h
* header file which has a compatible 3-clause BSD license:
@ -48,10 +49,6 @@
#ifdef CONFIG_ARMV7M_DCACHE
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
@ -62,7 +59,9 @@
* Description:
* Invalidate the data cache within the specified region; we will be
* performing a DMA operation in this region and we want to purge old data
* in the cache.
* in the cache. Note that this function invalidates all cache ways
* in sets that could be associated with the address range, regardless of
* whether the address range is contained in the cache or not.
*
* Input Parameters:
* start - virtual start address of region
@ -147,4 +146,72 @@ void arch_invalidate_dcache(uintptr_t start, uintptr_t end)
ARM_ISB();
}
/****************************************************************************
* Name: arch_invalidate_dcache_by_addr
*
* Description:
* Invalidate the data cache within the specified region; we will be
* performing a DMA operation in this region and we want to purge old data
* in the cache. Note that this function only invalidates cache sets that
* contain data from this address range.
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is pre-empted.
*
****************************************************************************/
void arch_invalidate_dcache_by_addr(uintptr_t start, uintptr_t end)
{
uint32_t ccsidr;
uint32_t sshift;
uint32_t ssize;
/* Get the characteristics of the D-Cache */
ccsidr = getreg32(NVIC_CCSIDR);
sshift = CCSIDR_LSSHIFT(ccsidr) + 4; /* log2(cache-line-size-in-bytes) */
/* Invalidate the D-Cache containing this range of addresses */
ssize = (1 << sshift);
/* Round down the start address to the nearest cache line boundary.
*
* sshift = 5 : Offset to the beginning of the set field
* (ssize - 1) = 0x007f : Mask of the set field
*/
start &= ~(ssize - 1);
ARM_DSB();
do
{
/* The below store causes the cache to check its directory and
* determine if this address is contained in the cache. If so, it
* invalidate that cache line. Only the cache way containing the
* address is invalidated. If the address is not in the cache, then
* nothing is invalidated.
*/
putreg32(start, NVIC_DCIMVAC);
/* Increment the address by the size of one cache line. */
start += ssize;
}
while (start < end);
ARM_DSB();
ARM_ISB();
}
#endif /* CONFIG_ARMV7M_DCACHE */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* arch/arm/src/armv7-m/cache.h
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Copyright (C) 2015, 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Some logic in this header file derives from the ARM CMSIS core_cm7.h
@ -300,7 +300,9 @@ void arch_disable_dcache(void);
* Description:
* Invalidate the data cache within the specified region; we will be
* performing a DMA operation in this region and we want to purge old data
* in the cache.
* in the cache. Note that this function invalidates all cache ways
* in sets that could be associated with the address range, regardless of
* whether the address range is contained in the cache or not.
*
* Input Parameters:
* start - virtual start address of region
@ -322,6 +324,35 @@ void arch_invalidate_dcache(uintptr_t start, uintptr_t end);
# define arch_invalidate_dcache(s,e)
#endif
/****************************************************************************
* Name: arch_invalidate_dcache_by_addr
*
* Description:
* Invalidate the data cache within the specified region; we will be
* performing a DMA operation in this region and we want to purge old data
* in the cache. Note that this function only invalidates cache sets that
* contain data from this address range.
*
* Input Parameters:
* start - virtual start address of region
* end - virtual end address of region + 1
*
* Returned Value:
* None
*
* Assumptions:
* This operation is not atomic. This function assumes that the caller
* has exclusive access to the address range so that no harm is done if
* the operation is pre-empted.
*
****************************************************************************/
#ifdef CONFIG_ARMV7M_DCACHE
void arch_invalidate_dcache_by_addr(uintptr_t start, uintptr_t end);
#else
# define arch_invalidate_dcache_by_addr(s,e)
#endif
/****************************************************************************
* Name: arch_invalidate_dcache_all
*

View File

@ -240,7 +240,7 @@
#define NVIC_MVFR2_OFFSET 0x0f48 /* Media and VFP Feature Register 2 */
#define NVIC_ICIALLU_OFFSET 0x0f50 /* I-Cache Invalidate All to PoU (Cortex-M7) */
#define NVIC_ICIMVAU_OFFSET 0x0f58 /* I-Cache Invalidate by MVA to PoU (Cortex-M7) */
#define NVIC_DCIMVAU_OFFSET 0x0f5c /* D-Cache Invalidate by MVA to PoC (Cortex-M7) */
#define NVIC_DCIMVAC_OFFSET 0x0f5c /* D-Cache Invalidate by MVA to PoC (Cortex-M7) */
#define NVIC_DCISW_OFFSET 0x0f60 /* D-Cache Invalidate by Set-way (Cortex-M7) */
#define NVIC_DCCMVAU_OFFSET 0x0f64 /* D-Cache Clean by MVA to PoU (Cortex-M7) */
#define NVIC_DCCMVAC_OFFSET 0x0f68 /* D-Cache Clean by MVA to PoC (Cortex-M7) */
@ -429,6 +429,7 @@
#define NVIC_ICIALLU (ARMV7M_NVIC_BASE + NVIC_ICIALLU_OFFSET)
#define NVIC_ICIMVAU (ARMV7M_NVIC_BASE + NVIC_ICIMVAU_OFFSET)
#define NVIC_DCIMVAU (ARMV7M_NVIC_BASE + NVIC_DCIMVAU_OFFSET)
#define NVIC_DCIMVAC (ARMV7M_NVIC_BASE + NVIC_DCIMVAC_OFFSET)
#define NVIC_DCISW (ARMV7M_NVIC_BASE + NVIC_DCISW_OFFSET)
#define NVIC_DCCMVAU (ARMV7M_NVIC_BASE + NVIC_DCCMVAU_OFFSET)
#define NVIC_DCCMVAC (ARMV7M_NVIC_BASE + NVIC_DCCMVAC_OFFSET)

View File

@ -655,6 +655,7 @@ config STM32F7_STM32F722XX
default n
select STM32F7_STM32F72XX
select ARCH_HAVE_FPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -671,6 +672,7 @@ config STM32F7_STM32F723XX
default n
select STM32F7_STM32F72XX
select ARCH_HAVE_FPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -687,6 +689,7 @@ config STM32F7_STM32F745XX
default n
select STM32F7_STM32F74XX
select ARCH_HAVE_FPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -706,6 +709,7 @@ config STM32F7_STM32F746XX
default n
select STM32F7_STM32F74XX
select ARCH_HAVE_FPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -748,6 +752,7 @@ config STM32F7_STM32F765XX
select STM32F7_STM32F76XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -797,6 +802,7 @@ config STM32F7_STM32F768XX # Revisit Wehn parts released
select STM32F7_STM32F76XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -823,6 +829,7 @@ config STM32F7_STM32F768AX # Revisit When parts released
select STM32F7_STM32F76XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -848,6 +855,7 @@ config STM32F7_STM32F769XX
select STM32F7_STM32F76XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -874,6 +882,7 @@ config STM32F7_STM32F769AX # Revisit When parts released
select STM32F7_STM32F76XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -899,6 +908,7 @@ config STM32F7_STM32F777XX
select STM32F7_STM32F77XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -927,6 +937,7 @@ config STM32F7_STM32F778XX # Revisit when parts released
select STM32F7_STM32F77XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -955,6 +966,7 @@ config STM32F7_STM32F778AX
select STM32F7_STM32F77XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -982,6 +994,7 @@ config STM32F7_STM32F779XX
select STM32F7_STM32F77XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM
@ -1010,6 +1023,7 @@ config STM32F7_STM32F779AX
select STM32F7_STM32F77XX
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select ARCH_HAVE_SDIO_DELAYED_INVLDT
select ARMV7M_HAVE_ICACHE
select ARMV7M_HAVE_DCACHE
select ARMV7M_HAVE_ITCM

View File

@ -4,6 +4,7 @@
* Copyright (C) 2009, 2011-2018 Gregory Nutt. All rights reserved.
* Authors: Gregory Nutt <gnutt@nuttx.org>
* David Sidrane <david_s5@nscdg.com>
* Bob Feretich <bob.feretich@rafresearch.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -63,6 +64,7 @@
#include "chip.h"
#include "up_arch.h"
#include "stm32_dtcm.h"
#include "stm32_dma.h"
#include "stm32_gpio.h"
#include "stm32_sdmmc.h"
@ -563,7 +565,11 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev,
FAR uint8_t *buffer, size_t buflen);
static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen);
#ifdef CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT
static int stm32_dmadelydinvldt(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen);
#endif
#endif /* CONFIG_STM32F7_SDMMC_DMA */
/* Initialization/uninitialization/reset ************************************/
@ -612,14 +618,17 @@ struct stm32_dev_s g_sdmmcdev1 =
#endif
.dmarecvsetup = stm32_dmarecvsetup,
.dmasendsetup = stm32_dmasendsetup,
#else
#ifdef CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT
.dmadelydinvldt = stm32_dmadelydinvldt,
#endif
#lse
#ifdef CONFIG_ARCH_HAVE_SDIO_PREFLIGHT
.dmapreflight = NULL,
#endif
.dmarecvsetup = stm32_recvsetup,
.dmasendsetup = stm32_sendsetup,
#endif
#endif
#endif /* CONFIG_STM32F7_SDMMC_DMA */
#endif /* CONFIG_SDIO_DMA*/
},
.base = STM32_SDMMC1_BASE,
.nirq = STM32_IRQ_SDMMC1,
@ -687,6 +696,9 @@ struct stm32_dev_s g_sdmmcdev2 =
#endif
.dmarecvsetup = stm32_dmarecvsetup,
.dmasendsetup = stm32_dmasendsetup,
#ifdef CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT
.dmadelydinvldt = stm32_dmadelydinvldt,
#endif
#endif
},
.base = STM32_SDMMC2_BASE,
@ -2524,7 +2536,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
else if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
(cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
(cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
(cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
(cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE)
{
mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
@ -3055,7 +3067,12 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Force RAM reread */
arch_invalidate_dcache((uintptr_t)buffer,(uintptr_t)buffer + buflen);
if ((uintptr_t)buffer < DTCM_START || (uintptr_t)buffer + buflen > DTCM_END)
{
#if !defined(CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT)
arch_invalidate_dcache_by_addr((uintptr_t)buffer,(uintptr_t)buffer + buflen);
#endif
}
/* Start the DMA */
@ -3121,9 +3138,16 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
stm32_sampleinit();
stm32_sample(priv, SAMPLENDX_BEFORE_SETUP);
/* Flush cache to physical memory */
/* Flush cache to physical memory when not in DTCM memory */
arch_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
if ((uintptr_t)buffer < DTCM_START || (uintptr_t)buffer + buflen > DTCM_END)
{
#ifdef CONFIG_ARMV7M_DCACHE_WRITETHROUGH
arch_invalidate_dcache_by_addr((uintptr_t)buffer, (uintptr_t)buffer + buflen);
#else
arch_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
#endif
}
/* Save the source buffer information for use by the interrupt handler */
@ -3158,6 +3182,42 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
}
#endif
/****************************************************************************
* Name: stm32_dmadelydinvldt
*
* Description:
* Delayed D-cache invalidation.
* This function should be called after receive DMA completion to perform
* D-cache invalidation. This eliminates the need for cache aligned DMA
* buffers when the D-cache is in store-through mode.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
* buffer - The memory to DMA into
* buflen - The size of the DMA transfer in bytes
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
#ifdef CONFIG_STM32F7_SDMMC_DMA
static int stm32_dmadelydinvldt(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen)
{
/* Invaliate cache to physical memory when not in DTCM memory. */
if ((uintptr_t)buffer < DTCM_START ||
(uintptr_t)buffer + buflen > DTCM_END)
{
arch_invalidate_dcache_by_addr((uintptr_t)buffer,
(uintptr_t)buffer + buflen);
}
return OK;
}
#endif
/****************************************************************************
* Initialization/uninitialization/reset
****************************************************************************/

View File

@ -237,6 +237,10 @@ config ARCH_HAVE_SDIO_PREFLIGHT
bool
default n
config ARCH_HAVE_SDIO_DELAYED_INVLDT
bool
default n
menuconfig MMCSD
bool "MMC/SD Driver Support"
default n

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2009-2013, 2016-2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Bob Feretich <bob.fereich@rafresearch.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -1408,6 +1409,9 @@ static ssize_t mmcsd_readsingle(FAR struct mmcsd_state_s *priv,
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
MMCSD_BLOCK_RDATADELAY);
#ifdef CONFIG_SDIO_DMA
SDIO_DMADELYDINVLDT(priv->dev, buffer, priv->blocksize);
#endif
if (ret != OK)
{
ferr("ERROR: CMD17 transfer failed: %d\n", ret);
@ -1550,6 +1554,10 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_state_s *priv,
/* Send STOP_TRANSMISSION */
ret = mmcsd_stoptransmission(priv);
#ifdef CONFIG_SDIO_DMA
SDIO_DMADELYDINVLDT(priv->dev, buffer, priv->blocksize * nblocks);
#endif
if (ret != OK)
{
ferr("ERROR: mmcsd_stoptransmission failed: %d\n", ret);
@ -1794,6 +1802,8 @@ static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
*
* Description:
* Write multiple, contiguous blocks of data to the physical device.
* This function expects that the data to be written is contained in
* one large buffer that is pointed to by buffer.
*
****************************************************************************/

View File

@ -814,6 +814,31 @@
# define SDIO_DMARECVSETUP(dev,buffer,len) (-ENOSYS)
#endif
/****************************************************************************
* Name: SDIO_DMADELYDINVLDT
*
* Description:
* Delayed D-cache invalidation.
* This function should be called after receive DMA completion to perform
* D-cache invalidation. This eliminates the need for cache aligned DMA
* buffers when the D-cache is in store-through mode.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
* buffer - The memory to DMA from
* buflen - The size of the DMA transfer in bytes
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT)
# define SDIO_DMADELYDINVLDT(dev,buffer,len) ((dev)->dmadelydinvldt(dev,buffer,len))
#else
# define SDIO_DMADELYDINVLDT(dev,buffer,len) (OK)
#endif
/****************************************************************************
* Name: SDIO_DMASENDSETUP
*
@ -949,7 +974,11 @@ struct sdio_dev_s
size_t buflen);
int (*dmasendsetup)(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen);
#ifdef CONFIG_ARCH_HAVE_SDIO_DELAYED_INVLDT
int (*dmadelydinvldt)(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen);
#endif
#endif /* CONFIG_SDIO_DMA */
};
/****************************************************************************