|
|
|
@ -25,6 +25,7 @@
|
|
|
|
|
#include <linux/serial_8250.h>
|
|
|
|
|
#include <linux/of_fdt.h>
|
|
|
|
|
#include <linux/libfdt.h>
|
|
|
|
|
#include <linux/kexec.h>
|
|
|
|
|
|
|
|
|
|
#include <asm/processor.h>
|
|
|
|
|
#include <asm/reboot.h>
|
|
|
|
@ -58,11 +59,208 @@ struct octeon_boot_descriptor *octeon_boot_desc_ptr;
|
|
|
|
|
struct cvmx_bootinfo *octeon_bootinfo;
|
|
|
|
|
EXPORT_SYMBOL(octeon_bootinfo);
|
|
|
|
|
|
|
|
|
|
static unsigned long long RESERVE_LOW_MEM = 0ull;
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
/*
|
|
|
|
|
* Wait for relocation code is prepared and send
|
|
|
|
|
* secondary CPUs to spin until kernel is relocated.
|
|
|
|
|
*/
|
|
|
|
|
static void octeon_kexec_smp_down(void *ignored)
|
|
|
|
|
{
|
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
set_cpu_online(cpu, false);
|
|
|
|
|
while (!atomic_read(&kexec_ready_to_reboot))
|
|
|
|
|
cpu_relax();
|
|
|
|
|
|
|
|
|
|
asm volatile (
|
|
|
|
|
" sync \n"
|
|
|
|
|
" synci ($0) \n");
|
|
|
|
|
|
|
|
|
|
relocated_kexec_smp_wait(NULL);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define OCTEON_DDR0_BASE (0x0ULL)
|
|
|
|
|
#define OCTEON_DDR0_SIZE (0x010000000ULL)
|
|
|
|
|
#define OCTEON_DDR1_BASE (0x410000000ULL)
|
|
|
|
|
#define OCTEON_DDR1_SIZE (0x010000000ULL)
|
|
|
|
|
#define OCTEON_DDR2_BASE (0x020000000ULL)
|
|
|
|
|
#define OCTEON_DDR2_SIZE (0x3e0000000ULL)
|
|
|
|
|
#define OCTEON_MAX_PHY_MEM_SIZE (16*1024*1024*1024ULL)
|
|
|
|
|
|
|
|
|
|
static struct kimage *kimage_ptr;
|
|
|
|
|
|
|
|
|
|
static void kexec_bootmem_init(uint64_t mem_size, uint32_t low_reserved_bytes)
|
|
|
|
|
{
|
|
|
|
|
int64_t addr;
|
|
|
|
|
struct cvmx_bootmem_desc *bootmem_desc;
|
|
|
|
|
|
|
|
|
|
bootmem_desc = cvmx_bootmem_get_desc();
|
|
|
|
|
|
|
|
|
|
if (mem_size > OCTEON_MAX_PHY_MEM_SIZE) {
|
|
|
|
|
mem_size = OCTEON_MAX_PHY_MEM_SIZE;
|
|
|
|
|
pr_err("Error: requested memory too large,"
|
|
|
|
|
"truncating to maximum size\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER;
|
|
|
|
|
bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER;
|
|
|
|
|
|
|
|
|
|
addr = (OCTEON_DDR0_BASE + RESERVE_LOW_MEM + low_reserved_bytes);
|
|
|
|
|
bootmem_desc->head_addr = 0;
|
|
|
|
|
|
|
|
|
|
if (mem_size <= OCTEON_DDR0_SIZE) {
|
|
|
|
|
__cvmx_bootmem_phy_free(addr,
|
|
|
|
|
mem_size - RESERVE_LOW_MEM -
|
|
|
|
|
low_reserved_bytes, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__cvmx_bootmem_phy_free(addr,
|
|
|
|
|
OCTEON_DDR0_SIZE - RESERVE_LOW_MEM -
|
|
|
|
|
low_reserved_bytes, 0);
|
|
|
|
|
|
|
|
|
|
mem_size -= OCTEON_DDR0_SIZE;
|
|
|
|
|
|
|
|
|
|
if (mem_size > OCTEON_DDR1_SIZE) {
|
|
|
|
|
__cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0);
|
|
|
|
|
__cvmx_bootmem_phy_free(OCTEON_DDR2_BASE,
|
|
|
|
|
mem_size - OCTEON_DDR1_SIZE, 0);
|
|
|
|
|
} else
|
|
|
|
|
__cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int octeon_kexec_prepare(struct kimage *image)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *bootloader = "kexec";
|
|
|
|
|
|
|
|
|
|
octeon_boot_desc_ptr->argc = 0;
|
|
|
|
|
for (i = 0; i < image->nr_segments; i++) {
|
|
|
|
|
if (!strncmp(bootloader, (char *)image->segment[i].buf,
|
|
|
|
|
strlen(bootloader))) {
|
|
|
|
|
/*
|
|
|
|
|
* convert command line string to array
|
|
|
|
|
* of parameters (as bootloader does).
|
|
|
|
|
*/
|
|
|
|
|
int argc = 0, offt;
|
|
|
|
|
char *str = (char *)image->segment[i].buf;
|
|
|
|
|
char *ptr = strchr(str, ' ');
|
|
|
|
|
while (ptr && (OCTEON_ARGV_MAX_ARGS > argc)) {
|
|
|
|
|
*ptr = '\0';
|
|
|
|
|
if (ptr[1] != ' ') {
|
|
|
|
|
offt = (int)(ptr - str + 1);
|
|
|
|
|
octeon_boot_desc_ptr->argv[argc] =
|
|
|
|
|
image->segment[i].mem + offt;
|
|
|
|
|
argc++;
|
|
|
|
|
}
|
|
|
|
|
ptr = strchr(ptr + 1, ' ');
|
|
|
|
|
}
|
|
|
|
|
octeon_boot_desc_ptr->argc = argc;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Information about segments will be needed during pre-boot memory
|
|
|
|
|
* initialization.
|
|
|
|
|
*/
|
|
|
|
|
kimage_ptr = image;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void octeon_generic_shutdown(void)
|
|
|
|
|
{
|
|
|
|
|
int cpu, i;
|
|
|
|
|
struct cvmx_bootmem_desc *bootmem_desc;
|
|
|
|
|
void *named_block_array_ptr;
|
|
|
|
|
|
|
|
|
|
bootmem_desc = cvmx_bootmem_get_desc();
|
|
|
|
|
named_block_array_ptr =
|
|
|
|
|
cvmx_phys_to_ptr(bootmem_desc->named_block_array_addr);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
/* disable watchdogs */
|
|
|
|
|
for_each_online_cpu(cpu)
|
|
|
|
|
cvmx_write_csr(CVMX_CIU_WDOGX(cpu_logical_map(cpu)), 0);
|
|
|
|
|
#else
|
|
|
|
|
cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0);
|
|
|
|
|
#endif
|
|
|
|
|
if (kimage_ptr != kexec_crash_image) {
|
|
|
|
|
memset(named_block_array_ptr,
|
|
|
|
|
0x0,
|
|
|
|
|
CVMX_BOOTMEM_NUM_NAMED_BLOCKS *
|
|
|
|
|
sizeof(struct cvmx_bootmem_named_block_desc));
|
|
|
|
|
/*
|
|
|
|
|
* Mark all memory (except low 0x100000 bytes) as free.
|
|
|
|
|
* It is the same thing that bootloader does.
|
|
|
|
|
*/
|
|
|
|
|
kexec_bootmem_init(octeon_bootinfo->dram_size*1024ULL*1024ULL,
|
|
|
|
|
0x100000);
|
|
|
|
|
/*
|
|
|
|
|
* Allocate all segments to avoid their corruption during boot.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < kimage_ptr->nr_segments; i++)
|
|
|
|
|
cvmx_bootmem_alloc_address(
|
|
|
|
|
kimage_ptr->segment[i].memsz + 2*PAGE_SIZE,
|
|
|
|
|
kimage_ptr->segment[i].mem - PAGE_SIZE,
|
|
|
|
|
PAGE_SIZE);
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Do not mark all memory as free. Free only named sections
|
|
|
|
|
* leaving the rest of memory unchanged.
|
|
|
|
|
*/
|
|
|
|
|
struct cvmx_bootmem_named_block_desc *ptr =
|
|
|
|
|
(struct cvmx_bootmem_named_block_desc *)
|
|
|
|
|
named_block_array_ptr;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < bootmem_desc->named_block_num_blocks; i++)
|
|
|
|
|
if (ptr[i].size)
|
|
|
|
|
cvmx_bootmem_free_named(ptr[i].name);
|
|
|
|
|
}
|
|
|
|
|
kexec_args[2] = 1UL; /* running on octeon_main_processor */
|
|
|
|
|
kexec_args[3] = (unsigned long)octeon_boot_desc_ptr;
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
secondary_kexec_args[2] = 0UL; /* running on secondary cpu */
|
|
|
|
|
secondary_kexec_args[3] = (unsigned long)octeon_boot_desc_ptr;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void octeon_shutdown(void)
|
|
|
|
|
{
|
|
|
|
|
octeon_generic_shutdown();
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
smp_call_function(octeon_kexec_smp_down, NULL, 0);
|
|
|
|
|
smp_wmb();
|
|
|
|
|
while (num_online_cpus() > 1) {
|
|
|
|
|
cpu_relax();
|
|
|
|
|
mdelay(1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void octeon_crash_shutdown(struct pt_regs *regs)
|
|
|
|
|
{
|
|
|
|
|
octeon_generic_shutdown();
|
|
|
|
|
default_machine_crash_shutdown(regs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* CONFIG_KEXEC */
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_CAVIUM_RESERVE32
|
|
|
|
|
uint64_t octeon_reserve32_memory;
|
|
|
|
|
EXPORT_SYMBOL(octeon_reserve32_memory);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
/* crashkernel cmdline parameter is parsed _after_ memory setup
|
|
|
|
|
* we also parse it here (workaround for EHB5200) */
|
|
|
|
|
static uint64_t crashk_size, crashk_base;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int octeon_uart;
|
|
|
|
|
|
|
|
|
|
extern asmlinkage void handle_int(void);
|
|
|
|
@ -417,6 +615,8 @@ void octeon_user_io_init(void)
|
|
|
|
|
void __init prom_init(void)
|
|
|
|
|
{
|
|
|
|
|
struct cvmx_sysinfo *sysinfo;
|
|
|
|
|
const char *arg;
|
|
|
|
|
char *p;
|
|
|
|
|
int i;
|
|
|
|
|
int argc;
|
|
|
|
|
#ifdef CONFIG_CAVIUM_RESERVE32
|
|
|
|
@ -568,6 +768,15 @@ void __init prom_init(void)
|
|
|
|
|
if (octeon_is_simulation())
|
|
|
|
|
MAX_MEMORY = 64ull << 20;
|
|
|
|
|
|
|
|
|
|
arg = strstr(arcs_cmdline, "mem=");
|
|
|
|
|
if (arg) {
|
|
|
|
|
MAX_MEMORY = memparse(arg + 4, &p);
|
|
|
|
|
if (MAX_MEMORY == 0)
|
|
|
|
|
MAX_MEMORY = 32ull << 30;
|
|
|
|
|
if (*p == '@')
|
|
|
|
|
RESERVE_LOW_MEM = memparse(p + 1, &p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arcs_cmdline[0] = 0;
|
|
|
|
|
argc = octeon_boot_desc_ptr->argc;
|
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
@ -575,15 +784,29 @@ void __init prom_init(void)
|
|
|
|
|
cvmx_phys_to_ptr(octeon_boot_desc_ptr->argv[i]);
|
|
|
|
|
if ((strncmp(arg, "MEM=", 4) == 0) ||
|
|
|
|
|
(strncmp(arg, "mem=", 4) == 0)) {
|
|
|
|
|
sscanf(arg + 4, "%llu", &MAX_MEMORY);
|
|
|
|
|
MAX_MEMORY <<= 20;
|
|
|
|
|
MAX_MEMORY = memparse(arg + 4, &p);
|
|
|
|
|
if (MAX_MEMORY == 0)
|
|
|
|
|
MAX_MEMORY = 32ull << 30;
|
|
|
|
|
if (*p == '@')
|
|
|
|
|
RESERVE_LOW_MEM = memparse(p + 1, &p);
|
|
|
|
|
} else if (strcmp(arg, "ecc_verbose") == 0) {
|
|
|
|
|
#ifdef CONFIG_CAVIUM_REPORT_SINGLE_BIT_ECC
|
|
|
|
|
__cvmx_interrupt_ecc_report_single_bit_errors = 1;
|
|
|
|
|
pr_notice("Reporting of single bit ECC errors is "
|
|
|
|
|
"turned on\n");
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
} else if (strncmp(arg, "crashkernel=", 12) == 0) {
|
|
|
|
|
crashk_size = memparse(arg+12, &p);
|
|
|
|
|
if (*p == '@')
|
|
|
|
|
crashk_base = memparse(p+1, &p);
|
|
|
|
|
strcat(arcs_cmdline, " ");
|
|
|
|
|
strcat(arcs_cmdline, arg);
|
|
|
|
|
/*
|
|
|
|
|
* To do: switch parsing to new style, something like:
|
|
|
|
|
* parse_crashkernel(arg, sysinfo->system_dram_size,
|
|
|
|
|
* &crashk_size, &crashk_base);
|
|
|
|
|
*/
|
|
|
|
|
#endif
|
|
|
|
|
} else if (strlen(arcs_cmdline) + strlen(arg) + 1 <
|
|
|
|
|
sizeof(arcs_cmdline) - 1) {
|
|
|
|
@ -619,11 +842,18 @@ void __init prom_init(void)
|
|
|
|
|
_machine_restart = octeon_restart;
|
|
|
|
|
_machine_halt = octeon_halt;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
_machine_kexec_shutdown = octeon_shutdown;
|
|
|
|
|
_machine_crash_shutdown = octeon_crash_shutdown;
|
|
|
|
|
_machine_kexec_prepare = octeon_kexec_prepare;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
octeon_user_io_init();
|
|
|
|
|
register_smp_ops(&octeon_smp_ops);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Exclude a single page from the regions obtained in plat_mem_setup. */
|
|
|
|
|
#ifndef CONFIG_CRASH_DUMP
|
|
|
|
|
static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *size)
|
|
|
|
|
{
|
|
|
|
|
if (addr > *mem && addr < *mem + *size) {
|
|
|
|
@ -638,14 +868,21 @@ static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *size)
|
|
|
|
|
*size -= PAGE_SIZE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_CRASH_DUMP */
|
|
|
|
|
|
|
|
|
|
void __init plat_mem_setup(void)
|
|
|
|
|
{
|
|
|
|
|
uint64_t mem_alloc_size;
|
|
|
|
|
uint64_t total;
|
|
|
|
|
uint64_t crashk_end;
|
|
|
|
|
#ifndef CONFIG_CRASH_DUMP
|
|
|
|
|
int64_t memory;
|
|
|
|
|
uint64_t kernel_start;
|
|
|
|
|
uint64_t kernel_size;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
|
crashk_end = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The Mips memory init uses the first memory location for
|
|
|
|
@ -658,6 +895,17 @@ void __init plat_mem_setup(void)
|
|
|
|
|
if (mem_alloc_size > MAX_MEMORY)
|
|
|
|
|
mem_alloc_size = MAX_MEMORY;
|
|
|
|
|
|
|
|
|
|
/* Crashkernel ignores bootmem list. It relies on mem=X@Y option */
|
|
|
|
|
#ifdef CONFIG_CRASH_DUMP
|
|
|
|
|
add_memory_region(RESERVE_LOW_MEM, MAX_MEMORY, BOOT_MEM_RAM);
|
|
|
|
|
total += MAX_MEMORY;
|
|
|
|
|
#else
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
if (crashk_size > 0) {
|
|
|
|
|
add_memory_region(crashk_base, crashk_size, BOOT_MEM_RAM);
|
|
|
|
|
crashk_end = crashk_base + crashk_size;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
/*
|
|
|
|
|
* When allocating memory, we want incrementing addresses from
|
|
|
|
|
* bootmem_alloc so the code in add_memory_region can merge
|
|
|
|
@ -672,6 +920,9 @@ void __init plat_mem_setup(void)
|
|
|
|
|
CVMX_BOOTMEM_FLAG_NO_LOCKING);
|
|
|
|
|
if (memory >= 0) {
|
|
|
|
|
u64 size = mem_alloc_size;
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
uint64_t end;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* exclude a page at the beginning and end of
|
|
|
|
@ -684,20 +935,67 @@ void __init plat_mem_setup(void)
|
|
|
|
|
memory_exclude_page(CVMX_PCIE_BAR1_PHYS_BASE +
|
|
|
|
|
CVMX_PCIE_BAR1_PHYS_SIZE,
|
|
|
|
|
&memory, &size);
|
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
|
end = memory + mem_alloc_size;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function automatically merges address
|
|
|
|
|
* regions next to each other if they are
|
|
|
|
|
* received in incrementing order.
|
|
|
|
|
* This function automatically merges address regions
|
|
|
|
|
* next to each other if they are received in
|
|
|
|
|
* incrementing order
|
|
|
|
|
*/
|
|
|
|
|
if (size)
|
|
|
|
|
add_memory_region(memory, size, BOOT_MEM_RAM);
|
|
|
|
|
if (memory < crashk_base && end > crashk_end) {
|
|
|
|
|
/* region is fully in */
|
|
|
|
|
add_memory_region(memory,
|
|
|
|
|
crashk_base - memory,
|
|
|
|
|
BOOT_MEM_RAM);
|
|
|
|
|
total += crashk_base - memory;
|
|
|
|
|
add_memory_region(crashk_end,
|
|
|
|
|
end - crashk_end,
|
|
|
|
|
BOOT_MEM_RAM);
|
|
|
|
|
total += end - crashk_end;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memory >= crashk_base && end <= crashk_end)
|
|
|
|
|
/*
|
|
|
|
|
* Entire memory region is within the new
|
|
|
|
|
* kernel's memory, ignore it.
|
|
|
|
|
*/
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (memory > crashk_base && memory < crashk_end &&
|
|
|
|
|
end > crashk_end) {
|
|
|
|
|
/*
|
|
|
|
|
* Overlap with the beginning of the region,
|
|
|
|
|
* reserve the beginning.
|
|
|
|
|
*/
|
|
|
|
|
mem_alloc_size -= crashk_end - memory;
|
|
|
|
|
memory = crashk_end;
|
|
|
|
|
} else if (memory < crashk_base && end > crashk_base &&
|
|
|
|
|
end < crashk_end)
|
|
|
|
|
/*
|
|
|
|
|
* Overlap with the beginning of the region,
|
|
|
|
|
* chop of end.
|
|
|
|
|
*/
|
|
|
|
|
mem_alloc_size -= end - crashk_base;
|
|
|
|
|
#endif
|
|
|
|
|
add_memory_region(memory, mem_alloc_size, BOOT_MEM_RAM);
|
|
|
|
|
total += mem_alloc_size;
|
|
|
|
|
/* Recovering mem_alloc_size */
|
|
|
|
|
mem_alloc_size = 4 << 20;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cvmx_bootmem_unlock();
|
|
|
|
|
/* Add the memory region for the kernel. */
|
|
|
|
|
kernel_start = (unsigned long) _text;
|
|
|
|
|
kernel_size = ALIGN(_end - _text, 0x100000);
|
|
|
|
|
|
|
|
|
|
/* Adjust for physical offset. */
|
|
|
|
|
kernel_start &= ~0xffffffff80000000ULL;
|
|
|
|
|
add_memory_region(kernel_start, kernel_size, BOOT_MEM_RAM);
|
|
|
|
|
#endif /* CONFIG_CRASH_DUMP */
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_CAVIUM_RESERVE32
|
|
|
|
|
/*
|
|
|
|
|