From ef226fe49fe1927831f5cfb2dca9c58fc83db6e5 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 20 Nov 2013 15:24:14 -0600 Subject: [PATCH] ARM9: More cache control functions --- arch/arm/src/arm/cache.h | 78 +++++++++ arch/arm/src/arm/up_cache.S | 273 +++++++++++++++++++++++++++--- arch/arm/src/lpc31xx/lpc31_ehci.c | 49 +++--- 3 files changed, 352 insertions(+), 48 deletions(-) create mode 100644 arch/arm/src/arm/cache.h diff --git a/arch/arm/src/arm/cache.h b/arch/arm/src/arm/cache.h new file mode 100644 index 0000000000..4606a18f62 --- /dev/null +++ b/arch/arm/src/arm/cache.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * arch/arm/src/arm/cache.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Several of these cache operations come from Atmel sample code with + * modifications for better integration with NuttX. The Atmel sample code + * has a BSD compatibile license that requires this copyright notice: + * + * Copyright (c) 2008, Atmel Corporation + * + * [Actually, I think that all of the Atmel functions are commented out now] + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the names NuttX nor Atmel nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_ARM_CACHE_H +#define __ARCH_ARM_SRC_ARM_CACHE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Defintiions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void cp15_flush_idcache(uint32_t start, uint32_t end); +#if 0 /* Not used */ +void cp15_invalidate_idcache(void); +void cp15_invalidate_icache(void); +#endif +void cp15_invalidate_dcache(uint32_t start, uint32_t end); +#if 0 /* Not used */ +void cp15_invalidate_dcache_all(void); +void cp15_prefetch_icacheline(unsigned int value); +void cp15_testcleaninvalidate_dcache(void); +void cp15_drain_writebuffer(void); + +unsigned int cp15_read_dcachelockdown(void); +void cp15_write_dcachelockdown(unsigned int value); +unsigned int cp15_read_icachelockdown(void); +void cp15_write_icachelockdown(unsigned int value); +#endif + +#endif // #ifndef __ARCH_ARM_SRC_ARM_CACHE_H + diff --git a/arch/arm/src/arm/up_cache.S b/arch/arm/src/arm/up_cache.S index 1d459e6930..eacd8cb57e 100644 --- a/arch/arm/src/arm/up_cache.S +++ b/arch/arm/src/arm/up_cache.S @@ -1,9 +1,17 @@ /**************************************************************************** * arch/arm/src/arm/up_cache.S * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * + * Several of these cache operations come from Atmel sample code with + * modifications for better integration with NuttX. The Atmel sample code + * has a BSD compatibile license that requires this copyright notice: + * + * Copyright (c) 2008, Atmel Corporation + * + * [Actually, I think that all of the Atmel functions are commented out now] + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -14,8 +22,8 @@ * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software + * 3. Neither the names NuttX nor Atmel nor the names of its contributors + * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -33,42 +41,259 @@ * ****************************************************************************/ + .file "up_cp15.S" + /**************************************************************************** * Included Files ****************************************************************************/ #include -#include "up_internal.h" -#include "up_arch.h" /**************************************************************************** - * Definitions + * Pre-processor Definitions ****************************************************************************/ #define CACHE_DLINESIZE 32 /**************************************************************************** - * Assembly Macros + * Cache Operations ****************************************************************************/ -/**************************************************************************** - * Name: up_flushicache - ****************************************************************************/ + .text + +/* Control functions caches and the write buffer c7 + * Register c7 controls the caches and the write buffer. The function of each cache + * operation is selected by the Opcode_2 and CRm fields in the MCR instruction used to + * write to CP15 c7. Writing other Opcode_2 or CRm values is Unpredictable. + * Reading from CP15 c7 is Unpredictable, with the exception of the two test and clean + * operations (see Table 2-18 on page 2-21 and Test and clean operations on page 2-23). + * You can use the following instruction to write to c7: + * MCR p15, , , , , + * + * Invalidate Icache and Dcache MCR p15, 0, , c7, c7, 0 + * Invalidate Icache MCR p15, 0, , c7, c5, 0 + * Invalidate Icache single entry (MVA) MVA MCR p15, 0, , c7, c5, 1 + * Invalidate Icache single entry (Set/Way) Set/Way MCR p15, 0, , c7, c5, 2 + * Prefetch Icache line (MVA) MVA MCR p15, 0, , c7, c13, 1 + * Invalidate Dcache MCR p15, 0, , c7, c6, 0 + * Invalidate Dcache single entry (MVA) MVA MCR p15, 0, , c7, c6, 1 + * Invalidate Dcache single entry (Set/Way) Set/Way MCR p15, 0, , c7, c6, 2 + * Clean Dcache single entry (MVA) MVA MCR p15, 0, , c7, c10, 1 + * Clean Dcache single entry (Set/Way) Set/Way MCR p15, 0, , c7, c10, 2 + * Test and clean Dcache - MRC p15, 0, , c7, c10, 3 + * Clean and invalidate Dcache entry (MVA) MVA MCR p15, 0, , c7, c14, 1 + * Clean and invalidate Dcache entry (Set/Way) Set/Way MCR p15, 0, , c7, c14, 2 + * Test, clean, and invalidate Dcache - MRC p15, 0, , c7, c14, 3 + * Drain write buffer SBZ MCR p15, 0, , c7, c10, 4 + * Wait for interrupt SBZ MCR p15, 0, , c7, c0, 4 + */ /* Esure coherency between the Icache and the Dcache in the region described - * by r0=start and r1=end. + * by r0=start and r1=end. Cleans the corresponding D-cache lines and invalidates + * the corresponding I-Cache lines. */ - .globl up_flushicache - .type up_flushicache,%function -up_flushicache: - bic r0, r0, #CACHE_DLINESIZE - 1 -1: mcr p15, 0, r0, c7, c10, 1 /* Clean D entry */ - mcr p15, 0, r0, c7, c5, 1 /* Invalidate I entry */ - add r0, r0, #CACHE_DLINESIZE - cmp r0, r1 - blo 1b - mcr p15, 0, r0, c7, c10, 4 /* Drain WB */ - mov pc, lr - .size up_flushicache, .-up_flushicache - .end + .globl cp15_flush_idcache + .type cp15_flush_idcache, function + +cp15_flush_idcache: + bic r0, r0, #CACHE_DLINESIZE - 1 +1: mcr p15, 0, r0, c7, c10, 1 /* Clean D entry */ + mcr p15, 0, r0, c7, c5, 1 /* Invalidate I entry */ + add r0, r0, #CACHE_DLINESIZE + cmp r0, r1 + blo 1b + mcr p15, 0, r0, c7, c10, 4 /* Drain WB */ + mov pc, lr + .size cp15_flush_idcache, .-cp15_flush_idcache + +#if 0 /* Not used */ +/* Invalidate all of Icache and Dcache */ + + .globl cp15_invalidate_idcache + .type cp15_invalidate_idcache, function + +cp15_invalidate_idcache: + mov r0, #0 + mcr p15, 0, r0, c7, c7, 0 + nop + nop + nop + nop + nop + nop + nop + nop + bx lr + .size cp15_invalidate_idcache, . - cp15_invalidate_idcache + +/* Invalidate all of Icache */ + + .globl cp15_invalidate_icache + .type cp15_invalidate_icache, function + +cp15_invalidate_icache: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + nop + nop + nop + nop + nop + nop + nop + nop + bx lr + .size cp15_invalidate_icache, . - cp15_invalidate_icache +#endif /* Not used */ + +/* Invalidate D-Cache in the region described by r0=start and r1=end. */ + +cp15_invalidate_dcache: + bic r0, r0, #CACHE_DLINESIZE - 1 + mcr p15, 0, r0, c7, c6, 1 /* Invalidate D entry */ + add r0, r0, #CACHE_DLINESIZE + cmp r0, r1 + blo 1b + mov pc, lr + .size cp15_flush_idcache, .-cp15_flush_idcache + +#if 0 /* Not used */ +/* Invalidate Dcache */ + + .globl cp15_invalidate_dcache + .type cp15_invalidate_dcache, function + +cp15_invalidate_dcache_all: + mov r0, #0 + mcr p15, 0, r0, c7, c6, 0 + nop + nop + nop + nop + nop + nop + nop + nop + bx lr + .size cp15_invalidate_dcache, . - cp15_invalidate_dcache + +/* CP15 Prefetch Icache line c7 + * Performs an Icache lookup of the specified modified virtual address. + * If the cache misses, and the region is cacheable, a linefill is performed. + * Prefetch Icache line (MVA): MCR p15, 0, , c7, c13, 1 + */ + + .globl cp15_prefetch_icacheline + .type cp15_prefetch_icacheline, function + +cp15_prefetch_icacheline: + mcr p15, 0, r0, c7, c13, 1 + bx lr + .size cp15_prefetch_icacheline, . - cp15_prefetch_icacheline + +/* CP15 Test, clean, and invalidate Dcache c7 + * As for test and clean, except that when the entire cache has + * been tested and cleaned, it is invalidated. + */ + + .globl cp15_testcleaninvalidate_dcache + .type cp15_testcleaninvalidate_dcache, function + +cp15_testcleaninvalidate_dcache: + mrc p15, 0, r0, c7, c14, 3 + bne cp15_testcleaninvalidate_dcache + bx lr + .size cp15_testcleaninvalidate_dcache, . - cp15_testcleaninvalidate_dcache + +/* CP15 Drain write buffer c7 + * This instruction acts as an explicit memory barrier. It drains + * the contents of the write buffers of all memory stores + * occurring in program order before this instruction is + * completed. No instructions occurring in program order + * after this instruction are executed until it completes. This + * can be used when timing of specific stores to the level two + * memory system has to be controlled (for example, when a + * store to an interrupt acknowledge location has to complete + * before interrupts are enabled). + */ + + .globl cp15_drain_writebuffer + .type cp15_drain_writebuffer, function + +cp15_drain_writebuffer: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 + bx lr + .size cp15_drain_writebuffer, . - cp15_drain_writebuffer + +/**************************************************************************** + * Cache Lockdown + ****************************************************************************/ + +/* Cache Lockdown Register c9 + * The Cache Lockdown Register uses a cache-way-based locking scheme (Format C) that + * enables you to control each cache way independently. + * These registers enable you to control which cache ways of the four-way cache are used + * for the allocation on a linefill. When the registers are defined, subsequent linefills are + * only placed in the specified target cache way. This gives you some control over the + * cache pollution caused by particular applications, and provides a traditional lockdown + * operation for locking critical code into the cache. + * + * Read Dcache Lockdown Register MRC p15,0,,c9,c0,0 + * Write Dcache Lockdown Register MCR p15,0,,c9,c0,0 + * Read Icache Lockdown Register MRC p15,0,,c9,c0,1 + * Write Icache Lockdown Register MCR p15,0,,c9,c0,1 + */ + + .globl cp15_read_dcachelockdown + .type cp15_read_dcachelockdown, function + +cp15_read_dcachelockdown: + mov r0, #0 + mrc p15, 0, r0, c9, c0, 0 + bx lr + .size cp15_read_dcachelockdown, . - cp15_read_dcachelockdown + + .globl cp15_write_dcachelockdown + .type cp15_write_dcachelockdown, function + +cp15_write_dcachelockdown: + mcr p15, 0, r0, c9, c0, 0 + nop + nop + nop + nop + nop + nop + nop + nop + bx lr + .size cp15_write_dcachelockdown, . - cp15_write_dcachelockdown + + .globl cp15_read_icachelockdown + .type cp15_read_icachelockdown, function + +cp15_read_icachelockdown: + mov r0, #0 + mrc p15, 0, r0, c9, c0, 1 + bx lr + .size cp15_read_icachelockdown, . - cp15_read_icachelockdown + + .globl cp15_write_icachelockdown + .type cp15_write_icachelockdown, function + +cp15_write_icachelockdown: + mcr p15, 0, r0, c9, c0, 1 + nop + nop + nop + nop + nop + nop + nop + nop + bx lr + .size cp15_write_icachelockdown, . - cp15_write_icachelockdown +#endif /* Not used */ + .end diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c index 224512935e..3f7dc0cb11 100755 --- a/arch/arm/src/lpc31xx/lpc31_ehci.c +++ b/arch/arm/src/lpc31xx/lpc31_ehci.c @@ -56,6 +56,7 @@ #include #include "up_arch.h" +#include "cache.h" #include "lpc31_internal.h" #include "lpc31_cgudrvr.h" @@ -1311,7 +1312,7 @@ static int lpc31_qtd_invalidate(struct lpc31_qtd_s *qtd, uint32_t **bp, void *ar * memory over the specified address range. */ - up_invalidate_dcache((uintptr_t)&qtd->hw, + cp15_invalidate_dcache((uintptr_t)&qtd->hw, (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); return OK; } @@ -1330,7 +1331,7 @@ static int lpc31_qh_invalidate(struct lpc31_qh_s *qh) { /* Invalidate the QH first so that we reload the qTD list head */ - up_invalidate_dcache((uintptr_t)&qh->hw, + cp15_invalidate_dcache((uintptr_t)&qh->hw, (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); /* Then invalidate all of the qTD entries in the queue */ @@ -1355,9 +1356,9 @@ static int lpc31_qtd_flush(struct lpc31_qtd_s *qtd, uint32_t **bp, void *arg) * to force re-loading of the data from memory when next accessed. */ - up_flush_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); - up_invalidate_dcache((uintptr_t)&qtd->hw, + cp15_flush_idcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + cp15_invalidate_dcache((uintptr_t)&qtd->hw, (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); return OK; @@ -1378,9 +1379,9 @@ static int lpc31_qh_flush(struct lpc31_qh_s *qh) * reloaded from D-Cache. */ - up_flush_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); - up_invalidate_dcache((uintptr_t)&qh->hw, + cp15_flush_idcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + cp15_invalidate_dcache((uintptr_t)&qh->hw, (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); /* Then flush all of the qTD entries in the queue */ @@ -1590,8 +1591,8 @@ static void lpc31_qh_enqueue(struct lpc31_qh_s *qhead, struct lpc31_qh_s *qh) physaddr = (uintptr_t)lpc31_physramaddr((uintptr_t)qh); qhead->hw.hlp = lpc31_swap32(physaddr | QH_HLP_TYP_QH); - up_flush_dcache((uintptr_t)&qhead->hw, - (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s)); + cp15_flush_idcache((uintptr_t)&qhead->hw, + (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s)); } /******************************************************************************* @@ -1727,8 +1728,8 @@ static int lpc31_qtd_addbpl(struct lpc31_qtd_s *qtd, const void *buffer, size_t * will be accessed for an OUT DMA. */ - up_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); - up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + cp15_flush_idcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); /* Loop, adding the aligned physical addresses of the buffer to the buffer page * list. Only the first entry need not be aligned (because only the first @@ -2275,7 +2276,7 @@ static ssize_t lpc31_async_transfer(struct lpc31_rhport_s *rhport, * invalid in this memory region. */ - up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); } #endif @@ -2510,7 +2511,7 @@ static int lpc31_qtd_ioccheck(struct lpc31_qtd_s *qtd, uint32_t **bp, void *arg) /* Make sure we reload the QH from memory */ - up_invalidate_dcache((uintptr_t)&qtd->hw, + cp15_invalidate_dcache((uintptr_t)&qtd->hw, (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); lpc31_qtd_print(qtd); @@ -2561,7 +2562,7 @@ static int lpc31_qh_ioccheck(struct lpc31_qh_s *qh, uint32_t **bp, void *arg) /* Make sure we reload the QH from memory */ - up_invalidate_dcache((uintptr_t)&qh->hw, + cp15_invalidate_dcache((uintptr_t)&qh->hw, (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); lpc31_qh_print(qh); @@ -2615,7 +2616,7 @@ static int lpc31_qh_ioccheck(struct lpc31_qh_s *qh, uint32_t **bp, void *arg) */ **bp = qh->hw.hlp; - up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + cp15_flush_idcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); /* Check for errors, update the data toggle */ @@ -2715,7 +2716,7 @@ static inline void lpc31_ioc_bottomhalf(void) /* Check the Asynchronous Queue */ /* Make sure that the head of the asynchronous queue is invalidated */ - up_invalidate_dcache((uintptr_t)&g_asynchead.hw, + cp15_invalidate_dcache((uintptr_t)&g_asynchead.hw, (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); /* Set the back pointer to the forward qTD pointer of the asynchronous @@ -2741,7 +2742,7 @@ static inline void lpc31_ioc_bottomhalf(void) /* Check the Interrupt Queue */ /* Make sure that the head of the interrupt queue is invalidated */ - up_invalidate_dcache((uintptr_t)&g_intrhead.hw, + cp15_invalidate_dcache((uintptr_t)&g_intrhead.hw, (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); /* Set the back pointer to the forward qTD pointer of the asynchronous @@ -4393,8 +4394,8 @@ FAR struct usbhost_connection_s *lpc31_ehci_initialize(int controller) g_asynchead.hw.overlay.token = lpc31_swap32(QH_TOKEN_HALTED); g_asynchead.fqp = lpc31_swap32(QTD_NQP_T); - up_flush_dcache((uintptr_t)&g_asynchead.hw, - (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); + cp15_flush_idcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); /* Set the Current Asynchronous List Address. */ @@ -4424,10 +4425,10 @@ FAR struct usbhost_connection_s *lpc31_ehci_initialize(int controller) /* Set the Periodic Frame List Base Address. */ - up_flush_dcache((uintptr_t)&g_intrhead.hw, - (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); - up_flush_dcache((uintptr_t)g_framelist, - (uintptr_t)g_framelist + FRAME_LIST_SIZE * sizeof(uint32_t)); + cp15_flush_idcache((uintptr_t)&g_intrhead.hw, + (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); + cp15_flush_idcache((uintptr_t)g_framelist, + (uintptr_t)g_framelist + FRAME_LIST_SIZE * sizeof(uint32_t)); physaddr = lpc31_physramaddr((uintptr_t)g_framelist); lpc31_putreg(lpc31_swap32(physaddr), &HCOR->periodiclistbase);