168ea0e0bSAndrew Jones /* 268ea0e0bSAndrew Jones * Secondary cpu support 368ea0e0bSAndrew Jones * 468ea0e0bSAndrew Jones * Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com> 568ea0e0bSAndrew Jones * 668ea0e0bSAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 768ea0e0bSAndrew Jones */ 868ea0e0bSAndrew Jones #include <libcflat.h> 962bdc67fSAndrew Jones #include <auxinfo.h> 1068ea0e0bSAndrew Jones #include <asm/thread_info.h> 11ff942175SAndrew Jones #include <asm/spinlock.h> 1268ea0e0bSAndrew Jones #include <asm/cpumask.h> 131742c67aSAndrew Jones #include <asm/barrier.h> 1468ea0e0bSAndrew Jones #include <asm/mmu.h> 1568ea0e0bSAndrew Jones #include <asm/psci.h> 1668ea0e0bSAndrew Jones #include <asm/smp.h> 1768ea0e0bSAndrew Jones 18ce024192SAndrew Jones bool cpu0_calls_idle; 19ce024192SAndrew Jones 2068ea0e0bSAndrew Jones cpumask_t cpu_present_mask; 2168ea0e0bSAndrew Jones cpumask_t cpu_online_mask; 229246de4cSAndrew Jones cpumask_t cpu_idle_mask; 23ff942175SAndrew Jones 24ff942175SAndrew Jones struct secondary_data { 25ff942175SAndrew Jones void *stack; /* must be first member of struct */ 26ff942175SAndrew Jones secondary_entry_fn entry; 27ff942175SAndrew Jones }; 2868ea0e0bSAndrew Jones struct secondary_data secondary_data; 29ff942175SAndrew Jones static struct spinlock lock; 3068ea0e0bSAndrew Jones 31*0df901e0SAndrew Jones /* Needed to compile with -Wmissing-prototypes */ 32*0df901e0SAndrew Jones secondary_entry_fn secondary_cinit(void); 33*0df901e0SAndrew Jones 3468ea0e0bSAndrew Jones secondary_entry_fn secondary_cinit(void) 3568ea0e0bSAndrew Jones { 3668ea0e0bSAndrew Jones struct thread_info *ti = current_thread_info(); 3768ea0e0bSAndrew Jones secondary_entry_fn entry; 3868ea0e0bSAndrew Jones 3968ea0e0bSAndrew Jones thread_info_init(ti, 0); 4062bdc67fSAndrew Jones 4162bdc67fSAndrew Jones if (!(auxinfo.flags & AUXINFO_MMU_OFF)) { 4236b50de9SAndrew Jones ti->pgtable = mmu_idmap; 431742c67aSAndrew Jones mmu_mark_enabled(ti->cpu); 4462bdc67fSAndrew Jones } 4568ea0e0bSAndrew Jones 4668ea0e0bSAndrew Jones /* 4768ea0e0bSAndrew Jones * Save secondary_data.entry locally to avoid opening a race 4868ea0e0bSAndrew Jones * window between marking ourselves online and calling it. 4968ea0e0bSAndrew Jones */ 5068ea0e0bSAndrew Jones entry = secondary_data.entry; 5168ea0e0bSAndrew Jones set_cpu_online(ti->cpu, true); 5268ea0e0bSAndrew Jones sev(); 5368ea0e0bSAndrew Jones 5468ea0e0bSAndrew Jones /* 5568ea0e0bSAndrew Jones * Return to the assembly stub, allowing entry to be called 5668ea0e0bSAndrew Jones * from there with an empty stack. 5768ea0e0bSAndrew Jones */ 5868ea0e0bSAndrew Jones return entry; 5968ea0e0bSAndrew Jones } 6068ea0e0bSAndrew Jones 61e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry) 6268ea0e0bSAndrew Jones { 6318ab6cadSAndrew Jones int ret; 6468ea0e0bSAndrew Jones 65632f935cSAndrew Jones secondary_data.stack = thread_stack_alloc(); 6668ea0e0bSAndrew Jones secondary_data.entry = entry; 671742c67aSAndrew Jones mmu_mark_disabled(cpu); 6818ab6cadSAndrew Jones ret = cpu_psci_cpu_boot(cpu); 6918ab6cadSAndrew Jones assert(ret == 0); 7068ea0e0bSAndrew Jones 7168ea0e0bSAndrew Jones while (!cpu_online(cpu)) 7268ea0e0bSAndrew Jones wfe(); 73e713fd55SAndrew Jones } 74ff942175SAndrew Jones 75e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry) 76e713fd55SAndrew Jones { 77e713fd55SAndrew Jones spin_lock(&lock); 78e713fd55SAndrew Jones assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu); 79e713fd55SAndrew Jones __smp_boot_secondary(cpu, entry); 80ff942175SAndrew Jones spin_unlock(&lock); 8168ea0e0bSAndrew Jones } 82543ce33cSAndrew Jones 83e713fd55SAndrew Jones struct on_cpu_info { 8400b34f56SAndrew Jones void (*func)(void *data); 85e713fd55SAndrew Jones void *data; 86942d182bSAndrew Jones cpumask_t waiters; 87e713fd55SAndrew Jones }; 88e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS]; 89e713fd55SAndrew Jones 9012009dbbSAndrew Jones static void __deadlock_check(int cpu, const cpumask_t *waiters, bool *found) 9112009dbbSAndrew Jones { 9212009dbbSAndrew Jones int i; 9312009dbbSAndrew Jones 9412009dbbSAndrew Jones for_each_cpu(i, waiters) { 9512009dbbSAndrew Jones if (i == cpu) { 9612009dbbSAndrew Jones printf("CPU%d", cpu); 9712009dbbSAndrew Jones *found = true; 9812009dbbSAndrew Jones return; 9912009dbbSAndrew Jones } 10012009dbbSAndrew Jones __deadlock_check(cpu, &on_cpu_info[i].waiters, found); 10112009dbbSAndrew Jones if (*found) { 10212009dbbSAndrew Jones printf(" <=> CPU%d", i); 10312009dbbSAndrew Jones return; 10412009dbbSAndrew Jones } 10512009dbbSAndrew Jones } 10612009dbbSAndrew Jones } 10712009dbbSAndrew Jones 10812009dbbSAndrew Jones static void deadlock_check(int me, int cpu) 10912009dbbSAndrew Jones { 11012009dbbSAndrew Jones bool found = false; 11112009dbbSAndrew Jones 11212009dbbSAndrew Jones __deadlock_check(cpu, &on_cpu_info[me].waiters, &found); 11312009dbbSAndrew Jones if (found) { 11412009dbbSAndrew Jones printf(" <=> CPU%d deadlock detectd\n", me); 11512009dbbSAndrew Jones assert(0); 11612009dbbSAndrew Jones } 11712009dbbSAndrew Jones } 11812009dbbSAndrew Jones 119942d182bSAndrew Jones static void cpu_wait(int cpu) 120942d182bSAndrew Jones { 121942d182bSAndrew Jones int me = smp_processor_id(); 122942d182bSAndrew Jones 123942d182bSAndrew Jones if (cpu == me) 124942d182bSAndrew Jones return; 125942d182bSAndrew Jones 126942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 12712009dbbSAndrew Jones deadlock_check(me, cpu); 128942d182bSAndrew Jones while (!cpu_idle(cpu)) 129942d182bSAndrew Jones wfe(); 130942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 131942d182bSAndrew Jones } 132942d182bSAndrew Jones 1339246de4cSAndrew Jones void do_idle(void) 134543ce33cSAndrew Jones { 1359246de4cSAndrew Jones int cpu = smp_processor_id(); 136543ce33cSAndrew Jones 137ce024192SAndrew Jones if (cpu == 0) 138ce024192SAndrew Jones cpu0_calls_idle = true; 139ce024192SAndrew Jones 1409246de4cSAndrew Jones set_cpu_idle(cpu, true); 1419246de4cSAndrew Jones sev(); 1429246de4cSAndrew Jones 1439246de4cSAndrew Jones for (;;) { 1449246de4cSAndrew Jones while (cpu_idle(cpu)) 1459246de4cSAndrew Jones wfe(); 146e713fd55SAndrew Jones smp_rmb(); 147e713fd55SAndrew Jones on_cpu_info[cpu].func(on_cpu_info[cpu].data); 148e713fd55SAndrew Jones on_cpu_info[cpu].func = NULL; 149e713fd55SAndrew Jones smp_wmb(); 1509246de4cSAndrew Jones set_cpu_idle(cpu, true); 1519246de4cSAndrew Jones sev(); 1529246de4cSAndrew Jones } 153543ce33cSAndrew Jones } 154543ce33cSAndrew Jones 155e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data) 156e713fd55SAndrew Jones { 157e713fd55SAndrew Jones if (cpu == smp_processor_id()) { 158e713fd55SAndrew Jones func(data); 159e713fd55SAndrew Jones return; 160e713fd55SAndrew Jones } 161e713fd55SAndrew Jones 162ce024192SAndrew Jones assert_msg(cpu != 0 || cpu0_calls_idle, "Waiting on CPU0, which is unlikely to idle. " 163ce024192SAndrew Jones "If this is intended set cpu0_calls_idle=1"); 164ce024192SAndrew Jones 165e713fd55SAndrew Jones spin_lock(&lock); 166e713fd55SAndrew Jones if (!cpu_online(cpu)) 167e713fd55SAndrew Jones __smp_boot_secondary(cpu, do_idle); 168e713fd55SAndrew Jones spin_unlock(&lock); 169e713fd55SAndrew Jones 170e713fd55SAndrew Jones for (;;) { 171942d182bSAndrew Jones cpu_wait(cpu); 172e713fd55SAndrew Jones spin_lock(&lock); 173e713fd55SAndrew Jones if ((volatile void *)on_cpu_info[cpu].func == NULL) 174e713fd55SAndrew Jones break; 175e713fd55SAndrew Jones spin_unlock(&lock); 176e713fd55SAndrew Jones } 177e713fd55SAndrew Jones on_cpu_info[cpu].func = func; 178e713fd55SAndrew Jones on_cpu_info[cpu].data = data; 179e713fd55SAndrew Jones spin_unlock(&lock); 180e713fd55SAndrew Jones set_cpu_idle(cpu, false); 181e713fd55SAndrew Jones sev(); 182e713fd55SAndrew Jones } 183e713fd55SAndrew Jones 184e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data) 185e713fd55SAndrew Jones { 186e713fd55SAndrew Jones on_cpu_async(cpu, func, data); 187942d182bSAndrew Jones cpu_wait(cpu); 188e713fd55SAndrew Jones } 189e713fd55SAndrew Jones 19000b34f56SAndrew Jones void on_cpus(void (*func)(void *data), void *data) 191543ce33cSAndrew Jones { 192ce024192SAndrew Jones int cpu, me = smp_processor_id(); 193543ce33cSAndrew Jones 194543ce33cSAndrew Jones for_each_present_cpu(cpu) { 195ce024192SAndrew Jones if (cpu == me) 196543ce33cSAndrew Jones continue; 19700b34f56SAndrew Jones on_cpu_async(cpu, func, data); 198543ce33cSAndrew Jones } 19900b34f56SAndrew Jones func(data); 200543ce33cSAndrew Jones 201942d182bSAndrew Jones for_each_present_cpu(cpu) { 202942d182bSAndrew Jones if (cpu == me) 203942d182bSAndrew Jones continue; 204942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 20512009dbbSAndrew Jones deadlock_check(me, cpu); 206942d182bSAndrew Jones } 207ce024192SAndrew Jones while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1) 2089246de4cSAndrew Jones wfe(); 209942d182bSAndrew Jones for_each_present_cpu(cpu) 210942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 211543ce33cSAndrew Jones } 212