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