2022-05-31 18:04:11 +08:00
|
|
|
// 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/export.h>
|
|
|
|
#include <linux/fcntl.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/cacheflush.h>
|
|
|
|
#include <asm/cpu.h>
|
|
|
|
#include <asm/cpu-features.h>
|
|
|
|
#include <asm/dma.h>
|
|
|
|
#include <asm/loongarch.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/setup.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
void cache_error_setup(void)
|
|
|
|
{
|
|
|
|
extern char __weak except_vec_cex;
|
|
|
|
set_merr_handler(0x0, &except_vec_cex, 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long icache_size __read_mostly;
|
|
|
|
static unsigned long dcache_size __read_mostly;
|
|
|
|
static unsigned long vcache_size __read_mostly;
|
|
|
|
static unsigned long scache_size __read_mostly;
|
|
|
|
|
|
|
|
static char *way_string[] = { NULL, "direct mapped", "2-way",
|
|
|
|
"3-way", "4-way", "5-way", "6-way", "7-way", "8-way",
|
|
|
|
"9-way", "10-way", "11-way", "12-way",
|
|
|
|
"13-way", "14-way", "15-way", "16-way",
|
|
|
|
};
|
|
|
|
|
|
|
|
static void probe_pcache(void)
|
|
|
|
{
|
|
|
|
struct cpuinfo_loongarch *c = ¤t_cpu_data;
|
|
|
|
unsigned int lsize, sets, ways;
|
|
|
|
unsigned int config;
|
|
|
|
|
|
|
|
config = read_cpucfg(LOONGARCH_CPUCFG17);
|
|
|
|
lsize = 1 << ((config & CPUCFG17_L1I_SIZE_M) >> CPUCFG17_L1I_SIZE);
|
|
|
|
sets = 1 << ((config & CPUCFG17_L1I_SETS_M) >> CPUCFG17_L1I_SETS);
|
|
|
|
ways = ((config & CPUCFG17_L1I_WAYS_M) >> CPUCFG17_L1I_WAYS) + 1;
|
|
|
|
|
|
|
|
c->icache.linesz = lsize;
|
|
|
|
c->icache.sets = sets;
|
|
|
|
c->icache.ways = ways;
|
|
|
|
icache_size = sets * ways * lsize;
|
|
|
|
c->icache.waysize = icache_size / c->icache.ways;
|
|
|
|
|
|
|
|
config = read_cpucfg(LOONGARCH_CPUCFG18);
|
|
|
|
lsize = 1 << ((config & CPUCFG18_L1D_SIZE_M) >> CPUCFG18_L1D_SIZE);
|
|
|
|
sets = 1 << ((config & CPUCFG18_L1D_SETS_M) >> CPUCFG18_L1D_SETS);
|
|
|
|
ways = ((config & CPUCFG18_L1D_WAYS_M) >> CPUCFG18_L1D_WAYS) + 1;
|
|
|
|
|
|
|
|
c->dcache.linesz = lsize;
|
|
|
|
c->dcache.sets = sets;
|
|
|
|
c->dcache.ways = ways;
|
|
|
|
dcache_size = sets * ways * lsize;
|
|
|
|
c->dcache.waysize = dcache_size / c->dcache.ways;
|
|
|
|
|
|
|
|
c->options |= LOONGARCH_CPU_PREFETCH;
|
|
|
|
|
|
|
|
pr_info("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n",
|
|
|
|
icache_size >> 10, way_string[c->icache.ways], "VIPT", c->icache.linesz);
|
|
|
|
|
|
|
|
pr_info("Primary data cache %ldkB, %s, %s, %s, linesize %d bytes\n",
|
|
|
|
dcache_size >> 10, way_string[c->dcache.ways], "VIPT", "no aliases", c->dcache.linesz);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void probe_vcache(void)
|
|
|
|
{
|
|
|
|
struct cpuinfo_loongarch *c = ¤t_cpu_data;
|
|
|
|
unsigned int lsize, sets, ways;
|
|
|
|
unsigned int config;
|
|
|
|
|
|
|
|
config = read_cpucfg(LOONGARCH_CPUCFG19);
|
|
|
|
lsize = 1 << ((config & CPUCFG19_L2_SIZE_M) >> CPUCFG19_L2_SIZE);
|
|
|
|
sets = 1 << ((config & CPUCFG19_L2_SETS_M) >> CPUCFG19_L2_SETS);
|
|
|
|
ways = ((config & CPUCFG19_L2_WAYS_M) >> CPUCFG19_L2_WAYS) + 1;
|
|
|
|
|
|
|
|
c->vcache.linesz = lsize;
|
|
|
|
c->vcache.sets = sets;
|
|
|
|
c->vcache.ways = ways;
|
|
|
|
vcache_size = lsize * sets * ways;
|
|
|
|
c->vcache.waysize = vcache_size / c->vcache.ways;
|
|
|
|
|
|
|
|
pr_info("Unified victim cache %ldkB %s, linesize %d bytes.\n",
|
|
|
|
vcache_size >> 10, way_string[c->vcache.ways], c->vcache.linesz);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void probe_scache(void)
|
|
|
|
{
|
|
|
|
struct cpuinfo_loongarch *c = ¤t_cpu_data;
|
|
|
|
unsigned int lsize, sets, ways;
|
|
|
|
unsigned int config;
|
|
|
|
|
|
|
|
config = read_cpucfg(LOONGARCH_CPUCFG20);
|
|
|
|
lsize = 1 << ((config & CPUCFG20_L3_SIZE_M) >> CPUCFG20_L3_SIZE);
|
|
|
|
sets = 1 << ((config & CPUCFG20_L3_SETS_M) >> CPUCFG20_L3_SETS);
|
|
|
|
ways = ((config & CPUCFG20_L3_WAYS_M) >> CPUCFG20_L3_WAYS) + 1;
|
|
|
|
|
|
|
|
c->scache.linesz = lsize;
|
|
|
|
c->scache.sets = sets;
|
|
|
|
c->scache.ways = ways;
|
|
|
|
/* 4 cores. scaches are shared */
|
|
|
|
scache_size = lsize * sets * ways;
|
|
|
|
c->scache.waysize = scache_size / c->scache.ways;
|
|
|
|
|
|
|
|
pr_info("Unified secondary cache %ldkB %s, linesize %d bytes.\n",
|
|
|
|
scache_size >> 10, way_string[c->scache.ways], c->scache.linesz);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpu_cache_init(void)
|
|
|
|
{
|
|
|
|
probe_pcache();
|
|
|
|
probe_vcache();
|
|
|
|
probe_scache();
|
|
|
|
|
|
|
|
shm_align_mask = PAGE_SIZE - 1;
|
|
|
|
}
|
2022-07-11 15:05:43 +08:00
|
|
|
|
|
|
|
static const pgprot_t protection_map[16] = {
|
|
|
|
[VM_NONE] = __pgprot(_CACHE_CC | _PAGE_USER |
|
|
|
|
_PAGE_PROTNONE | _PAGE_NO_EXEC |
|
|
|
|
_PAGE_NO_READ),
|
|
|
|
[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_PROTNONE | _PAGE_NO_EXEC |
|
|
|
|
_PAGE_NO_READ),
|
|
|
|
[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
|