root/arch/loongarch/mm/cache.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 *
 * Derived from MIPS:
 * Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
 * Copyright (C) 2007 MIPS Technologies, Inc.
 */
#include <linux/cacheinfo.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/syscalls.h>

#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
#include <asm/loongarch.h>
#include <asm/numa.h>
#include <asm/processor.h>
#include <asm/setup.h>

void cache_error_setup(void)
{
        extern char __weak except_vec_cex;
        set_merr_handler(0x0, &except_vec_cex, 0x80);
}

/*
 * LoongArch maintains ICache/DCache coherency by hardware,
 * we just need "ibar" to avoid instruction hazard here.
 */
void local_flush_icache_range(unsigned long start, unsigned long end)
{
        asm volatile ("\tibar 0\n"::);
}
EXPORT_SYMBOL(local_flush_icache_range);

static void flush_cache_leaf(unsigned int leaf)
{
        int i, j, nr_nodes;
        uint64_t addr = CSR_DMW0_BASE;
        struct cache_desc *cdesc = current_cpu_data.cache_leaves + leaf;

        nr_nodes = cache_private(cdesc) ? 1 : loongson_sysconf.nr_nodes;

        do {
                for (i = 0; i < cdesc->sets; i++) {
                        for (j = 0; j < cdesc->ways; j++) {
                                flush_cache_line(leaf, addr);
                                addr++;
                        }

                        addr -= cdesc->ways;
                        addr += cdesc->linesz;
                }
                addr += (1ULL << NODE_ADDRSPACE_SHIFT);
        } while (--nr_nodes > 0);
}

asmlinkage __visible void __flush_cache_all(void)
{
        int leaf;
        struct cache_desc *cdesc = current_cpu_data.cache_leaves;
        unsigned int cache_present = current_cpu_data.cache_leaves_present;

        leaf = cache_present - 1;
        if (cache_inclusive(cdesc + leaf)) {
                flush_cache_leaf(leaf);
                return;
        }

        for (leaf = 0; leaf < cache_present; leaf++)
                flush_cache_leaf(leaf);
}

#define L1IUPRE         (1 << 0)
#define L1IUUNIFY       (1 << 1)
#define L1DPRE          (1 << 2)

#define LXIUPRE         (1 << 0)
#define LXIUUNIFY       (1 << 1)
#define LXIUPRIV        (1 << 2)
#define LXIUINCL        (1 << 3)
#define LXDPRE          (1 << 4)
#define LXDPRIV         (1 << 5)
#define LXDINCL         (1 << 6)

#define populate_cache_properties(cfg0, cdesc, level, leaf)                             \
do {                                                                                    \
        unsigned int cfg1;                                                              \
                                                                                        \
        cfg1 = read_cpucfg(LOONGARCH_CPUCFG17 + leaf);                                  \
        if (level == 1) {                                                               \
                cdesc->flags |= CACHE_PRIVATE;                                          \
        } else {                                                                        \
                if (cfg0 & LXIUPRIV)                                                    \
                        cdesc->flags |= CACHE_PRIVATE;                                  \
                if (cfg0 & LXIUINCL)                                                    \
                        cdesc->flags |= CACHE_INCLUSIVE;                                \
        }                                                                               \
        cdesc->level = level;                                                           \
        cdesc->flags |= CACHE_PRESENT;                                                  \
        cdesc->ways = ((cfg1 & CPUCFG_CACHE_WAYS_M) >> CPUCFG_CACHE_WAYS) + 1;          \
        cdesc->sets = 1 << ((cfg1 & CPUCFG_CACHE_SETS_M) >> CPUCFG_CACHE_SETS);         \
        cdesc->linesz = 1 << ((cfg1 & CPUCFG_CACHE_LSIZE_M) >> CPUCFG_CACHE_LSIZE);     \
        cdesc++; leaf++;                                                                \
} while (0)

void cpu_cache_init(void)
{
        unsigned int leaf = 0, level = 1;
        unsigned int config = read_cpucfg(LOONGARCH_CPUCFG16);
        struct cache_desc *cdesc = current_cpu_data.cache_leaves;

        if (config & L1IUPRE) {
                if (config & L1IUUNIFY)
                        cdesc->type = CACHE_TYPE_UNIFIED;
                else
                        cdesc->type = CACHE_TYPE_INST;
                populate_cache_properties(config, cdesc, level, leaf);
        }

        if (config & L1DPRE) {
                cdesc->type = CACHE_TYPE_DATA;
                populate_cache_properties(config, cdesc, level, leaf);
        }

        config = config >> 3;
        for (level = 2; level <= CACHE_LEVEL_MAX; level++) {
                if (!config)
                        break;

                if (config & LXIUPRE) {
                        if (config & LXIUUNIFY)
                                cdesc->type = CACHE_TYPE_UNIFIED;
                        else
                                cdesc->type = CACHE_TYPE_INST;
                        populate_cache_properties(config, cdesc, level, leaf);
                }

                if (config & LXDPRE) {
                        cdesc->type = CACHE_TYPE_DATA;
                        populate_cache_properties(config, cdesc, level, leaf);
                }

                config = config >> 7;
        }

        BUG_ON(leaf > CACHE_LEAVES_MAX);

        current_cpu_data.cache_leaves_present = leaf;
        current_cpu_data.options |= LOONGARCH_CPU_PREFETCH;
}

static const pgprot_t protection_map[16] = {
        [VM_NONE]                                       = __pgprot(_CACHE_CC | _PAGE_USER |
                                                                   _PAGE_NO_EXEC | _PAGE_NO_READ |
                                                                   (_PAGE_PROTNONE ? : _PAGE_PRESENT)),
        [VM_READ]                                       = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC),
        [VM_WRITE]                                      = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC),
        [VM_WRITE | VM_READ]                            = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC),
        [VM_EXEC]                                       = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_EXEC | VM_READ]                             = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_EXEC | VM_WRITE]                            = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_EXEC | VM_WRITE | VM_READ]                  = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_SHARED]                                     = __pgprot(_CACHE_CC | _PAGE_USER |
                                                                   _PAGE_NO_EXEC | _PAGE_NO_READ |
                                                                   (_PAGE_PROTNONE ? : _PAGE_PRESENT)),
        [VM_SHARED | VM_READ]                           = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC),
        [VM_SHARED | VM_WRITE]                          = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC | _PAGE_WRITE),
        [VM_SHARED | VM_WRITE | VM_READ]                = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_NO_EXEC | _PAGE_WRITE),
        [VM_SHARED | VM_EXEC]                           = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_SHARED | VM_EXEC | VM_READ]                 = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT),
        [VM_SHARED | VM_EXEC | VM_WRITE]                = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_WRITE),
        [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]      = __pgprot(_CACHE_CC | _PAGE_VALID |
                                                                   _PAGE_USER | _PAGE_PRESENT |
                                                                   _PAGE_WRITE)
};
DECLARE_VM_GET_PAGE_PROT