blob: d68a02d57492d6dd3c2aa575520ddedc2c22ef81 [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
*******************************************************************************/
#include <common.h>
#include <command.h>
#include "mvOs.h"
#include "mv_amp.h"
#include "mvCpuIfRegs.h"
#include "uart/mvUart.h"
#ifdef CONFIG_SYS_HUSH_PARSER
#include <hush.h>
#endif
#ifdef CONFIG_AMP_SUPPORT
#ifdef CONFIG_AMP_TEST_CPU1
#define TEST_CPU1_WAKE_UP
#endif
int image_load_addr; /* used to pass load address to micro loader */
int image_param_addr; /* used to pass parameters address to micro loader */
int machine_id; /* used to pass machine id for linux micro loader */
int amp_group_id; /* Stores the current amp group id */
int amp_sync_boot; /* All images exit U-BOOT together using barrier*/
#ifdef CONFIG_ALP_HW_SEMAPHORE_WA
volatile int amp_soft_sem;
#endif
int amp_hw_lock_id = 0; /* Spinlock for barrier */
int amp_enable = 0;
volatile int amp_barrier; /* Barrier for AMP launch */
void linuxMicroLoader(void);
void vxWorksMicroLoader(void);
void lock_hw_mutex(int hw_lock_id);
void unlock_hw_mutex(int hw_lock_id);
int amp_verify_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
DECLARE_GLOBAL_DATA_PTR;
void amp_init(void)
{
char *env;
int group_count = 0;
env = getenv("amp_enable");
if((env) && (((strcmp(env,"yes") == 0) || (strcmp(env,"Yes") == 0) ))) {
amp_enable = 1;
}
if(amp_enable)
{
env = getenv("amp_groups");
if(env){
group_count = simple_strtoul(env, NULL, 0);
}
if(group_count < 2)
amp_sync_boot = 0;
else
amp_sync_boot = 1;
amp_barrier = group_count;
#ifdef CONFIG_ALP_HW_SEMAPHORE_WA
/* mark semaphore as free */
amp_soft_sem = 0xFF;
#endif
}
}
static void amp_duplicate_env(char *env_name, int group_id, int override)
{
char *env_val, *env;
char new_env_name[256];
env_val = getenv(env_name);
if(!env_val){
printf("Failed to duplicate undefined env %s\n", env_name);
return;
}
sprintf(new_env_name,"%s_g%d", env_name, group_id);
env = getenv(new_env_name);
if((!env) || (override)) {
setenv(new_env_name, env_val);
}
}
static void amp_set_env(char *env_name, char *val, int group_id, int override)
{
char *env;
char new_env_name[256];
sprintf(new_env_name,"%s_g%d", env_name, group_id);
env = getenv(new_env_name);
if((!env) || (override)) {
setenv(new_env_name,val);
}
}
static char* amp_getenv(char *env_name, int group_id)
{
char *env;
char amp_name[256];
sprintf(amp_name,"%s_g%d", env_name, group_id);
env = getenv(amp_name);
return env;
}
static void amp_set_linux_bootargs(int group_id)
{
char *env, args[256], tmp[256];
args[0] = '\0';
sprintf(tmp,"amp_cpu_count_g%d", group_id);
env = getenv(tmp);
if(env){
sprintf(args, "mv_cpu_count=%s",env);
}
sprintf(tmp,"amp_resources_g%d", group_id);
env = getenv(tmp);
if(env){
sprintf(args, "%s mv_rsrc=%s",args, env);
}
env = getenv("amp_shared_mem");
if(env){
sprintf(args, "%s mv_sh_mem=%s", args, env);
}
/* Each group gets the master cpu of the other group */
sprintf(tmp,"amp_master_cpu_g%d", (!group_id));
env = getenv(tmp);
if(env){
sprintf(args, "%s mv_ipc=%s",args, env);
}
sprintf(tmp,"amp_bootargs_g%d", group_id);
setenv(tmp, args);
}
static void amp_set_vxworks_bootargs(int group_id)
{
char args[256], tmp[256];
args[0] = '\0';
sprintf(args, "neta(0,0)");
sprintf(tmp,"amp_bootargs_g%d", group_id);
setenv(tmp, args);
}
static void amp_create_group(int group_id, char *os, char *cpu_count, char *master_cpu,
char *mem_base, char *mem_size, char *resrouces, int override)
{
char boot_cmd[1024], *env;
char cmd_name[256];
int img_load_addr;
int isLinux;
int isVxWorks;
int isFreeRTOS;
isLinux = ((strcmp(os, "linux") == 0));
isVxWorks = ((strcmp(os, "vxworks") == 0));
isFreeRTOS = ((strcmp(os, "freertos") == 0));
if(!isVxWorks && ! isLinux && !isFreeRTOS)
{
printf("Error: Specified unsupported OS %s\n", os);
return;
}
// Create amp envs
amp_set_env("amp_os", os, group_id, override);
amp_set_env("amp_cpu_count", cpu_count, group_id, override);
amp_set_env("amp_master_cpu", master_cpu, group_id, override);
amp_set_env("amp_mem_base", mem_base, group_id, override);
amp_set_env("amp_mem_size", mem_size, group_id, override);
amp_set_env("amp_resources", resrouces, group_id, override);
/* Duplicate boot variables for group != 0*/
/* They can be changed later using console */
if(group_id > 0){
amp_duplicate_env("image_name", group_id, 0);
amp_duplicate_env("console", group_id, 0);
#ifdef CONFIG_MTD_DEVICE
amp_duplicate_env("mtdparts", group_id, 0);
#endif
amp_duplicate_env("serverip", group_id, 0);
amp_duplicate_env("bootargs_root", group_id, 0);
amp_duplicate_env("rootpath", group_id, 0);
amp_duplicate_env("mvNetConfig", group_id, 0);
amp_set_env("ipaddr", "192.168.1.1", group_id, 0);
if(isLinux || isFreeRTOS)
amp_duplicate_env("bootargs_end", group_id, 0);
}
/* Create a boot command */
img_load_addr = 0x2000000 + (0x1000000 * group_id);
sprintf(cmd_name, "bootcmd_g%d", group_id);
if(isLinux || isFreeRTOS)
{
amp_set_linux_bootargs(group_id);
if(group_id == 0){
sprintf(boot_cmd, "tftpboot %#x $image_name; setenv bootargs "
"$console $mtdparts $bootargs_root "
"nfsroot=$serverip:$rootpath ip=$ipaddr:$serverip$bootargs_end "
"$mvNetConfig $amp_bootargs_g%d video=dovefb:lcd0:$lcd0_params "
"clcd.lcd0_enable=$lcd0_enable clcd.lcd_panel=$lcd_panel; bootm %#x; ",
img_load_addr, group_id, img_load_addr);
}
else{
sprintf(boot_cmd, "tftpboot %#x $image_name_g%d; setenv bootargs "
"$console_g%d $mtdparts_g%d $bootargs_root_g%d "
"nfsroot=$serverip_g%d:$rootpath_g%d "
"ip=$ipaddr_g%d:$serverip_g%d$bootargs_end_g%d "
"$mvNetConfig_g%d $amp_bootargs_g%d video=dovefb:lcd0:$lcd0_params "
"clcd.lcd0_enable=$lcd0_enable clcd.lcd_panel=$lcd_panel; bootm %#x; ",
img_load_addr, group_id, group_id, group_id, group_id,
group_id, group_id, group_id, group_id, group_id,
group_id, group_id, img_load_addr);
}
}
else if (isVxWorks)
{
amp_set_vxworks_bootargs(group_id);
if(group_id == 0){
setenv("bootargs_end", "ffffff00 u=anonymous pw=target");
sprintf(boot_cmd, "tftpboot %#x $image_name; setenv bootargs "
"$amp_bootargs_g%d host:$image_name h=$serverip "
"e=$ipaddr:$bootargs_end "
"o=$amp_resources_g%d:cpus=$amp_cpu_count_g%d"
":mem=$amp_mem_size_g%d; bootvx %#x;",
img_load_addr, group_id, group_id, group_id, group_id, img_load_addr);
}
else{
amp_set_env("bootargs_end", "ffffff00 u=anonymous pw=target", group_id, override);
sprintf(boot_cmd, "tftpboot %#x $image_name_g%d; setenv bootargs "
"$amp_bootargs_g%d host:$image_name_g%d h=$serverip_g%d "
"e=$ipaddr_g%d:$bootargs_end_g%d "
"o=$amp_resources_g%d:cpus=$amp_cpu_count_g%d"
":mem=$amp_mem_size_g%d; bootvx %#x;",
img_load_addr, group_id, group_id, group_id, group_id
, group_id, group_id, group_id, group_id, group_id, img_load_addr);
}
}
env = getenv(cmd_name);
if((!env) || (override))
setenv(cmd_name, boot_cmd);
}
void amp_wait_to_boot(void)
{
if(amp_sync_boot == 0)
return;
// Notify "im here" by reducing counter
lock_hw_mutex(amp_hw_lock_id);
amp_barrier--;
unlock_hw_mutex(amp_hw_lock_id);
// Now wait untill all cores
while(amp_barrier != 0);
}
static void amp_get_boot_cmd(char *boot_cmd_name, int group_id)
{
char tmp[16];
char *env;
sprintf(boot_cmd_name,"bootcmd_g%d", group_id);
/* Reset the amp boot args to consider latest changes*/
sprintf(tmp,"amp_os_g%d", group_id);
env = getenv(tmp);
if((strcmp(env, "linux") == 0))
amp_set_linux_bootargs(group_id);
}
/* The mv_amp_group_setup() function tries to allocate requested size of physical
* memory for each of the AMP groups. It is called from do_bootm_linux() and have
* to overwrite the dram_hw_info array before call to setup_memory_tags().
* It will adjust overlapping memory blocks, cut out shared memory and split the
* window going through the 4GB boundary.
* After this function is called, the info about memory banks (dram_hw_info) will
* be corrupted and it will be impossible to run amp_boot again. */
int mv_amp_group_setup(int group_id, int load_addr)
{
char *env;
int i;
unsigned long long mem_base = -1;
unsigned long long mem_size = 0;
unsigned long long mem_total = 0;
unsigned long shared_base = -1;
unsigned long shared_size = 0;
unsigned long long next_mem_base;
unsigned long g_mem_base;
unsigned long long g_mem_size;
unsigned long last_mem_base = 0;
unsigned long long last_mem_size = 0;
static unsigned int last_group_id;
static unsigned long long last_mem_end = 0;
static unsigned long long phys_mem_end = 0;
env = getenv("amp_enable");
if( !env || ( ((strcmp(env,"no") == 0) || (strcmp(env,"No") == 0) )))
return 0;
env = amp_getenv("amp_mem_base", group_id);
if(env)
mem_base = simple_strtoul(env, NULL, 16);
else {
printf("Error: please set variable amp_mem_base_g%d\n", group_id);
return 1;
}
env = amp_getenv("amp_mem_size", group_id);
if(env)
mem_size = simple_strtoull(env, NULL, 16);
else {
printf("Error: please set variable amp_mem_size_g%d\n", group_id);
return 1;
}
env = getenv("amp_shared_mem");
if(env) {
shared_base = simple_strtoul(env, &env, 16);
if (*env == ':')
shared_size = simple_strtoul(++env, NULL, 16);
}
/* Count the total size of contiguous physical memory */
if(phys_mem_end == 0) {
phys_mem_end = gd->dram_hw_info[0].start;
for(i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
if(phys_mem_end == gd->dram_hw_info[i].start)
phys_mem_end += gd->dram_hw_info[i].size;
}
mem_total = mem_size;
/* Check whether the memory region does not interfere with other groups
* Find end of memory block occupied by the group which have the highest base */
for(i = 0; i < MAX_AMP_GROUPS; i++) {
env = amp_getenv("amp_mem_base", i);
if(env == NULL)
continue;
g_mem_base = simple_strtoul(env, NULL, 16);
env = amp_getenv("amp_mem_size", i);
if(env == NULL)
continue;
g_mem_size = simple_strtoull(env, NULL, 16);
if((last_mem_end == 0) && (g_mem_base >= last_mem_base)) {
last_mem_base = g_mem_base;
last_mem_size = g_mem_size;
last_group_id = i;
}
if((mem_base < g_mem_base) &&
(mem_base + mem_size > g_mem_base))
mem_size = g_mem_base - mem_base;
}
/* Verify load address matches physical memory region */
if(((unsigned int)load_addr < mem_base) || ((unsigned int)load_addr >= mem_base + mem_size)){
printf("\nAMP Error: Load address (%#08x) not within allocated memory "
"[%#08x:%#08x]\n\n", load_addr, (unsigned int)mem_base,
(unsigned int)(mem_base + mem_size));
return 1;
}
if(last_mem_end == 0) {
if(last_mem_size == 0) {
printf("\nAMP Error: Groups memory not configured properly.\n");
return 1;
} else
last_mem_end = last_mem_base + last_mem_size;
}
gd->bd->bi_boot_params = mem_base + (0x100); // == param_phys in Makefile.boot
/* Overwrite dram_hw_info array to change available memory blocks passed
* to the OS from setup_memory_tags() */
for(i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
next_mem_base = 0;
if(mem_base >= phys_mem_end) {
mem_base = 0;
mem_size = 0;
} else if(mem_base + mem_size > phys_mem_end) {
mem_size = phys_mem_end - mem_base;
next_mem_base = phys_mem_end;
}
if((mem_base < 0x100000000ll) &&
(mem_base + mem_size > 0x100000000ll)) {
mem_size = 0x100000000ll - mem_base;
next_mem_base = 0x100000000ll;
}
if((shared_size > 0) && (mem_base <= shared_base) &&
(mem_base + mem_size > shared_base)) {
mem_size = shared_base - mem_base;
next_mem_base = shared_base + shared_size;
if (mem_total > shared_size)
mem_total -= shared_size;
else
mem_total = 0;
}
if(next_mem_base > last_mem_end)
last_mem_end = next_mem_base;
if(mem_base + mem_size > last_mem_end)
last_mem_end = mem_base + mem_size;
if((group_id != last_group_id) && (next_mem_base < last_mem_end))
next_mem_base = last_mem_end;
gd->dram_hw_info[i].start = mem_base;
gd->dram_hw_info[i].size = mem_size;
mem_total -= mem_size;
mem_size = mem_total;
if(mem_size > 0)
mem_base = next_mem_base;
else
mem_base = 0;
}
return 0;
}
#ifdef TEST_CPU1_WAKE_UP
void cpu1IsAlive(void)
{
ulong cpu1Stack = 0x20000;
int i;
asm ("mov sp, %0" : "=r" (cpu1Stack) : );
for (i=0; i<20; i++) {
printf("I'm CPU 1 - test cpu1 wake up (defined TEST_CPU1_WAKE_UP)\n");
}
hang();
}
#endif
static void mvWakeCpuToFunc(int cpu_id, void *func_addr)
{
MV_U32 *pOpcode;
/* Enable a window of size 0x7FF for bootrom code */
MV_REG_WRITE(0x200b8,0x00000991);//config window to Tunit SRAM
MV_REG_WRITE(0x200bc ,0xffff0000);
pOpcode = (MV_U32 *)0xffff0000;
*pOpcode++= 0xe59f0004; //set op-codes for boot
*pOpcode++= 0xe5900000;
*pOpcode++= 0xe12fff30;
*pOpcode++= 0xf10182d4;
/* Write function pointer to boot register. Bootrom will
* read from theres_more and jump to that adress */
#ifdef TEST_CPU1_WAKE_UP
if (cpu_id)
func_addr = cpu1IsAlive;
#endif
// set address in register
MV_REG_WRITE(CPU_RESUME_ADDR_REG(cpu_id), (MV_U32)func_addr);
/* Release hanging core by writing to reset register */
MV_REG_WRITE(CPU_SOFT_RESET_REG(cpu_id), 0);
}
#define AXP_CPU_DIVCLK_CTRL0 0x18700
#define AXP_CPU_DIVCLK_CTRL2_RATIO_FULL0 0x18708
#define AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1 0x1870C
static void mvSetSecondaryClock(int cpu_id)
{
MV_U32 val = 0;
MV_U32 divider;
/* Scale up CPU clock to max */
divider = MV_REG_READ(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1);
divider = (divider & 0x3F);
if (cpu_id == 1) {
val = MV_REG_READ(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1);
val &= ~(0x0000FF00); /* cpu1 clkdiv ratio; cpu0 based on SAR */
val |= divider << 8;
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1, val);
}
else if (cpu_id == 2) {
val = MV_REG_READ(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1);
val &= ~(0x00FF0000); /* cpu1 clkdiv ratio; cpu0 based on SAR */
val |= divider << 16;
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1, val);
}
else if (cpu_id == 3) {
val = MV_REG_READ(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1);
val &= ~0xFF000000; /* cpus 3 clkdiv ratios */
val |= divider << 24;
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL2_RATIO_FULL1, val);
}
/* Set clock devider reload smooth bit mask */
val = MV_REG_READ(AXP_CPU_DIVCLK_CTRL0);
val |= (0x1 << (cpu_id + 20));
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL0, val);
/* Request clock devider reload */
val = MV_REG_READ(AXP_CPU_DIVCLK_CTRL0);
val |= 1 << 24;
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL0, val);
/* Wait for clocks to settle down then release reload request */
udelay(100);
val &= ~(0xf << 21);
MV_REG_WRITE(AXP_CPU_DIVCLK_CTRL0, val);
udelay(100);
}
/*static void mvPrepareSecondaryLoad(int mach_id, int load_addr, int param_addr, int uart_port, int cpu_id)
{
}*/
static void amp_linux_boot(int mach_id, int load_addr, int param_addr, int group_id)
{
int clock_divisor = (CONFIG_SYS_TCLK / 16)/115200;
char *env, master_id_env[256], os_env[256];
int master_core_id=0;
/* Get master core id */
sprintf(master_id_env,"amp_master_cpu_g%d", group_id);
env = getenv(master_id_env);
if(env) {
master_core_id = strtol(env, NULL,16);
}
else{
printf("AMP Error: please set variable %s\n", master_id_env);
}
// Get OS type
sprintf(os_env,"amp_os_g%d", group_id);
env = getenv(os_env);
if(!env)
printf("AMP Error: please set variable %s\n", os_env);
printf ("\nBooting AMP group %d on CPU %d ...\n\n", group_id, master_core_id);
// Initialize UART for secondary group
mvUartInit(group_id, clock_divisor, mvUartBase(group_id));
// Initialize clock for secondary CPU
mvSetSecondaryClock(master_core_id);
// Set globals to be accessed by micro loader
machine_id = mach_id;
image_load_addr = load_addr;
image_param_addr = param_addr;
if((strcmp(env, "linux") == 0) || (strcmp(env, "freertos") == 0)) {
/* Wake up CPU to to micro loader function */
mvWakeCpuToFunc(master_core_id, &linuxMicroLoader);
}
else if (strcmp(env, "vxworks") == 0){
mvWakeCpuToFunc(master_core_id, &vxWorksMicroLoader);
}
else {
printf("AMP Error: Unsupported OS type %s\n", env);
}
}
int amp_boot(int machid, int load_addr, int param_addr)
{
/* Groups 1 an above are loaded on core > 1. so wake them up and return.
* Group 0 waits for other groups and proceeds to regular launch */
if(amp_group_id > 0){
amp_linux_boot(machid, load_addr, param_addr, amp_group_id);
return 1;
}
else{
return 0;
}
}
static void group_printenv(char *env_name, int group_id, int amp_only_env)
{
char command[256];
if((amp_only_env == 0) && (group_id == 0)){
sprintf(command,"printenv %s",env_name);
}
else{
sprintf(command,"printenv %s_g%d",env_name, group_id);
}
parse_string_outer(command, 0);
}
int amp_printenv_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int group_id = -1, group_count, id;
char *env, *os;
int isLinux, isFreeRTOS;
env = getenv("amp_enable");
if(!env || ( ((strcmp(env,"no") == 0) || (strcmp(env,"No") == 0) ))){
printf ("Error: Command available only when amp_enable=yes\n");
return 1;
}
if(argc > 1){
group_id = simple_strtoul(argv[1], NULL, 0);
}
/* Ensures the loop prints the required group */
id = group_id;
group_count = group_id + 1;
/* Make the loop print all groups */
if(group_id == -1)
{
id = 0;
env = getenv("amp_groups");
if(!env){
printf ("Warning: amp_groups undefined, assuming 0 groups\n");
group_count = 0;
}
else{
group_count = simple_strtoul(env, 0, 10);
}
}
/* Print shared AMP envs*/
printf("\n**** AMP shared ****\n\n");
parse_string_outer("printenv amp_groups",0);
parse_string_outer("printenv amp_shared_mem",0);
/* Print group sepcific envs */
for(; id < group_count; id++)
{
os = amp_getenv("amp_os", id);
isLinux = ((strcmp(os, "linux") == 0));
isFreeRTOS = ((strcmp(os, "freertos") == 0));
printf("\n**** AMP Group %d ****\n\n", id);
group_printenv("amp_os", id, 1);
group_printenv("amp_master_cpu", id, 1);
group_printenv("amp_cpu_count", id, 1);
group_printenv("amp_mem_base", id, 1);
group_printenv("amp_mem_size", id, 1);
group_printenv("amp_resources", id, 1);
group_printenv("amp_bootargs", id, 1);
group_printenv("image_name", id, 0);
group_printenv("ipaddr", id, 0);
group_printenv("serverip", id, 0);
group_printenv("bootargs_end", id, 0);
if(isLinux || isFreeRTOS){
group_printenv("console", id, 0);
#ifdef CONFIG_MTD_DEVICE
group_printenv("mtdparts", id, 0);
#endif
group_printenv("bootargs_root", id, 0);
group_printenv("rootpath", id, 0);
group_printenv("mvNetConfig", id, 0);
}
group_printenv("bootcmd", id, 1);
}
return 1;
}
U_BOOT_CMD(
amp_printenv, 2, 1, amp_printenv_cmd,
"amp_printenv - print only AMP env variables\n",
"<group_id> \n"
"\t<group_id> - which AMP group env to print. default = all. \n"
);
static int amp_boot_group(int group_id)
{
char bootcmd_name[256];
int rcode = 0;
// Change current AMP group ID
amp_group_id = group_id;
// Fetch the bootcmd name. usualy bootcmd_g${X}. X = group ID
amp_get_boot_cmd(bootcmd_name, group_id);
// run the boot command
#ifndef CONFIG_SYS_HUSH_PARSER
if (run_command (getenv (bootcmd_name), 0) < 0)
rcode = 1;
#else
if (parse_string_outer (getenv (bootcmd_name),
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
rcode = 1;
#endif
return rcode;
}
int amp_boot_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int group_id = -1, group_count;
char *env;
int rcode = 0;
env = getenv("amp_enable");
if(!env || ( ((strcmp(env,"no") == 0) || (strcmp(env,"No") == 0) ))){
printf ("Error: Command available only when amp_enable=yes\n");
return 1;
}
/* Verify boot params prior to boot */
env = getenv("amp_verify_boot");
if((env) && ((strcmp(env,"yes") == 0) || (strcmp(env,"Yes") == 0))) {
if(amp_verify_cmd(0,0,0, argv))
return 1;
}
if(argc > 1){
group_id = simple_strtoul(argv[1], NULL, 0);
if(argc > 2){
amp_sync_boot = simple_strtoul(argv[2], NULL, 0);
}
/*amp_group_id = group_id;
do_bootd(cmdtp, flag, 1, NULL);*/
rcode = amp_boot_group(group_id);
}
else{
/* If no group id specified, boot all groups*/
env = getenv("amp_groups");
if(!env){
printf("Error: please define amp_groups\n");
return 1;
}
else{
group_count = simple_strtoul(env, NULL, 0);
if(group_count < 1){
printf("Error: please define amp_groups > 0\n");
return 1;
}
}
/* Start booting groups. keep group 0 last */
group_id = group_count;
while(group_id--)
{
/*amp_group_id = group_id;
do_bootd(cmdtp, flag, 1, NULL);*/
rcode |= amp_boot_group(group_id);
}
}
return rcode;
}
U_BOOT_CMD(
amp_boot, 3, 1, amp_boot_cmd,
"amp_boot - boot an AMP group\n",
"<group_id> \n"
"<sync_load> \n"
"\t<group_id> - which AMP group to boot. default = all. \n"
"\t<sync_load> - All image exit U-BOOT together. \n"
);
int amp_config_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int group_id, group_count;
char *env, groups[5];
env = getenv("amp_enable");
if(!env || ( ((strcmp(env,"no") == 0) || (strcmp(env,"No") == 0) ))){
printf ("Error: Command available only when amp_enable=yes\n");
return 1;
}
// Config cpu count
if (argc != 8){
printf ("Please specify all AMP parameters\n");
return 1;
}
group_id = simple_strtoul(argv[1], NULL, 0);
group_count = simple_strtoul(getenv("amp_groups"), NULL, 0);
if((group_id < 0) || (group_id > (MAX_AMP_GROUPS -1))) {
printf ("Bad group id %d specified. valid range [%d, %d]\n", group_id, 0, (MAX_AMP_GROUPS -1));
return 1;
}
if ((strcmp(argv[2], "linux") != 0) && (strcmp(argv[2], "vxworks") != 0) &&
(strcmp(argv[2], "freertos") != 0))
{
printf("Error: Specified unsupported OS %s\n", argv[2]);
return 1;
}
/* Add group to group count */
if(group_id >= group_count)
{
sprintf(groups, "%d", group_count + 1);
setenv("amp_groups", groups);
}
/* Create all env variables for group.*/
amp_create_group(group_id, argv[2], argv[3], argv[4], argv[5], argv[6], argv[7],1);
return 0;
}
U_BOOT_CMD(
amp_config, 8, 1, amp_config_cmd,
"Fully config an AMP group\n",
"<group_id> <os_type> <cpu_count> <master_cpu> <mem_base> <mem_size> <resources> \n"
"\t<group_id> - which AMP group to config\n"
"\t<os_type> - Os type to load. Accepts: linux and vxworks\n"
"\t<cpu_count> - how many cpus to use in the group. \n"
"\t<master_cpu> - cpu id of first cpu in the group. \n"
"\t<mem_base> - base physical address of group's image \n"
"\t<mem_size> - requested memory size of group's image; for LPAE may overlap with other groups \n"
"\t<resources> - resource string \n"
);
int amp_verify_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int group_count, id, test_id, i;
int cpu_count, master_cpu, mem_base, shared_base, shared_size = 0;
int mem_blk_start[MAX_AMP_GROUPS];
int base_cpu_id;
int mstr_rsrc = 0;
char *env;
char *src, *test, *token;
char tmp_rsrc[256];
env = getenv("amp_enable");
if(!env || ( ((strcmp(env,"no") == 0) || (strcmp(env,"No") == 0) ))){
printf ("Set amp_enable=yes\n");
goto failed;
}
/* Verify group count */
env = getenv("amp_groups");
if((!env)){
printf ("Set amp_groups\n");
goto failed;
}
else{
group_count = simple_strtoul(env, NULL, 0);
if(group_count < 2){
printf ("Invalid amp_groups = %d. Set amp_groups > 1 to enable AMP\n", group_count);
goto failed;
}
if(group_count > MAX_AMP_GROUPS){
printf ("amp_groups %d too large. Supporting %d groups\n",group_count, MAX_AMP_GROUPS);
goto failed;
}
}
base_cpu_id = 0;
for(id = 0; id < group_count; id++)
{
/* Verify CPU mapping */
cpu_count = simple_strtoul(amp_getenv("amp_cpu_count", id), NULL, 0);
master_cpu = simple_strtoul(amp_getenv("amp_master_cpu", id), NULL, 0);
if((master_cpu + cpu_count) > 4){ //TODO- take define here
printf ("Too many CPUs to group %d\n", id);
goto failed;
}
if((id != 0) && (master_cpu == 0)){
printf ("CPU 0 can only be part of group 0\n");
goto failed;
}
if(master_cpu < base_cpu_id){
printf ("Group %d CPUs overlap with group %d\n", id, id - 1);
goto failed;
}
base_cpu_id = master_cpu + cpu_count;
mem_base = simple_strtoul(amp_getenv("amp_mem_base", id), NULL, 16);
/* Verify Memory allocation - groups cannot have the same base addresses.
* Overlapping blocks will be automatically adjusted. */
for(i = 0; i < id; i++)
{
if(mem_base == mem_blk_start[i]) {
printf ("group %d memory (%#010x) overlaps with group %d (%#010x)\n",
id, mem_base, i, mem_blk_start[i]);
goto failed;
}
}
/* VxWorks restrictions */
env = amp_getenv("amp_os", id);
if(strcmp(env, "vxworks") == 0)
{
/* VxWorks SMP must be loaded from CPU 0 due to OS restrictions */
if(master_cpu != 0)
{
printf ("Group %d has unsupported setup. OS = VxWorks && master_cpu != 0\n", id);
goto failed;
}
/* VxWorks must have physical address space starting from 0x0 */
if(mem_base > 0)
{
printf ("Group %d has unsupported setup. OS = VxWorks && mem_base > 0x0\n", id);
goto failed;
}
}
mem_blk_start[id] = mem_base;
}
/* Verify shared memory location. Memory blocks overlapping on shared block will be adjusted. */
env = getenv("amp_shared_mem");
if(env) {
shared_base = simple_strtoul(env, &env, 16);
if (*env == ':')
shared_size = simple_strtoul(++env, NULL, 16);
if (shared_size == 0) {
printf ("Shared memory size is not configured properly.\n");
goto failed;
}
for(id = 0; id < group_count; id++)
if ((shared_base <= mem_blk_start[id]) &&
(shared_base + shared_size > mem_blk_start[id])) {
printf ("Shared memory overlaps with group %d memory.\n", id);
goto failed;
}
} else {
printf ("Shared memory base is not configured properly.\n");
goto failed;
}
/* Check that all resources are exclusive */
for(id = 0; id < group_count; id++)
{
src = amp_getenv("amp_resources", id);
/*Verify at least one group has MSTR set */
if(strstr(src, "mstr") != 0)
mstr_rsrc++;
for(test_id = id + 1; test_id < group_count; test_id++)
{
test = amp_getenv("amp_resources", test_id);
strncpy(tmp_rsrc, test, 256);
token = strtok (tmp_rsrc,":");
while (token != NULL)
{
if(strstr(src, token) != 0)
{
printf("Resource %s exists in group %d and %d\n", token, id, test_id);
goto failed;
}
token = strtok (NULL, ":");
}
}
}
if(mstr_rsrc == 0)
{
printf("No group has the master boot resource (mstr)\n");
goto failed;
}
printf("AMP Configuration test - PASSED !\n");
return 0;
failed:
printf("AMP Configuration test - FAILED !\n");
return 1;
}
U_BOOT_CMD(
amp_verify, 1, 1, amp_verify_cmd,
"Verfiy AMP configuration\n"," "
);
#endif /* CONFIG_AMP_SUPPORT */