mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
9.4 KiB
369 lines
9.4 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// Copyright (C) 2005-2017 Andes Technology Corporation |
|
|
|
#include <linux/cpu.h> |
|
#include <linux/memblock.h> |
|
#include <linux/seq_file.h> |
|
#include <linux/console.h> |
|
#include <linux/screen_info.h> |
|
#include <linux/delay.h> |
|
#include <linux/dma-mapping.h> |
|
#include <linux/of_fdt.h> |
|
#include <linux/of_platform.h> |
|
#include <asm/setup.h> |
|
#include <asm/sections.h> |
|
#include <asm/proc-fns.h> |
|
#include <asm/cache_info.h> |
|
#include <asm/elf.h> |
|
#include <asm/fpu.h> |
|
#include <nds32_intrinsic.h> |
|
|
|
#define HWCAP_MFUSR_PC 0x000001 |
|
#define HWCAP_EXT 0x000002 |
|
#define HWCAP_EXT2 0x000004 |
|
#define HWCAP_FPU 0x000008 |
|
#define HWCAP_AUDIO 0x000010 |
|
#define HWCAP_BASE16 0x000020 |
|
#define HWCAP_STRING 0x000040 |
|
#define HWCAP_REDUCED_REGS 0x000080 |
|
#define HWCAP_VIDEO 0x000100 |
|
#define HWCAP_ENCRYPT 0x000200 |
|
#define HWCAP_EDM 0x000400 |
|
#define HWCAP_LMDMA 0x000800 |
|
#define HWCAP_PFM 0x001000 |
|
#define HWCAP_HSMP 0x002000 |
|
#define HWCAP_TRACE 0x004000 |
|
#define HWCAP_DIV 0x008000 |
|
#define HWCAP_MAC 0x010000 |
|
#define HWCAP_L2C 0x020000 |
|
#define HWCAP_FPU_DP 0x040000 |
|
#define HWCAP_V2 0x080000 |
|
#define HWCAP_DX_REGS 0x100000 |
|
#define HWCAP_HWPRE 0x200000 |
|
|
|
unsigned long cpu_id, cpu_rev, cpu_cfgid; |
|
bool has_fpu = false; |
|
char cpu_series; |
|
char *endianness = NULL; |
|
|
|
unsigned int __atags_pointer __initdata; |
|
unsigned int elf_hwcap; |
|
EXPORT_SYMBOL(elf_hwcap); |
|
|
|
/* |
|
* The following string table, must sync with HWCAP_xx bitmask, |
|
* which is defined above |
|
*/ |
|
static const char *hwcap_str[] = { |
|
"mfusr_pc", |
|
"perf1", |
|
"perf2", |
|
"fpu", |
|
"audio", |
|
"16b", |
|
"string", |
|
"reduced_regs", |
|
"video", |
|
"encrypt", |
|
"edm", |
|
"lmdma", |
|
"pfm", |
|
"hsmp", |
|
"trace", |
|
"div", |
|
"mac", |
|
"l2c", |
|
"fpu_dp", |
|
"v2", |
|
"dx_regs", |
|
"hw_pre", |
|
NULL, |
|
}; |
|
|
|
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH |
|
#define WRITE_METHOD "write through" |
|
#else |
|
#define WRITE_METHOD "write back" |
|
#endif |
|
|
|
struct cache_info L1_cache_info[2]; |
|
static void __init dump_cpu_info(int cpu) |
|
{ |
|
int i, p = 0; |
|
char str[sizeof(hwcap_str) + 16]; |
|
|
|
for (i = 0; hwcap_str[i]; i++) { |
|
if (elf_hwcap & (1 << i)) { |
|
sprintf(str + p, "%s ", hwcap_str[i]); |
|
p += strlen(hwcap_str[i]) + 1; |
|
} |
|
} |
|
|
|
pr_info("CPU%d Features: %s\n", cpu, str); |
|
|
|
L1_cache_info[ICACHE].ways = CACHE_WAY(ICACHE); |
|
L1_cache_info[ICACHE].line_size = CACHE_LINE_SIZE(ICACHE); |
|
L1_cache_info[ICACHE].sets = CACHE_SET(ICACHE); |
|
L1_cache_info[ICACHE].size = |
|
L1_cache_info[ICACHE].ways * L1_cache_info[ICACHE].line_size * |
|
L1_cache_info[ICACHE].sets / 1024; |
|
pr_info("L1I:%dKB/%dS/%dW/%dB\n", L1_cache_info[ICACHE].size, |
|
L1_cache_info[ICACHE].sets, L1_cache_info[ICACHE].ways, |
|
L1_cache_info[ICACHE].line_size); |
|
L1_cache_info[DCACHE].ways = CACHE_WAY(DCACHE); |
|
L1_cache_info[DCACHE].line_size = CACHE_LINE_SIZE(DCACHE); |
|
L1_cache_info[DCACHE].sets = CACHE_SET(DCACHE); |
|
L1_cache_info[DCACHE].size = |
|
L1_cache_info[DCACHE].ways * L1_cache_info[DCACHE].line_size * |
|
L1_cache_info[DCACHE].sets / 1024; |
|
pr_info("L1D:%dKB/%dS/%dW/%dB\n", L1_cache_info[DCACHE].size, |
|
L1_cache_info[DCACHE].sets, L1_cache_info[DCACHE].ways, |
|
L1_cache_info[DCACHE].line_size); |
|
pr_info("L1 D-Cache is %s\n", WRITE_METHOD); |
|
if (L1_cache_info[DCACHE].size != L1_CACHE_BYTES) |
|
pr_crit |
|
("The cache line size(%d) of this processor is not the same as L1_CACHE_BYTES(%d).\n", |
|
L1_cache_info[DCACHE].size, L1_CACHE_BYTES); |
|
#ifdef CONFIG_CPU_CACHE_ALIASING |
|
{ |
|
int aliasing_num; |
|
aliasing_num = |
|
L1_cache_info[ICACHE].size * 1024 / PAGE_SIZE / |
|
L1_cache_info[ICACHE].ways; |
|
L1_cache_info[ICACHE].aliasing_num = aliasing_num; |
|
L1_cache_info[ICACHE].aliasing_mask = |
|
(aliasing_num - 1) << PAGE_SHIFT; |
|
aliasing_num = |
|
L1_cache_info[DCACHE].size * 1024 / PAGE_SIZE / |
|
L1_cache_info[DCACHE].ways; |
|
L1_cache_info[DCACHE].aliasing_num = aliasing_num; |
|
L1_cache_info[DCACHE].aliasing_mask = |
|
(aliasing_num - 1) << PAGE_SHIFT; |
|
} |
|
#endif |
|
#ifdef CONFIG_FPU |
|
/* Disable fpu and enable when it is used. */ |
|
if (has_fpu) |
|
disable_fpu(); |
|
#endif |
|
} |
|
|
|
static void __init setup_cpuinfo(void) |
|
{ |
|
unsigned long tmp = 0, cpu_name; |
|
|
|
cpu_dcache_inval_all(); |
|
cpu_icache_inval_all(); |
|
__nds32__isb(); |
|
|
|
cpu_id = (__nds32__mfsr(NDS32_SR_CPU_VER) & CPU_VER_mskCPUID) >> CPU_VER_offCPUID; |
|
cpu_name = ((cpu_id) & 0xf0) >> 4; |
|
cpu_series = cpu_name ? cpu_name - 10 + 'A' : 'N'; |
|
cpu_id = cpu_id & 0xf; |
|
cpu_rev = (__nds32__mfsr(NDS32_SR_CPU_VER) & CPU_VER_mskREV) >> CPU_VER_offREV; |
|
cpu_cfgid = (__nds32__mfsr(NDS32_SR_CPU_VER) & CPU_VER_mskCFGID) >> CPU_VER_offCFGID; |
|
|
|
pr_info("CPU:%c%ld, CPU_VER 0x%08x(id %lu, rev %lu, cfg %lu)\n", |
|
cpu_series, cpu_id, __nds32__mfsr(NDS32_SR_CPU_VER), cpu_id, cpu_rev, cpu_cfgid); |
|
|
|
elf_hwcap |= HWCAP_MFUSR_PC; |
|
|
|
if (((__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskBASEV) >> MSC_CFG_offBASEV) == 0) { |
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskDIV) |
|
elf_hwcap |= HWCAP_DIV; |
|
|
|
if ((__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskMAC) |
|
|| (cpu_id == 12 && cpu_rev < 4)) |
|
elf_hwcap |= HWCAP_MAC; |
|
} else { |
|
elf_hwcap |= HWCAP_V2; |
|
elf_hwcap |= HWCAP_DIV; |
|
elf_hwcap |= HWCAP_MAC; |
|
} |
|
|
|
if (cpu_cfgid & 0x0001) |
|
elf_hwcap |= HWCAP_EXT; |
|
|
|
if (cpu_cfgid & 0x0002) |
|
elf_hwcap |= HWCAP_BASE16; |
|
|
|
if (cpu_cfgid & 0x0004) |
|
elf_hwcap |= HWCAP_EXT2; |
|
|
|
if (cpu_cfgid & 0x0008) { |
|
elf_hwcap |= HWCAP_FPU; |
|
has_fpu = true; |
|
} |
|
if (cpu_cfgid & 0x0010) |
|
elf_hwcap |= HWCAP_STRING; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MMU_CFG) & MMU_CFG_mskDE) |
|
endianness = "MSB"; |
|
else |
|
endianness = "LSB"; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskEDM) |
|
elf_hwcap |= HWCAP_EDM; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskLMDMA) |
|
elf_hwcap |= HWCAP_LMDMA; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskPFM) |
|
elf_hwcap |= HWCAP_PFM; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskHSMP) |
|
elf_hwcap |= HWCAP_HSMP; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskTRACE) |
|
elf_hwcap |= HWCAP_TRACE; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskAUDIO) |
|
elf_hwcap |= HWCAP_AUDIO; |
|
|
|
if (__nds32__mfsr(NDS32_SR_MSC_CFG) & MSC_CFG_mskL2C) |
|
elf_hwcap |= HWCAP_L2C; |
|
|
|
#ifdef CONFIG_HW_PRE |
|
if (__nds32__mfsr(NDS32_SR_MISC_CTL) & MISC_CTL_makHWPRE_EN) |
|
elf_hwcap |= HWCAP_HWPRE; |
|
#endif |
|
|
|
tmp = __nds32__mfsr(NDS32_SR_CACHE_CTL); |
|
if (!IS_ENABLED(CONFIG_CPU_DCACHE_DISABLE)) |
|
tmp |= CACHE_CTL_mskDC_EN; |
|
|
|
if (!IS_ENABLED(CONFIG_CPU_ICACHE_DISABLE)) |
|
tmp |= CACHE_CTL_mskIC_EN; |
|
__nds32__mtsr_isb(tmp, NDS32_SR_CACHE_CTL); |
|
|
|
dump_cpu_info(smp_processor_id()); |
|
} |
|
|
|
static void __init setup_memory(void) |
|
{ |
|
unsigned long ram_start_pfn; |
|
unsigned long free_ram_start_pfn; |
|
phys_addr_t memory_start, memory_end; |
|
|
|
memory_end = memory_start = 0; |
|
|
|
/* Find main memory where is the kernel */ |
|
memory_start = memblock_start_of_DRAM(); |
|
memory_end = memblock_end_of_DRAM(); |
|
|
|
if (!memory_end) { |
|
panic("No memory!"); |
|
} |
|
|
|
ram_start_pfn = PFN_UP(memblock_start_of_DRAM()); |
|
/* free_ram_start_pfn is first page after kernel */ |
|
free_ram_start_pfn = PFN_UP(__pa(&_end)); |
|
max_pfn = PFN_DOWN(memblock_end_of_DRAM()); |
|
/* it could update max_pfn */ |
|
if (max_pfn - ram_start_pfn <= MAXMEM_PFN) |
|
max_low_pfn = max_pfn; |
|
else { |
|
max_low_pfn = MAXMEM_PFN + ram_start_pfn; |
|
if (!IS_ENABLED(CONFIG_HIGHMEM)) |
|
max_pfn = MAXMEM_PFN + ram_start_pfn; |
|
} |
|
/* high_memory is related with VMALLOC */ |
|
high_memory = (void *)__va(max_low_pfn * PAGE_SIZE); |
|
min_low_pfn = free_ram_start_pfn; |
|
|
|
/* |
|
* initialize the boot-time allocator (with low memory only). |
|
* |
|
* This makes the memory from the end of the kernel to the end of |
|
* RAM usable. |
|
*/ |
|
memblock_set_bottom_up(true); |
|
memblock_reserve(PFN_PHYS(ram_start_pfn), PFN_PHYS(free_ram_start_pfn - ram_start_pfn)); |
|
|
|
early_init_fdt_reserve_self(); |
|
early_init_fdt_scan_reserved_mem(); |
|
|
|
memblock_dump_all(); |
|
} |
|
|
|
void __init setup_arch(char **cmdline_p) |
|
{ |
|
early_init_devtree(__atags_pointer ? \ |
|
phys_to_virt(__atags_pointer) : __dtb_start); |
|
|
|
setup_cpuinfo(); |
|
|
|
setup_initial_init_mm(_stext, _etext, _edata, _end); |
|
|
|
/* setup bootmem allocator */ |
|
setup_memory(); |
|
|
|
/* paging_init() sets up the MMU and marks all pages as reserved */ |
|
paging_init(); |
|
|
|
/* invalidate all TLB entries because the new mapping is created */ |
|
__nds32__tlbop_flua(); |
|
|
|
/* use generic way to parse */ |
|
parse_early_param(); |
|
|
|
unflatten_and_copy_device_tree(); |
|
|
|
*cmdline_p = boot_command_line; |
|
early_trap_init(); |
|
} |
|
|
|
static int c_show(struct seq_file *m, void *v) |
|
{ |
|
int i; |
|
|
|
seq_printf(m, "Processor\t: %c%ld (id %lu, rev %lu, cfg %lu)\n", |
|
cpu_series, cpu_id, cpu_id, cpu_rev, cpu_cfgid); |
|
|
|
seq_printf(m, "L1I\t\t: %luKB/%luS/%luW/%luB\n", |
|
CACHE_SET(ICACHE) * CACHE_WAY(ICACHE) * |
|
CACHE_LINE_SIZE(ICACHE) / 1024, CACHE_SET(ICACHE), |
|
CACHE_WAY(ICACHE), CACHE_LINE_SIZE(ICACHE)); |
|
|
|
seq_printf(m, "L1D\t\t: %luKB/%luS/%luW/%luB\n", |
|
CACHE_SET(DCACHE) * CACHE_WAY(DCACHE) * |
|
CACHE_LINE_SIZE(DCACHE) / 1024, CACHE_SET(DCACHE), |
|
CACHE_WAY(DCACHE), CACHE_LINE_SIZE(DCACHE)); |
|
|
|
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", |
|
loops_per_jiffy / (500000 / HZ), |
|
(loops_per_jiffy / (5000 / HZ)) % 100); |
|
|
|
/* dump out the processor features */ |
|
seq_puts(m, "Features\t: "); |
|
|
|
for (i = 0; hwcap_str[i]; i++) |
|
if (elf_hwcap & (1 << i)) |
|
seq_printf(m, "%s ", hwcap_str[i]); |
|
|
|
seq_puts(m, "\n\n"); |
|
|
|
return 0; |
|
} |
|
|
|
static void *c_start(struct seq_file *m, loff_t * pos) |
|
{ |
|
return *pos < 1 ? (void *)1 : NULL; |
|
} |
|
|
|
static void *c_next(struct seq_file *m, void *v, loff_t * pos) |
|
{ |
|
++*pos; |
|
return NULL; |
|
} |
|
|
|
static void c_stop(struct seq_file *m, void *v) |
|
{ |
|
} |
|
|
|
struct seq_operations cpuinfo_op = { |
|
.start = c_start, |
|
.next = c_next, |
|
.stop = c_stop, |
|
.show = c_show |
|
};
|
|
|