uboot/arch/arm/cpu/armv7m/cache.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
   4 * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
   5 */
   6
   7#include <common.h>
   8#include <cpu_func.h>
   9#include <errno.h>
  10#include <log.h>
  11#include <asm/armv7m.h>
  12#include <asm/cache.h>
  13#include <asm/io.h>
  14#include <linux/bitops.h>
  15
  16/* Cache maintenance operation registers */
  17
  18#define V7M_CACHE_REG_ICIALLU           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x00))
  19#define INVAL_ICACHE_POU                0
  20#define V7M_CACHE_REG_ICIMVALU          ((u32 *)(V7M_CACHE_MAINT_BASE + 0x08))
  21#define V7M_CACHE_REG_DCIMVAC           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x0C))
  22#define V7M_CACHE_REG_DCISW             ((u32 *)(V7M_CACHE_MAINT_BASE + 0x10))
  23#define V7M_CACHE_REG_DCCMVAU           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x14))
  24#define V7M_CACHE_REG_DCCMVAC           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x18))
  25#define V7M_CACHE_REG_DCCSW             ((u32 *)(V7M_CACHE_MAINT_BASE + 0x1C))
  26#define V7M_CACHE_REG_DCCIMVAC          ((u32 *)(V7M_CACHE_MAINT_BASE + 0x20))
  27#define V7M_CACHE_REG_DCCISW            ((u32 *)(V7M_CACHE_MAINT_BASE + 0x24))
  28#define WAYS_SHIFT                      30
  29#define SETS_SHIFT                      5
  30
  31/* armv7m processor feature registers */
  32
  33#define V7M_PROC_REG_CLIDR              ((u32 *)(V7M_PROC_FTR_BASE + 0x00))
  34#define V7M_PROC_REG_CTR                ((u32 *)(V7M_PROC_FTR_BASE + 0x04))
  35#define V7M_PROC_REG_CCSIDR             ((u32 *)(V7M_PROC_FTR_BASE + 0x08))
  36#define MASK_NUM_WAYS                   GENMASK(12, 3)
  37#define MASK_NUM_SETS                   GENMASK(27, 13)
  38#define CLINE_SIZE_MASK                 GENMASK(2, 0)
  39#define NUM_WAYS_SHIFT                  3
  40#define NUM_SETS_SHIFT                  13
  41#define V7M_PROC_REG_CSSELR             ((u32 *)(V7M_PROC_FTR_BASE + 0x0C))
  42#define SEL_I_OR_D                      BIT(0)
  43
  44enum cache_type {
  45        DCACHE,
  46        ICACHE,
  47};
  48
  49/* PoU : Point of Unification, Poc: Point of Coherency */
  50enum cache_action {
  51        INVALIDATE_POU,         /* i-cache invalidate by address */
  52        INVALIDATE_POC,         /* d-cache invalidate by address */
  53        INVALIDATE_SET_WAY,     /* d-cache invalidate by sets/ways */
  54        FLUSH_POU,              /* d-cache clean by address to the PoU */
  55        FLUSH_POC,              /* d-cache clean by address to the PoC */
  56        FLUSH_SET_WAY,          /* d-cache clean by sets/ways */
  57        FLUSH_INVAL_POC,        /* d-cache clean & invalidate by addr to PoC */
  58        FLUSH_INVAL_SET_WAY,    /* d-cache clean & invalidate by set/ways */
  59};
  60
  61#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
  62struct dcache_config {
  63        u32 ways;
  64        u32 sets;
  65};
  66
  67static void get_cache_ways_sets(struct dcache_config *cache)
  68{
  69        u32 cache_size_id = readl(V7M_PROC_REG_CCSIDR);
  70
  71        cache->ways = (cache_size_id & MASK_NUM_WAYS) >> NUM_WAYS_SHIFT;
  72        cache->sets = (cache_size_id & MASK_NUM_SETS) >> NUM_SETS_SHIFT;
  73}
  74
  75/*
  76 * Return the io register to perform required cache action like clean or clean
  77 * & invalidate by sets/ways.
  78 */
  79static u32 *get_action_reg_set_ways(enum cache_action action)
  80{
  81        switch (action) {
  82        case INVALIDATE_SET_WAY:
  83                return V7M_CACHE_REG_DCISW;
  84        case FLUSH_SET_WAY:
  85                return V7M_CACHE_REG_DCCSW;
  86        case FLUSH_INVAL_SET_WAY:
  87                return V7M_CACHE_REG_DCCISW;
  88        default:
  89                break;
  90        };
  91
  92        return NULL;
  93}
  94
  95/*
  96 * Return the io register to perform required cache action like clean or clean
  97 * & invalidate by adddress or range.
  98 */
  99static u32 *get_action_reg_range(enum cache_action action)
 100{
 101        switch (action) {
 102        case INVALIDATE_POU:
 103                return V7M_CACHE_REG_ICIMVALU;
 104        case INVALIDATE_POC:
 105                return V7M_CACHE_REG_DCIMVAC;
 106        case FLUSH_POU:
 107                return V7M_CACHE_REG_DCCMVAU;
 108        case FLUSH_POC:
 109                return V7M_CACHE_REG_DCCMVAC;
 110        case FLUSH_INVAL_POC:
 111                return V7M_CACHE_REG_DCCIMVAC;
 112        default:
 113                break;
 114        }
 115
 116        return NULL;
 117}
 118
 119static u32 get_cline_size(enum cache_type type)
 120{
 121        u32 size;
 122
 123        if (type == DCACHE)
 124                clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
 125        else if (type == ICACHE)
 126                setbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
 127        /* Make sure cache selection is effective for next memory access */
 128        dsb();
 129
 130        size = readl(V7M_PROC_REG_CCSIDR) & CLINE_SIZE_MASK;
 131        /* Size enocoded as 2 less than log(no_of_words_in_cache_line) base 2 */
 132        size = 1 << (size + 2);
 133        debug("cache line size is %d\n", size);
 134
 135        return size;
 136}
 137
 138/* Perform the action like invalidate/clean on a range of cache addresses */
 139static int action_cache_range(enum cache_action action, u32 start_addr,
 140                              int64_t size)
 141{
 142        u32 cline_size;
 143        u32 *action_reg;
 144        enum cache_type type;
 145
 146        action_reg = get_action_reg_range(action);
 147        if (!action_reg)
 148                return -EINVAL;
 149        if (action == INVALIDATE_POU)
 150                type = ICACHE;
 151        else
 152                type = DCACHE;
 153
 154        /* Cache line size is minium size for the cache action */
 155        cline_size = get_cline_size(type);
 156        /* Align start address to cache line boundary */
 157        start_addr &= ~(cline_size - 1);
 158        debug("total size for cache action = %llx\n", size);
 159        do {
 160                writel(start_addr, action_reg);
 161                size -= cline_size;
 162                start_addr += cline_size;
 163        } while (size > cline_size);
 164
 165        /* Make sure cache action is effective for next memory access */
 166        dsb();
 167        isb();  /* Make sure instruction stream sees it */
 168        debug("cache action on range done\n");
 169
 170        return 0;
 171}
 172
 173/* Perform the action like invalidate/clean on all cached addresses */
 174static int action_dcache_all(enum cache_action action)
 175{
 176        struct dcache_config cache;
 177        u32 *action_reg;
 178        int i, j;
 179
 180        action_reg = get_action_reg_set_ways(action);
 181        if (!action_reg)
 182                return -EINVAL;
 183
 184        clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
 185        /* Make sure cache selection is effective for next memory access */
 186        dsb();
 187
 188        get_cache_ways_sets(&cache);    /* Get number of ways & sets */
 189        debug("cache: ways= %d, sets= %d\n", cache.ways + 1, cache.sets + 1);
 190        for (i = cache.sets; i >= 0; i--) {
 191                for (j = cache.ways; j >= 0; j--) {
 192                        writel((j << WAYS_SHIFT) | (i << SETS_SHIFT),
 193                               action_reg);
 194                }
 195        }
 196
 197        /* Make sure cache action is effective for next memory access */
 198        dsb();
 199        isb();  /* Make sure instruction stream sees it */
 200
 201        return 0;
 202}
 203
 204void dcache_enable(void)
 205{
 206        if (dcache_status())    /* return if cache already enabled */
 207                return;
 208
 209        if (action_dcache_all(INVALIDATE_SET_WAY)) {
 210                printf("ERR: D-cache not enabled\n");
 211                return;
 212        }
 213
 214        setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
 215
 216        /* Make sure cache action is effective for next memory access */
 217        dsb();
 218        isb();  /* Make sure instruction stream sees it */
 219}
 220
 221void dcache_disable(void)
 222{
 223        if (!dcache_status())
 224                return;
 225
 226        /* if dcache is enabled-> dcache disable & then flush */
 227        if (action_dcache_all(FLUSH_SET_WAY)) {
 228                printf("ERR: D-cache not flushed\n");
 229                return;
 230        }
 231
 232        clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
 233
 234        /* Make sure cache action is effective for next memory access */
 235        dsb();
 236        isb();  /* Make sure instruction stream sees it */
 237}
 238
 239int dcache_status(void)
 240{
 241        return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_DCACHE)) != 0;
 242}
 243
 244void invalidate_dcache_range(unsigned long start, unsigned long stop)
 245{
 246        if (action_cache_range(INVALIDATE_POC, start, stop - start)) {
 247                printf("ERR: D-cache not invalidated\n");
 248                return;
 249        }
 250}
 251
 252void flush_dcache_range(unsigned long start, unsigned long stop)
 253{
 254        if (action_cache_range(FLUSH_POC, start, stop - start)) {
 255                printf("ERR: D-cache not flushed\n");
 256                return;
 257        }
 258}
 259void flush_dcache_all(void)
 260{
 261        if (action_dcache_all(FLUSH_SET_WAY)) {
 262                printf("ERR: D-cache not flushed\n");
 263                return;
 264        }
 265}
 266
 267void invalidate_dcache_all(void)
 268{
 269        if (action_dcache_all(INVALIDATE_SET_WAY)) {
 270                printf("ERR: D-cache not invalidated\n");
 271                return;
 272        }
 273}
 274#else
 275void dcache_enable(void)
 276{
 277        return;
 278}
 279
 280void dcache_disable(void)
 281{
 282        return;
 283}
 284
 285int dcache_status(void)
 286{
 287        return 0;
 288}
 289
 290void flush_dcache_all(void)
 291{
 292}
 293
 294void invalidate_dcache_all(void)
 295{
 296}
 297
 298void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
 299                                     enum dcache_option option)
 300{
 301}
 302
 303#endif
 304
 305#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
 306
 307void invalidate_icache_all(void)
 308{
 309        writel(INVAL_ICACHE_POU, V7M_CACHE_REG_ICIALLU);
 310
 311        /* Make sure cache action is effective for next memory access */
 312        dsb();
 313        isb();  /* Make sure instruction stream sees it */
 314}
 315
 316void icache_enable(void)
 317{
 318        if (icache_status())
 319                return;
 320
 321        invalidate_icache_all();
 322        setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
 323
 324        /* Make sure cache action is effective for next memory access */
 325        dsb();
 326        isb();  /* Make sure instruction stream sees it */
 327}
 328
 329int icache_status(void)
 330{
 331        return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_ICACHE)) != 0;
 332}
 333
 334void icache_disable(void)
 335{
 336        if (!icache_status())
 337                return;
 338
 339        isb();  /* flush pipeline */
 340        clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
 341        isb();  /* subsequent instructions fetch see cache disable effect */
 342}
 343#else
 344void invalidate_icache_all(void)
 345{
 346        return;
 347}
 348
 349void icache_enable(void)
 350{
 351        return;
 352}
 353
 354void icache_disable(void)
 355{
 356        return;
 357}
 358
 359int icache_status(void)
 360{
 361        return 0;
 362}
 363#endif
 364
 365void enable_caches(void)
 366{
 367#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
 368        icache_enable();
 369#endif
 370#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
 371        dcache_enable();
 372#endif
 373}
 374