diff options
Diffstat (limited to 'roms/u-boot/arch/arm/cpu/armv7m')
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/Makefile | 9 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/cache.c | 373 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/config.mk | 6 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/cpu.c | 58 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/mpu.c | 44 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/start.S | 16 | ||||
-rw-r--r-- | roms/u-boot/arch/arm/cpu/armv7m/systick-timer.c | 119 |
7 files changed, 625 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arm/cpu/armv7m/Makefile b/roms/u-boot/arch/arm/cpu/armv7m/Makefile new file mode 100644 index 000000000..baeac9343 --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +extra-y := start.o +obj-y += cpu.o cache.o +obj-$(CONFIG_SYS_ARM_MPU) += mpu.o +obj-$(CONFIG_SYS_ARCH_TIMER) += systick-timer.o diff --git a/roms/u-boot/arch/arm/cpu/armv7m/cache.c b/roms/u-boot/arch/arm/cpu/armv7m/cache.c new file mode 100644 index 000000000..d1aecf6a8 --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/cache.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. + */ + +#include <common.h> +#include <cpu_func.h> +#include <errno.h> +#include <log.h> +#include <asm/armv7m.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <linux/bitops.h> + +/* Cache maintenance operation registers */ + +#define V7M_CACHE_REG_ICIALLU ((u32 *)(V7M_CACHE_MAINT_BASE + 0x00)) +#define INVAL_ICACHE_POU 0 +#define V7M_CACHE_REG_ICIMVALU ((u32 *)(V7M_CACHE_MAINT_BASE + 0x08)) +#define V7M_CACHE_REG_DCIMVAC ((u32 *)(V7M_CACHE_MAINT_BASE + 0x0C)) +#define V7M_CACHE_REG_DCISW ((u32 *)(V7M_CACHE_MAINT_BASE + 0x10)) +#define V7M_CACHE_REG_DCCMVAU ((u32 *)(V7M_CACHE_MAINT_BASE + 0x14)) +#define V7M_CACHE_REG_DCCMVAC ((u32 *)(V7M_CACHE_MAINT_BASE + 0x18)) +#define V7M_CACHE_REG_DCCSW ((u32 *)(V7M_CACHE_MAINT_BASE + 0x1C)) +#define V7M_CACHE_REG_DCCIMVAC ((u32 *)(V7M_CACHE_MAINT_BASE + 0x20)) +#define V7M_CACHE_REG_DCCISW ((u32 *)(V7M_CACHE_MAINT_BASE + 0x24)) +#define WAYS_SHIFT 30 +#define SETS_SHIFT 5 + +/* armv7m processor feature registers */ + +#define V7M_PROC_REG_CLIDR ((u32 *)(V7M_PROC_FTR_BASE + 0x00)) +#define V7M_PROC_REG_CTR ((u32 *)(V7M_PROC_FTR_BASE + 0x04)) +#define V7M_PROC_REG_CCSIDR ((u32 *)(V7M_PROC_FTR_BASE + 0x08)) +#define MASK_NUM_WAYS GENMASK(12, 3) +#define MASK_NUM_SETS GENMASK(27, 13) +#define CLINE_SIZE_MASK GENMASK(2, 0) +#define NUM_WAYS_SHIFT 3 +#define NUM_SETS_SHIFT 13 +#define V7M_PROC_REG_CSSELR ((u32 *)(V7M_PROC_FTR_BASE + 0x0C)) +#define SEL_I_OR_D BIT(0) + +enum cache_type { + DCACHE, + ICACHE, +}; + +/* PoU : Point of Unification, Poc: Point of Coherency */ +enum cache_action { + INVALIDATE_POU, /* i-cache invalidate by address */ + INVALIDATE_POC, /* d-cache invalidate by address */ + INVALIDATE_SET_WAY, /* d-cache invalidate by sets/ways */ + FLUSH_POU, /* d-cache clean by address to the PoU */ + FLUSH_POC, /* d-cache clean by address to the PoC */ + FLUSH_SET_WAY, /* d-cache clean by sets/ways */ + FLUSH_INVAL_POC, /* d-cache clean & invalidate by addr to PoC */ + FLUSH_INVAL_SET_WAY, /* d-cache clean & invalidate by set/ways */ +}; + +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) +struct dcache_config { + u32 ways; + u32 sets; +}; + +static void get_cache_ways_sets(struct dcache_config *cache) +{ + u32 cache_size_id = readl(V7M_PROC_REG_CCSIDR); + + cache->ways = (cache_size_id & MASK_NUM_WAYS) >> NUM_WAYS_SHIFT; + cache->sets = (cache_size_id & MASK_NUM_SETS) >> NUM_SETS_SHIFT; +} + +/* + * Return the io register to perform required cache action like clean or clean + * & invalidate by sets/ways. + */ +static u32 *get_action_reg_set_ways(enum cache_action action) +{ + switch (action) { + case INVALIDATE_SET_WAY: + return V7M_CACHE_REG_DCISW; + case FLUSH_SET_WAY: + return V7M_CACHE_REG_DCCSW; + case FLUSH_INVAL_SET_WAY: + return V7M_CACHE_REG_DCCISW; + default: + break; + }; + + return NULL; +} + +/* + * Return the io register to perform required cache action like clean or clean + * & invalidate by adddress or range. + */ +static u32 *get_action_reg_range(enum cache_action action) +{ + switch (action) { + case INVALIDATE_POU: + return V7M_CACHE_REG_ICIMVALU; + case INVALIDATE_POC: + return V7M_CACHE_REG_DCIMVAC; + case FLUSH_POU: + return V7M_CACHE_REG_DCCMVAU; + case FLUSH_POC: + return V7M_CACHE_REG_DCCMVAC; + case FLUSH_INVAL_POC: + return V7M_CACHE_REG_DCCIMVAC; + default: + break; + } + + return NULL; +} + +static u32 get_cline_size(enum cache_type type) +{ + u32 size; + + if (type == DCACHE) + clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D)); + else if (type == ICACHE) + setbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D)); + /* Make sure cache selection is effective for next memory access */ + dsb(); + + size = readl(V7M_PROC_REG_CCSIDR) & CLINE_SIZE_MASK; + /* Size enocoded as 2 less than log(no_of_words_in_cache_line) base 2 */ + size = 1 << (size + 2); + debug("cache line size is %d\n", size); + + return size; +} + +/* Perform the action like invalidate/clean on a range of cache addresses */ +static int action_cache_range(enum cache_action action, u32 start_addr, + int64_t size) +{ + u32 cline_size; + u32 *action_reg; + enum cache_type type; + + action_reg = get_action_reg_range(action); + if (!action_reg) + return -EINVAL; + if (action == INVALIDATE_POU) + type = ICACHE; + else + type = DCACHE; + + /* Cache line size is minium size for the cache action */ + cline_size = get_cline_size(type); + /* Align start address to cache line boundary */ + start_addr &= ~(cline_size - 1); + debug("total size for cache action = %llx\n", size); + do { + writel(start_addr, action_reg); + size -= cline_size; + start_addr += cline_size; + } while (size > cline_size); + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ + debug("cache action on range done\n"); + + return 0; +} + +/* Perform the action like invalidate/clean on all cached addresses */ +static int action_dcache_all(enum cache_action action) +{ + struct dcache_config cache; + u32 *action_reg; + int i, j; + + action_reg = get_action_reg_set_ways(action); + if (!action_reg) + return -EINVAL; + + clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D)); + /* Make sure cache selection is effective for next memory access */ + dsb(); + + get_cache_ways_sets(&cache); /* Get number of ways & sets */ + debug("cache: ways= %d, sets= %d\n", cache.ways + 1, cache.sets + 1); + for (i = cache.sets; i >= 0; i--) { + for (j = cache.ways; j >= 0; j--) { + writel((j << WAYS_SHIFT) | (i << SETS_SHIFT), + action_reg); + } + } + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ + + return 0; +} + +void dcache_enable(void) +{ + if (dcache_status()) /* return if cache already enabled */ + return; + + if (action_dcache_all(INVALIDATE_SET_WAY)) { + printf("ERR: D-cache not enabled\n"); + return; + } + + setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE)); + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ +} + +void dcache_disable(void) +{ + if (!dcache_status()) + return; + + /* if dcache is enabled-> dcache disable & then flush */ + if (action_dcache_all(FLUSH_SET_WAY)) { + printf("ERR: D-cache not flushed\n"); + return; + } + + clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE)); + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ +} + +int dcache_status(void) +{ + return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_DCACHE)) != 0; +} + +void invalidate_dcache_range(unsigned long start, unsigned long stop) +{ + if (action_cache_range(INVALIDATE_POC, start, stop - start)) { + printf("ERR: D-cache not invalidated\n"); + return; + } +} + +void flush_dcache_range(unsigned long start, unsigned long stop) +{ + if (action_cache_range(FLUSH_POC, start, stop - start)) { + printf("ERR: D-cache not flushed\n"); + return; + } +} +void flush_dcache_all(void) +{ + if (action_dcache_all(FLUSH_SET_WAY)) { + printf("ERR: D-cache not flushed\n"); + return; + } +} + +void invalidate_dcache_all(void) +{ + if (action_dcache_all(INVALIDATE_SET_WAY)) { + printf("ERR: D-cache not invalidated\n"); + return; + } +} +#else +void dcache_enable(void) +{ + return; +} + +void dcache_disable(void) +{ + return; +} + +int dcache_status(void) +{ + return 0; +} + +void flush_dcache_all(void) +{ +} + +void invalidate_dcache_all(void) +{ +} + +void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, + enum dcache_option option) +{ +} + +#endif + +#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF) + +void invalidate_icache_all(void) +{ + writel(INVAL_ICACHE_POU, V7M_CACHE_REG_ICIALLU); + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ +} + +void icache_enable(void) +{ + if (icache_status()) + return; + + invalidate_icache_all(); + setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE)); + + /* Make sure cache action is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ +} + +int icache_status(void) +{ + return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_ICACHE)) != 0; +} + +void icache_disable(void) +{ + if (!icache_status()) + return; + + isb(); /* flush pipeline */ + clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE)); + isb(); /* subsequent instructions fetch see cache disable effect */ +} +#else +void invalidate_icache_all(void) +{ + return; +} + +void icache_enable(void) +{ + return; +} + +void icache_disable(void) +{ + return; +} + +int icache_status(void) +{ + return 0; +} +#endif + +void enable_caches(void) +{ +#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF) + icache_enable(); +#endif +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) + dcache_enable(); +#endif +} diff --git a/roms/u-boot/arch/arm/cpu/armv7m/config.mk b/roms/u-boot/arch/arm/cpu/armv7m/config.mk new file mode 100644 index 000000000..f50964cfb --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/config.mk @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2015 +# Kamil Lulko, <kamil.lulko@gmail.com> + +PLATFORM_CPPFLAGS += -mno-unaligned-access diff --git a/roms/u-boot/arch/arm/cpu/armv7m/cpu.c b/roms/u-boot/arch/arm/cpu/armv7m/cpu.c new file mode 100644 index 000000000..63721018c --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/cpu.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010,2011 + * Vladimir Khusainov, Emcraft Systems, vlad@emcraft.com + * + * (C) Copyright 2015 + * Kamil Lulko, <kamil.lulko@gmail.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <irq_func.h> +#include <asm/io.h> +#include <asm/armv7m.h> + +/* + * This is called right before passing control to + * the Linux kernel point. + */ +int cleanup_before_linux(void) +{ + /* + * this function is called just before we call linux + * it prepares the processor for linux + * + * disable interrupt and turn off caches etc ... + */ + disable_interrupts(); + /* + * turn off D-cache + * dcache_disable() in turn flushes the d-cache + * MPU is still enabled & can't be disabled as the u-boot + * code might be running in sdram which by default is not + * executable area. + */ + dcache_disable(); + /* invalidate to make sure no cache line gets dirty between + * dcache flushing and disabling dcache */ + invalidate_dcache_all(); + + icache_disable(); + invalidate_icache_all(); + + return 0; +} + +/* + * Perform the low-level reset. + */ +void reset_cpu(void) +{ + /* + * Perform reset but keep priority group unchanged. + */ + writel((V7M_AIRCR_VECTKEY << V7M_AIRCR_VECTKEY_SHIFT) + | (V7M_SCB->aircr & V7M_AIRCR_PRIGROUP_MSK) + | V7M_AIRCR_SYSRESET, &V7M_SCB->aircr); +} diff --git a/roms/u-boot/arch/arm/cpu/armv7m/mpu.c b/roms/u-boot/arch/arm/cpu/armv7m/mpu.c new file mode 100644 index 000000000..81e7492f1 --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/mpu.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. + */ + +#include <linux/bitops.h> +#include <asm/armv7m.h> +#include <asm/armv7_mpu.h> +#include <asm/io.h> + +#define V7M_MPU_CTRL_ENABLE BIT(0) +#define V7M_MPU_CTRL_DISABLE (0 << 0) +#define V7M_MPU_CTRL_HFNMIENA BIT(1) +#define V7M_MPU_CTRL_PRIVDEFENA BIT(2) +#define VALID_REGION BIT(4) + +void disable_mpu(void) +{ + writel(0, &V7M_MPU->ctrl); +} + +void enable_mpu(void) +{ + writel(V7M_MPU_CTRL_ENABLE | V7M_MPU_CTRL_PRIVDEFENA, &V7M_MPU->ctrl); + + /* Make sure new mpu config is effective for next memory access */ + dsb(); + isb(); /* Make sure instruction stream sees it */ +} + +void mpu_config(struct mpu_region_config *reg_config) +{ + uint32_t attr; + + attr = get_attr_encoding(reg_config->mr_attr); + + writel(reg_config->start_addr | VALID_REGION | reg_config->region_no, + &V7M_MPU->rbar); + + writel(reg_config->xn << XN_SHIFT | reg_config->ap << AP_SHIFT | attr + | reg_config->reg_size << REGION_SIZE_SHIFT | ENABLE_REGION + , &V7M_MPU->rasr); +} diff --git a/roms/u-boot/arch/arm/cpu/armv7m/start.S b/roms/u-boot/arch/arm/cpu/armv7m/start.S new file mode 100644 index 000000000..0c07f2140 --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/start.S @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2015 + * Kamil Lulko, <kamil.lulko@gmail.com> + */ + +#include <asm/assembler.h> + +.globl reset +.type reset, %function +reset: + W(b) _main + +.globl c_runtime_cpu_setup +c_runtime_cpu_setup: + mov pc, lr diff --git a/roms/u-boot/arch/arm/cpu/armv7m/systick-timer.c b/roms/u-boot/arch/arm/cpu/armv7m/systick-timer.c new file mode 100644 index 000000000..556eaf8c7 --- /dev/null +++ b/roms/u-boot/arch/arm/cpu/armv7m/systick-timer.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ARM Cortex M3/M4/M7 SysTick timer driver + * (C) Copyright 2017 Renesas Electronics Europe Ltd + * + * Based on arch/arm/mach-stm32/stm32f1/timer.c + * (C) Copyright 2015 + * Kamil Lulko, <kamil.lulko@gmail.com> + * + * Copyright 2015 ATS Advanced Telematics Systems GmbH + * Copyright 2015 Konsulko Group, Matt Porter <mporter@konsulko.com> + * + * The SysTick timer is a 24-bit count down timer. The clock can be either the + * CPU clock or a reference clock. Since the timer will wrap around very quickly + * when using the CPU clock, and we do not handle the timer interrupts, it is + * expected that this driver is only ever used with a slow reference clock. + * + * The number of reference clock ticks that correspond to 10ms is normally + * defined in the SysTick Calibration register's TENMS field. However, on some + * devices this is wrong, so this driver allows the clock rate to be defined + * using CONFIG_SYS_HZ_CLOCK. + */ + +#include <common.h> +#include <init.h> +#include <time.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/bitops.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* SysTick Base Address - fixed for all Cortex M3, M4 and M7 devices */ +#define SYSTICK_BASE 0xE000E010 + +struct cm3_systick { + uint32_t ctrl; + uint32_t reload_val; + uint32_t current_val; + uint32_t calibration; +}; + +#define TIMER_MAX_VAL 0x00FFFFFF +#define SYSTICK_CTRL_EN BIT(0) +/* Clock source: 0 = Ref clock, 1 = CPU clock */ +#define SYSTICK_CTRL_CPU_CLK BIT(2) +#define SYSTICK_CAL_NOREF BIT(31) +#define SYSTICK_CAL_SKEW BIT(30) +#define SYSTICK_CAL_TENMS_MASK 0x00FFFFFF + +/* read the 24-bit timer */ +static ulong read_timer(void) +{ + struct cm3_systick *systick = (struct cm3_systick *)SYSTICK_BASE; + + /* The timer counts down, therefore convert to an incrementing timer */ + return TIMER_MAX_VAL - readl(&systick->current_val); +} + +int timer_init(void) +{ + struct cm3_systick *systick = (struct cm3_systick *)SYSTICK_BASE; + u32 cal; + + writel(TIMER_MAX_VAL, &systick->reload_val); + /* Any write to current_val reg clears it to 0 */ + writel(0, &systick->current_val); + + cal = readl(&systick->calibration); + if (cal & SYSTICK_CAL_NOREF) + /* Use CPU clock, no interrupts */ + writel(SYSTICK_CTRL_EN | SYSTICK_CTRL_CPU_CLK, &systick->ctrl); + else + /* Use external clock, no interrupts */ + writel(SYSTICK_CTRL_EN, &systick->ctrl); + + /* + * If the TENMS field is inexact or wrong, specify the clock rate using + * CONFIG_SYS_HZ_CLOCK. + */ +#if defined(CONFIG_SYS_HZ_CLOCK) + gd->arch.timer_rate_hz = CONFIG_SYS_HZ_CLOCK; +#else + gd->arch.timer_rate_hz = (cal & SYSTICK_CAL_TENMS_MASK) * 100; +#endif + + gd->arch.tbl = 0; + gd->arch.tbu = 0; + gd->arch.lastinc = read_timer(); + + return 0; +} + +/* return milli-seconds timer value */ +ulong get_timer(ulong base) +{ + unsigned long long t = get_ticks() * 1000; + + return (ulong)((t / gd->arch.timer_rate_hz)) - base; +} + +unsigned long long get_ticks(void) +{ + u32 now = read_timer(); + + if (now >= gd->arch.lastinc) + gd->arch.tbl += (now - gd->arch.lastinc); + else + gd->arch.tbl += (TIMER_MAX_VAL - gd->arch.lastinc) + now; + + gd->arch.lastinc = now; + + return gd->arch.tbl; +} + +ulong get_tbclk(void) +{ + return gd->arch.timer_rate_hz; +} |