From edbfb3a43b6c4e1dd28f6d00a59896cae198f68b Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Fri, 27 Apr 2018 08:15:57 +0200 Subject: cortex-m: add D-cache support Add support to enable the architectural D-cache on ARMv7-M CPU supporting it. Update the MPU code in order to be able to declare an 'uncached' RAM region (e.g. to store the DMA buffer). Signed-off-by: Vincent Palatin BRANCH=poppy BUG=b:78535052, b:75068419 TEST=with the following CL, on ZerbleBarn, boot and capture a finger image. Change-Id: I275445e7c0b558cedc3e7d6fc6840ff9b4b76285 Reviewed-on: https://chromium-review.googlesource.com/1032776 Commit-Ready: Vincent Palatin Tested-by: Vincent Palatin Reviewed-by: Nicolas Boichat --- chip/stm32/system.c | 4 --- core/cortex-m/build.mk | 1 + core/cortex-m/cache.S | 76 +++++++++++++++++++++++++++++++++++++++++++++ core/cortex-m/cpu.c | 17 +++++++--- core/cortex-m/cpu.h | 9 ++++-- core/cortex-m/include/mpu.h | 10 ++++++ core/cortex-m/mpu.c | 43 ++++++++++++++++++++++--- include/config.h | 8 +++++ include/link_defs.h | 5 +++ 9 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 core/cortex-m/cache.S diff --git a/chip/stm32/system.c b/chip/stm32/system.c index b77f0e8457..ea4309c965 100644 --- a/chip/stm32/system.c +++ b/chip/stm32/system.c @@ -205,10 +205,6 @@ void chip_pre_init(void) uint32_t apb1fz_reg = 0; uint32_t apb2fz_reg = 0; -#ifdef CONFIG_ARMV7M_CACHE - cpu_enable_icache(); -#endif - #if defined(CHIP_FAMILY_STM32F0) apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 | STM32_RCC_PB1_TIM6 | diff --git a/core/cortex-m/build.mk b/core/cortex-m/build.mk index 5d035a43cd..59114b20a8 100644 --- a/core/cortex-m/build.mk +++ b/core/cortex-m/build.mk @@ -23,6 +23,7 @@ LDFLAGS_EXTRA+=-flto endif core-y=cpu.o init.o ldivmod.o llsr.o uldivmod.o +core-$(CONFIG_ARMV7M_CACHE)+=cache.o core-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic.o core-$(CONFIG_COMMON_RUNTIME)+=switch.o task.o core-$(CONFIG_WATCHDOG)+=watchdog.o diff --git a/core/cortex-m/cache.S b/core/cortex-m/cache.S new file mode 100644 index 0000000000..0a3d3bb67d --- /dev/null +++ b/core/cortex-m/cache.S @@ -0,0 +1,76 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * ARMv7-M architectural caches maintenance operations. + */ + +.syntax unified +.text +.thumb + +/* System Control Block: cache registers */ +#define SCB_CCSIDR 0xe000ed80 +#define SCB_CCSELR 0xe000ed84 +#define SCB_DCISW 0xe000ef60 +#define SCB_DCCISW 0xe000ef74 + +.macro dcache_set_way_op name register +@ +@ Perform an operation on all D-cache sets/ways. +@ +@ Note: implemented in assembly to guarantee that we are not touching the +@ D-cache in the middle of the loop. +@ +.thumb_func +.section .text.\name +.global \name +\name: + /* Select Level-1 Data cache (for operations on CCSIDR). */ + ldr r1, =SCB_CCSELR + movs r0, #0 + ldr r2, =SCB_CCSIDR + str r0, [r1] /* set CCSELR = 0 */ + + /* Ensure the CCSELR write is effective before reading CCSIDR. */ + dsb + /* CCSIDR contains the cache geometry. */ + ldr r3, [r2] /* [27:13] Number of sets -1 [12:3] Number of ways -1 */ + + /* register used to do the set/way cache operation. */ + ldr r0, =\register + /* r2 is the number of cache 'sets' - 1 */ + ubfx r2, r3, #13, #15 + /* r12 is the number of cache 'ways' - 1 */ + ubfx r12, r3, #3, #10 + +1: + mov r1, r12 /* reset way index */ +2: + /* + * Build address Set/Way operation e.g DC(C)ISW + * [31:30] way index [13:5] set index + */ + lsls r3, r2, #5 /* set index */ + /* TODO(crbug.com/848704) remove cache geometry assumptions */ + orr r3, r3, r1, lsl #30 /* way index */ + /* Perform operation (e.g invalidate) on a D-cache line */ + str r3, [r0] + /* go to previous way */ + subs r1, #1 + bcs 2b + /* go to previous set */ + subs r2, #1 + bcs 1b + + /* Ensure everything has propagated and return. */ + dsb + isb + bx lr +.endm + +/* D-cache Invalidate by set-way */ +dcache_set_way_op cpu_invalidate_dcache SCB_DCISW + +/* D-cache Clean and Invalidate by set-way, to Point of Coherency */ +dcache_set_way_op cpu_clean_invalidate_dcache SCB_DCCISW diff --git a/core/cortex-m/cpu.c b/core/cortex-m/cpu.c index ea6cf5c4a5..4ed61f44e0 100644 --- a/core/cortex-m/cpu.c +++ b/core/cortex-m/cpu.c @@ -30,7 +30,7 @@ static void cpu_invalidate_icache(void) asm volatile("dsb; isb"); } -void cpu_enable_icache(void) +void cpu_enable_caches(void) { /* Check whether the I-cache is already enabled */ if (!(CPU_NVIC_CCR & CPU_NVIC_CCR_ICACHE)) { @@ -40,16 +40,25 @@ void cpu_enable_icache(void) CPU_NVIC_CCR |= CPU_NVIC_CCR_ICACHE; asm volatile("dsb; isb"); } + /* Check whether the D-cache is already enabled */ + if (!(CPU_NVIC_CCR & CPU_NVIC_CCR_DCACHE)) { + /* Invalidate the D-cache first */ + cpu_invalidate_dcache(); + /* Turn on the caching */ + CPU_NVIC_CCR |= CPU_NVIC_CCR_DCACHE; + asm volatile("dsb; isb"); + } } static void cpu_sysjump_cache(void) { /* - * Disable the I-cache - * so we will invalidate it after the sysjump if needed + * Disable the I-cache and the D-cache + * The I-cache will be invalidated after the sysjump if needed * (e.g after a flash update). */ - CPU_NVIC_CCR &= ~CPU_NVIC_CCR_ICACHE; + cpu_clean_invalidate_dcache(); + CPU_NVIC_CCR &= ~(CPU_NVIC_CCR_ICACHE | CPU_NVIC_CCR_DCACHE); asm volatile("dsb; isb"); } DECLARE_HOOK(HOOK_SYSJUMP, cpu_sysjump_cache, HOOK_PRIO_LAST); diff --git a/core/cortex-m/cpu.h b/core/cortex-m/cpu.h index f4400a0444..a6029e2e7e 100644 --- a/core/cortex-m/cpu.h +++ b/core/cortex-m/cpu.h @@ -60,10 +60,15 @@ enum { #define CPU_SCB_CCSELR CPUREG(0xe000ed84) #define CPU_SCB_ICIALLU CPUREG(0xe000ef50) #define CPU_SCB_DCISW CPUREG(0xe000ef60) +#define CPU_SCB_DCCISW CPUREG(0xe000ef74) /* Set up the cpu to detect faults */ void cpu_init(void); -/* Enable the CPU instruction cache if it is not already enabled */ -void cpu_enable_icache(void); +/* Enable the CPU I-cache and D-cache if they are not already enabled */ +void cpu_enable_caches(void); +/* Invalidate the D-cache */ +void cpu_invalidate_dcache(void); +/* Clean and Invalidate the D-cache to the Point of Coherency */ +void cpu_clean_invalidate_dcache(void); #endif /* __CROS_EC_CPU_H */ diff --git a/core/cortex-m/include/mpu.h b/core/cortex-m/include/mpu.h index bb5eaa4c7e..049f32f4f3 100644 --- a/core/cortex-m/include/mpu.h +++ b/core/cortex-m/include/mpu.h @@ -9,6 +9,7 @@ #define __CROS_EC_MPU_H #include "common.h" +#include "config.h" /* chips might override MPU attribute settings */ /* * Region assignment. 7 as the highest, a higher index has a higher priority. @@ -28,6 +29,8 @@ enum mpu_region { REGION_STORAGE2 = 5, /* Second region for unaligned size */ REGION_DATA_RAM_TEXT = 6, /* Exempt region of data RAM */ REGION_CHIP_RESERVED = 7, /* Reserved for use in chip/ */ + /* only for chips with MPU supporting 16 regions */ + REGION_UNCACHED_RAM = 8, /* For uncached data RAM */ }; #define MPU_TYPE REG32(0xe000ed90) @@ -37,6 +40,9 @@ enum mpu_region { #define MPU_SIZE REG16(0xe000eda0) #define MPU_ATTR REG16(0xe000eda2) +#define MPU_TYPE_UNIFIED_MASK 0x00FF0001 +#define MPU_TYPE_REG_COUNT(t) (((t) >> 8) & 0xFF) + #define MPU_CTRL_PRIVDEFEN (1 << 2) #define MPU_CTRL_HFNMIENA (1 << 1) #define MPU_CTRL_ENABLE (1 << 0) @@ -55,8 +61,12 @@ enum mpu_region { /* Suggested value for TEX S/C/B bit. See table 3-6 of Stellaris LM4F232H5QC * datasheet and table 38 of STM32F10xxx Cortex-M3 programming manual. */ +#ifndef MPU_ATTR_INTERNAL_SRAM #define MPU_ATTR_INTERNAL_SRAM 6 /* for Internal SRAM */ +#endif +#ifndef MPU_ATTR_FLASH_MEMORY #define MPU_ATTR_FLASH_MEMORY 2 /* for flash memory */ +#endif /** * Enable MPU diff --git a/core/cortex-m/mpu.c b/core/cortex-m/mpu.c index 5537d1830d..d99540287c 100644 --- a/core/cortex-m/mpu.c +++ b/core/cortex-m/mpu.c @@ -7,6 +7,7 @@ #include "mpu.h" #include "console.h" +#include "cpu.h" #include "registers.h" #include "task.h" #include "util.h" @@ -32,8 +33,16 @@ static void mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit, MPU_SIZE &= ~1; /* Disable */ if (enable) { MPU_BASE = addr; - MPU_ATTR = attr; - MPU_SIZE = (srd << 8) | ((size_bit - 1) << 1) | 1; /* Enable */ + /* + * MPU_ATTR = attr; + * MPU_SIZE = (srd << 8) | ((size_bit - 1) << 1) | 1; + * + * WORKAROUND: the 2 half-word accesses above should work + * according to the doc, but they don't ..., do a single 32-bit + * one. + */ + REG32(&MPU_SIZE) = ((uint32_t)attr << 16) + | (srd << 8) | ((size_bit - 1) << 1) | 1; } asm volatile("isb; dsb;"); @@ -202,16 +211,42 @@ int mpu_lock_rw_flash(void) } #endif /* !CONFIG_EXTERNAL_STORAGE */ +#ifdef CONFIG_CHIP_UNCACHED_REGION +/* Store temporarily the regions ranges to use them for the MPU configuration */ +#define REGION(_name, _flag, _start, _size) \ + static const uint32_t CONCAT2(_region_start_, _name) \ + __attribute__((unused, section(".unused"))) = _start; \ + static const uint32_t CONCAT2(_region_size_, _name) \ + __attribute__((unused, section(".unused"))) = _size; +#include "memory_regions.inc" +#undef REGION +#endif /* CONFIG_CHIP_UNCACHED_REGION */ + int mpu_pre_init(void) { int i; + uint32_t mpu_type = mpu_get_type(); - if (mpu_get_type() != 0x00000800) + /* Supports MPU with 8 or 16 unified regions */ + if ((mpu_type & MPU_TYPE_UNIFIED_MASK) || + (MPU_TYPE_REG_COUNT(mpu_type) != 8 && + MPU_TYPE_REG_COUNT(mpu_type) != 16)) return EC_ERROR_UNIMPLEMENTED; mpu_disable(); - for (i = 0; i < 8; ++i) + for (i = 0; i < MPU_TYPE_REG_COUNT(mpu_type); ++i) mpu_config_region(i, CONFIG_RAM_BASE, CONFIG_RAM_SIZE, 0, 0); +#ifdef CONFIG_ARMV7M_CACHE +#ifdef CONFIG_CHIP_UNCACHED_REGION + mpu_config_region(REGION_UNCACHED_RAM, + CONCAT2(_region_start_, CONFIG_CHIP_UNCACHED_REGION), + CONCAT2(_region_size_, CONFIG_CHIP_UNCACHED_REGION), + MPU_ATTR_XN | MPU_ATTR_RW_RW, 1); + mpu_enable(); +#endif /* CONFIG_CHIP_UNCACHED_REGION */ + cpu_enable_caches(); +#endif /* CONFIG_ARMV7M_CACHE */ + return EC_SUCCESS; } diff --git a/include/config.h b/include/config.h index aa99222954..da3a89dcb3 100644 --- a/include/config.h +++ b/include/config.h @@ -742,6 +742,14 @@ */ #undef CONFIG_CHIP_PRE_INIT +/* + * Set the caching attributes of one of the RAM regions to uncached. + * + * When defined, CONFIG_CHIP_UNCACHED_REGION must be equal to the name of one + * of the regions defined in memory_regions.inc for CONFIG_CHIP_MEMORY_REGIONS. + */ +#undef CONFIG_CHIP_UNCACHED_REGION + /*****************************************************************************/ /* Chipset config */ diff --git a/include/link_defs.h b/include/link_defs.h index f27651691c..1ff5804bb9 100644 --- a/include/link_defs.h +++ b/include/link_defs.h @@ -110,5 +110,10 @@ extern const void *__data_end; #else #define __SECTION(name) #endif /* CONFIG_MEMORY_REGIONS */ +#ifdef CONFIG_CHIP_UNCACHED_REGION +#define __uncached __SECTION(CONFIG_CHIP_UNCACHED_REGION) +#else +#define __uncached +#endif #endif /* __CROS_EC_LINK_DEFS_H */ -- cgit v1.2.1