2021-01-28 07:05:24 +00:00
|
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
|
|
|
|
#include "smp.h"
|
2021-01-28 14:09:12 +00:00
|
|
|
#include "adt.h"
|
2021-09-16 15:15:52 +00:00
|
|
|
#include "cpu_regs.h"
|
2021-01-28 14:09:12 +00:00
|
|
|
#include "string.h"
|
2021-01-28 07:05:24 +00:00
|
|
|
#include "types.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
2021-01-28 14:09:12 +00:00
|
|
|
#define CPU_START_OFF 0x54000
|
2021-01-28 07:05:24 +00:00
|
|
|
|
|
|
|
struct spin_table {
|
2021-02-04 16:09:09 +00:00
|
|
|
u64 mpidr;
|
2021-01-28 07:05:24 +00:00
|
|
|
u64 flag;
|
|
|
|
u64 target;
|
2021-01-28 15:25:40 +00:00
|
|
|
u64 args[4];
|
|
|
|
u64 retval;
|
2021-01-28 07:05:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void *_reset_stack;
|
|
|
|
|
2021-09-15 12:59:50 +00:00
|
|
|
u8 secondary_stacks[MAX_CPUS][SECONDARY_STACK_SIZE] ALIGNED(0x4000);
|
2021-01-28 07:05:24 +00:00
|
|
|
|
2021-09-18 17:17:04 +00:00
|
|
|
static bool wfe_mode = false;
|
|
|
|
|
2021-01-28 15:25:40 +00:00
|
|
|
static int target_cpu;
|
|
|
|
static struct spin_table spin_table[MAX_CPUS];
|
2021-01-28 07:05:24 +00:00
|
|
|
|
|
|
|
extern u8 _vectors_start[0];
|
|
|
|
|
|
|
|
void smp_secondary_entry(void)
|
|
|
|
{
|
2021-01-28 12:59:55 +00:00
|
|
|
struct spin_table *me = &spin_table[target_cpu];
|
2021-09-15 13:00:47 +00:00
|
|
|
|
|
|
|
if (in_el2())
|
|
|
|
msr(TPIDR_EL2, target_cpu);
|
|
|
|
else
|
|
|
|
msr(TPIDR_EL1, target_cpu);
|
|
|
|
|
2021-01-28 14:47:41 +00:00
|
|
|
printf(" Index: %d (table: %p)\n\n", target_cpu, me);
|
|
|
|
|
2021-02-04 16:09:09 +00:00
|
|
|
me->mpidr = mrs(MPIDR_EL1) & 0xFFFFFF;
|
|
|
|
|
2021-01-28 12:59:55 +00:00
|
|
|
sysop("dmb sy");
|
|
|
|
me->flag = 1;
|
2021-01-28 07:05:24 +00:00
|
|
|
sysop("dmb sy");
|
|
|
|
u64 target;
|
2021-09-16 15:15:52 +00:00
|
|
|
|
2021-01-28 07:05:24 +00:00
|
|
|
while (1) {
|
2021-01-28 12:59:55 +00:00
|
|
|
while (!(target = me->target)) {
|
2021-09-18 17:17:04 +00:00
|
|
|
if (wfe_mode)
|
|
|
|
sysop("wfe");
|
|
|
|
else
|
|
|
|
deep_wfi();
|
2021-09-16 15:15:52 +00:00
|
|
|
msr(SYS_IMP_APL_IPI_SR_EL1, 1);
|
2021-01-28 07:05:24 +00:00
|
|
|
}
|
|
|
|
sysop("dmb sy");
|
2021-01-28 12:59:55 +00:00
|
|
|
me->flag++;
|
2021-01-28 07:05:24 +00:00
|
|
|
sysop("dmb sy");
|
2021-01-28 15:25:40 +00:00
|
|
|
me->retval = ((u64(*)(u64 a, u64 b, u64 c, u64 d))target)(me->args[0], me->args[1],
|
|
|
|
me->args[2], me->args[3]);
|
|
|
|
sysop("dmb sy");
|
|
|
|
me->target = 0;
|
|
|
|
sysop("dmb sy");
|
2021-01-28 07:05:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-28 14:09:12 +00:00
|
|
|
static void smp_start_cpu(int index, int cluster, int core, u64 rvbar, u64 cpu_start_base)
|
2021-01-28 07:05:24 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2021-01-28 15:25:40 +00:00
|
|
|
if (spin_table[index].flag)
|
|
|
|
return;
|
|
|
|
|
2021-01-28 07:05:24 +00:00
|
|
|
printf("Starting CPU %d (%d:%d)... ", index, cluster, core);
|
|
|
|
|
2021-01-28 15:25:40 +00:00
|
|
|
memset(&spin_table[index], 0, sizeof(struct spin_table));
|
2021-01-28 07:05:24 +00:00
|
|
|
|
|
|
|
target_cpu = index;
|
2021-01-28 14:47:41 +00:00
|
|
|
_reset_stack = secondary_stacks[index] + SECONDARY_STACK_SIZE;
|
2021-01-28 07:05:24 +00:00
|
|
|
|
|
|
|
sysop("dmb sy");
|
|
|
|
|
2021-01-28 14:09:12 +00:00
|
|
|
write64(rvbar, (u64)_vectors_start);
|
2021-01-28 07:05:24 +00:00
|
|
|
|
2021-02-03 10:18:57 +00:00
|
|
|
// Some kind of system level startup/status bit
|
|
|
|
// Without this, IRQs don't work
|
2021-11-01 12:00:26 +00:00
|
|
|
write32(cpu_start_base + 0x4, 1 << (4 * cluster + core));
|
2021-02-03 10:18:57 +00:00
|
|
|
|
|
|
|
// Actually start the core
|
2021-11-01 10:17:09 +00:00
|
|
|
write32(cpu_start_base + 0x8 + 4 * cluster, 1 << core);
|
2021-01-28 07:05:24 +00:00
|
|
|
|
|
|
|
for (i = 0; i < 500; i++) {
|
|
|
|
sysop("dmb ld");
|
|
|
|
if (spin_table[index].flag)
|
|
|
|
break;
|
|
|
|
udelay(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= 500)
|
|
|
|
printf("Failed!\n");
|
2021-04-17 07:30:18 +00:00
|
|
|
else
|
|
|
|
printf(" Started.\n");
|
2021-01-28 07:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void smp_start_secondaries(void)
|
|
|
|
{
|
|
|
|
printf("Starting secondary CPUs...\n");
|
|
|
|
|
2021-01-28 14:09:12 +00:00
|
|
|
int pmgr_path[8];
|
|
|
|
u64 pmgr_reg;
|
|
|
|
|
|
|
|
if (adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path) < 0) {
|
|
|
|
printf("Error getting /arm-io/pmgr node\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (adt_get_reg(adt, pmgr_path, "reg", 0, &pmgr_reg, NULL) < 0) {
|
|
|
|
printf("Error getting /arm-io/pmgr regs\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int node = adt_path_offset(adt, "/cpus");
|
|
|
|
if (node < 0) {
|
|
|
|
printf("Error getting /cpus node\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cpu_nodes[MAX_CPUS];
|
|
|
|
|
|
|
|
memset(cpu_nodes, 0, sizeof(cpu_nodes));
|
|
|
|
|
|
|
|
ADT_FOREACH_CHILD(adt, node)
|
|
|
|
{
|
|
|
|
u32 cpu_id;
|
|
|
|
|
|
|
|
if (ADT_GETPROP(adt, node, "cpu-id", &cpu_id) < 0)
|
|
|
|
continue;
|
|
|
|
if (cpu_id >= MAX_CPUS) {
|
|
|
|
printf("cpu-id %d exceeds max CPU count %d: increase MAX_CPUS\n", cpu_id, MAX_CPUS);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_nodes[cpu_id] = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i < MAX_CPUS; i++) {
|
|
|
|
int node = cpu_nodes[i];
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
break;
|
|
|
|
|
|
|
|
u32 reg;
|
|
|
|
u64 cpu_impl_reg[2];
|
|
|
|
if (ADT_GETPROP(adt, node, "reg", ®) < 0)
|
|
|
|
continue;
|
|
|
|
if (ADT_GETPROP_ARRAY(adt, node, "cpu-impl-reg", cpu_impl_reg) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
smp_start_cpu(i, reg >> 8, reg & 0xff, cpu_impl_reg[0], pmgr_reg + CPU_START_OFF);
|
|
|
|
}
|
2021-02-04 16:09:09 +00:00
|
|
|
|
|
|
|
spin_table[0].mpidr = mrs(MPIDR_EL1) & 0xFFFFFF;
|
2021-01-28 07:05:24 +00:00
|
|
|
}
|
2021-01-28 15:25:40 +00:00
|
|
|
|
2021-09-21 04:17:00 +00:00
|
|
|
void smp_send_ipi(int cpu)
|
|
|
|
{
|
2021-09-21 04:33:36 +00:00
|
|
|
u64 mpidr = spin_table[cpu].mpidr;
|
|
|
|
msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, (mpidr & 0xff) | ((mpidr & 0xff00) << 8));
|
2021-09-21 04:17:00 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 15:25:40 +00:00
|
|
|
void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3)
|
|
|
|
{
|
|
|
|
struct spin_table *target = &spin_table[cpu];
|
|
|
|
|
|
|
|
if (cpu == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
u64 flag = target->flag;
|
|
|
|
target->args[0] = arg0;
|
|
|
|
target->args[1] = arg1;
|
|
|
|
target->args[2] = arg2;
|
|
|
|
target->args[3] = arg3;
|
|
|
|
sysop("dmb sy");
|
|
|
|
target->target = (u64)func;
|
|
|
|
sysop("dmb sy");
|
2021-09-16 15:15:52 +00:00
|
|
|
|
2021-11-08 10:57:02 +00:00
|
|
|
if (wfe_mode)
|
|
|
|
sysop("sev");
|
|
|
|
else
|
|
|
|
smp_send_ipi(cpu);
|
2021-09-16 15:15:52 +00:00
|
|
|
|
2021-01-28 15:25:40 +00:00
|
|
|
while (target->flag == flag)
|
|
|
|
sysop("dmb sy");
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 smp_wait(int cpu)
|
|
|
|
{
|
|
|
|
struct spin_table *target = &spin_table[cpu];
|
|
|
|
|
|
|
|
while (target->target)
|
|
|
|
sysop("dmb sy");
|
|
|
|
|
|
|
|
return target->retval;
|
|
|
|
}
|
2021-02-04 16:09:09 +00:00
|
|
|
|
2021-09-18 17:17:04 +00:00
|
|
|
void smp_set_wfe_mode(bool new_mode)
|
|
|
|
{
|
|
|
|
wfe_mode = new_mode;
|
|
|
|
sysop("dmb sy");
|
|
|
|
|
|
|
|
for (int cpu = 1; cpu < MAX_CPUS; cpu++)
|
|
|
|
if (smp_is_alive(cpu))
|
2021-09-21 04:17:00 +00:00
|
|
|
smp_send_ipi(cpu);
|
2021-09-18 17:17:04 +00:00
|
|
|
|
|
|
|
sysop("sev");
|
|
|
|
}
|
|
|
|
|
2021-05-03 12:47:57 +00:00
|
|
|
bool smp_is_alive(int cpu)
|
|
|
|
{
|
|
|
|
return spin_table[cpu].flag;
|
|
|
|
}
|
|
|
|
|
2021-09-21 04:33:36 +00:00
|
|
|
uint64_t smp_get_mpidr(int cpu)
|
2021-02-04 16:09:09 +00:00
|
|
|
{
|
|
|
|
return spin_table[cpu].mpidr;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 smp_get_release_addr(int cpu)
|
|
|
|
{
|
|
|
|
struct spin_table *target = &spin_table[cpu];
|
|
|
|
|
|
|
|
target->args[0] = 0;
|
|
|
|
target->args[1] = 0;
|
|
|
|
target->args[2] = 0;
|
|
|
|
target->args[3] = 0;
|
|
|
|
return (u64)&target->target;
|
|
|
|
}
|