diff --git a/doc/README.arm-caches b/doc/README.arm-caches index cd2b4587c2..f6a52e3e38 100644 --- a/doc/README.arm-caches +++ b/doc/README.arm-caches @@ -40,6 +40,8 @@ Buffer Requirements: - If the buffer is not cache-line aligned invalidation will be restricted to the aligned part. That is, one cache-line at the respective boundary may be left out while doing invalidation. +- A suitable buffer can be alloced on the stack using the + ALLOC_CACHE_ALIGN_BUFFER macro. Cleanup Before Linux: - cleanup_before_linux() should flush the D-cache, invalidate I-cache, and diff --git a/include/common.h b/include/common.h index a1683a20aa..13575272b4 100644 --- a/include/common.h +++ b/include/common.h @@ -843,6 +843,64 @@ int cpu_release(int nr, int argc, char * const argv[]); #include #endif +/* + * The ALLOC_CACHE_ALIGN_BUFFER macro is used to allocate a buffer on the + * stack that meets the minimum architecture alignment requirements for DMA. + * Such a buffer is useful for DMA operations where flushing and invalidating + * the cache before and after a read and/or write operation is required for + * correct operations. + * + * When called the macro creates an array on the stack that is sized such + * that: + * + * 1) The beginning of the array can be advanced enough to be aligned. + * + * 2) The size of the aligned portion of the array is a multiple of the minimum + * architecture alignment required for DMA. + * + * 3) The aligned portion contains enough space for the original number of + * elements requested. + * + * The macro then creates a pointer to the aligned portion of this array and + * assigns to the pointer the address of the first element in the aligned + * portion of the array. + * + * Calling the macro as: + * + * ALLOC_CACHE_ALIGN_BUFFER(uint32_t, buffer, 1024); + * + * Will result in something similar to saying: + * + * uint32_t buffer[1024]; + * + * The following differences exist: + * + * 1) The resulting buffer is guaranteed to be aligned to the value of + * ARCH_DMA_MINALIGN. + * + * 2) The buffer variable created by the macro is a pointer to the specified + * type, and NOT an array of the specified type. This can be very important + * if you want the address of the buffer, which you probably do, to pass it + * to the DMA hardware. The value of &buffer is different in the two cases. + * In the macro case it will be the address of the pointer, not the address + * of the space reserved for the buffer. However, in the second case it + * would be the address of the buffer. So if you are replacing hard coded + * stack buffers with this macro you need to make sure you remove the & from + * the locations where you are taking the address of the buffer. + * + * Note that the size parameter is the number of array elements to allocate, + * not the number of bytes. + * + * This macro can not be used outside of function scope, or for the creation + * of a function scoped static buffer. It can not be used to create a cache + * line aligned global buffer. + */ +#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \ + char __##name[ROUND(size * sizeof(type), ARCH_DMA_MINALIGN) + \ + ARCH_DMA_MINALIGN - 1]; \ + \ + type *name = (type *) ALIGN((uintptr_t)__##name, ARCH_DMA_MINALIGN) + /* Pull in stuff for the build system */ #ifdef DO_DEPS_ONLY # include