/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2004-2007 Cavium Networks
* Copyright (C) 2008, 2009 Wind River Systems
* written by Ralf Baechle <ralf@linux-mips.org>
*/
#include <linux/compiler.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/serial.h>
#include <linux/smp.h>
#include <linux/types.h>
#include <linux/string.h> /* for memset */
#include <linux/tty.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#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>
#include <asm/smp-ops.h>
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h>
#include <asm/bootinfo.h>
#include <asm/sections.h>
#include <asm/time.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/pci-octeon.h>
#include <asm/octeon/cvmx-mio-defs.h>
extern struct plat_smp_ops octeon_smp_ops;
#ifdef CONFIG_PCI
extern void pci_console_init(const char *arg);
#endif
static unsigned long long MAX_MEMORY = 512ull << 20;
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[