blob: 27e10709ba7ec2e7b54890175b5f1dd3b2daf3df [file] [log] [blame]
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <common.h>
#include <malloc.h>
#include <asm/psci.h>
#include <cli.h>
#include <console.h>
#include <linux/linkage.h>
DECLARE_GLOBAL_DATA_PTR;
#define SECONDARY_CORE_STACKSZ (8 * 1024)
#define CPU_POWER_DOWN (1 << 16)
extern void *globl_core_array;
struct cpu_entry_arg {
void *stack_ptr;
volatile void *gd_ptr;
void *arg_ptr;
int cpu_up;
int cmd_complete;
int cmd_result;
void *stack_top_ptr;
};
extern void secondary_cpu_init(void);
extern void bring_secondary_core_down(int);
struct cpu_entry_arg core[NR_CPUS - 1];
asmlinkage void secondary_core_entry(char *argv, int *cmd_complete,
int *cmd_result)
{
unsigned int state = 0;
if (!mmu_enabled()) {
mmu_setup();
cp_delay();
}
/* Update here as ncessary - secondary entry point */
*cmd_result = cli_simple_run_command(argv, CMD_FLAG_SEC_CORE);
*cmd_complete = 1;
state = CPU_POWER_DOWN;
bring_secondary_core_down(state);
}
static void disable_console(void)
{
gd->flags |= GD_FLG_SILENT | GD_FLG_DISABLE_CONSOLE;
}
static void enable_console(void)
{
gd->flags &= ~(GD_FLG_SILENT | GD_FLG_DISABLE_CONSOLE);
}
int do_runmulticore(cmd_tbl_t *cmdtp,
int flag, int argc, char *const argv[])
{
int j;
int i;
int ret;
int delay = 0;
int core_status = 0;
int core_on_status = 0;
char *ptr = NULL;
if ((argc <= 1) || (argc > NR_CPUS + 1))
return CMD_RET_USAGE;
dcache_disable();
/* Setting up stack for secondary cores */
memset(core, 0, sizeof(core));
globl_core_array = core;
for (i = 1; i < argc; i++) {
ptr = malloc(SECONDARY_CORE_STACKSZ);
if (NULL == ptr) {
j = i - 1;
while (j >= 0) {
free(core[i - 1].stack_ptr);
j--;
}
printf("Memory allocation failure\n");
return CMD_RET_FAILURE;
}
/* 0xf0 is the padding length */
core[i - 1].stack_top_ptr = ptr;
core[i - 1].stack_ptr = (ptr + (SECONDARY_CORE_STACKSZ) - 0xf0);
core[i - 1].cpu_up = 0;
core[i - 1].cmd_complete = 0;
core[i - 1].cmd_result = -1;
core[i - 1].gd_ptr = gd;
core[i - 1].arg_ptr = argv[i];
}
if (!mmu_enabled()) {
mmu_setup();
cp_delay();
}
/* Bringing up the secondary cores */
for (i = 1; i < argc; i++) {
printf("Scheduling Core %d\n", i);
delay = 0;
disable_console();
ret = bring_sec_core_up(i, (unsigned int)secondary_cpu_init,
(unsigned int)&(core[i - 1]));
if (ret) {
panic("Some problem to getting core %d up\n", i);
}
while ((delay < 5) && (!(core[i - 1].cpu_up))) {
mdelay(1000);
delay++;
}
if (!(core[i - 1].cpu_up)) {
panic("Can't bringup core %d\n",i);
}
enable_console();
core_status |= (BIT(i - 1));
core_on_status |= (BIT(i - 1));
}
/* Waiting for secondary cores to complete the task */
while (core_status) {
for (i = 1; i < argc; i++) {
if ((core_status & (BIT(i - 1))) &&
(core[i - 1].cmd_complete)) {
printf("Command on core %d is %s\n", i,
core[i - 1].cmd_complete ?
((core[i - 1].cmd_result == -1) ?
"FAIL" : "PASS"):
"INCOMPLETE");
core_status &= (~BIT((i - 1)));
}
}
if (ctrlc()) {
run_command("reset", 0);
}
}
for (i = 1; (core_status && (i < argc)); i++) {
if (core_status & (BIT(i - 1))) {
printf("Command on core %d is %s\n", i,
core[i - 1].cmd_complete ?
((core[i - 1].cmd_result == -1) ?
"FAIL" : "PASS"): "INCOMPLETE");
}
}
/* Waiting for cores to powerdown */
delay = 0;
while (core_on_status) {
for (i = 1; i < argc; i++) {
if (core_on_status & (BIT(i - 1))) {
if (is_secondary_core_off(i) == 1) {
printf("core %d powered off\n", i);
core_on_status &= (~BIT((i - 1)));
}
}
}
mdelay(1000);
delay++;
if (delay > 5)
panic("Some cores can't be powered off\n");
}
/* Free up all the stack */
for (i = 1; i < argc; i++) {
free(core[i - 1].stack_top_ptr);
}
printf("Status:\n");
for (i = 1; i < argc; i++) {
printf("Core %d: %s\n", i,
core[i - 1].cmd_complete ?
((core[i - 1].cmd_result == -1) ?
"FAIL" : "PASS"): "INCOMPLETE");
}
invalidate_dcache_all();
dcache_enable();
return CMD_RET_SUCCESS;
}
U_BOOT_CMD(runmulticore, 4, 0, do_runmulticore,
"Enable and schedule secondary cores",
"runmulticore <\"command to core1\"> [core2 core3 ...]");