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> 10dfc1fec2SAndrew Jones #include <cpumask.h> 1168ea0e0bSAndrew Jones #include <asm/thread_info.h> 12ff942175SAndrew Jones #include <asm/spinlock.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 2468ea0e0bSAndrew Jones struct secondary_data secondary_data; 25ff942175SAndrew Jones static struct spinlock lock; 2668ea0e0bSAndrew Jones 270df901e0SAndrew Jones /* Needed to compile with -Wmissing-prototypes */ 280df901e0SAndrew Jones secondary_entry_fn secondary_cinit(void); 290df901e0SAndrew Jones 3068ea0e0bSAndrew Jones secondary_entry_fn secondary_cinit(void) 3168ea0e0bSAndrew Jones { 3268ea0e0bSAndrew Jones struct thread_info *ti = current_thread_info(); 3368ea0e0bSAndrew Jones secondary_entry_fn entry; 3468ea0e0bSAndrew Jones 3568ea0e0bSAndrew Jones thread_info_init(ti, 0); 3662bdc67fSAndrew Jones 3762bdc67fSAndrew Jones if (!(auxinfo.flags & AUXINFO_MMU_OFF)) { 3836b50de9SAndrew Jones ti->pgtable = mmu_idmap; 391742c67aSAndrew Jones mmu_mark_enabled(ti->cpu); 4062bdc67fSAndrew Jones } 4168ea0e0bSAndrew Jones 4268ea0e0bSAndrew Jones /* 4368ea0e0bSAndrew Jones * Save secondary_data.entry locally to avoid opening a race 4468ea0e0bSAndrew Jones * window between marking ourselves online and calling it. 4568ea0e0bSAndrew Jones */ 4668ea0e0bSAndrew Jones entry = secondary_data.entry; 4768ea0e0bSAndrew Jones set_cpu_online(ti->cpu, true); 4805a76472SAndrew Jones smp_send_event(); 4968ea0e0bSAndrew Jones 5068ea0e0bSAndrew Jones /* 5168ea0e0bSAndrew Jones * Return to the assembly stub, allowing entry to be called 5268ea0e0bSAndrew Jones * from there with an empty stack. 5368ea0e0bSAndrew Jones */ 5468ea0e0bSAndrew Jones return entry; 5568ea0e0bSAndrew Jones } 5668ea0e0bSAndrew Jones 57e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry) 5868ea0e0bSAndrew Jones { 5918ab6cadSAndrew Jones int ret; 6068ea0e0bSAndrew Jones 61632f935cSAndrew Jones secondary_data.stack = thread_stack_alloc(); 6268ea0e0bSAndrew Jones secondary_data.entry = entry; 631742c67aSAndrew Jones mmu_mark_disabled(cpu); 6418ab6cadSAndrew Jones ret = cpu_psci_cpu_boot(cpu); 6518ab6cadSAndrew Jones assert(ret == 0); 6668ea0e0bSAndrew Jones 6768ea0e0bSAndrew Jones while (!cpu_online(cpu)) 6805a76472SAndrew Jones smp_wait_for_event(); 69e713fd55SAndrew Jones } 70ff942175SAndrew Jones 71e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry) 72e713fd55SAndrew Jones { 73e713fd55SAndrew Jones spin_lock(&lock); 74e713fd55SAndrew Jones assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu); 75e713fd55SAndrew Jones __smp_boot_secondary(cpu, entry); 76ff942175SAndrew Jones spin_unlock(&lock); 7768ea0e0bSAndrew Jones } 78543ce33cSAndrew Jones 79*01855004SAndrew Jones void smp_boot_secondary_nofail(int cpu, secondary_entry_fn entry) 80*01855004SAndrew Jones { 81*01855004SAndrew Jones spin_lock(&lock); 82*01855004SAndrew Jones if (!cpu_online(cpu)) 83*01855004SAndrew Jones __smp_boot_secondary(cpu, entry); 84*01855004SAndrew Jones spin_unlock(&lock); 85*01855004SAndrew Jones } 86*01855004SAndrew Jones 87e713fd55SAndrew Jones struct on_cpu_info { 8800b34f56SAndrew Jones void (*func)(void *data); 89e713fd55SAndrew Jones void *data; 90942d182bSAndrew Jones cpumask_t waiters; 91e713fd55SAndrew Jones }; 92e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS]; 93*01855004SAndrew Jones static cpumask_t on_cpu_info_lock; 94*01855004SAndrew Jones 95*01855004SAndrew Jones static bool get_on_cpu_info(int cpu) 96*01855004SAndrew Jones { 97*01855004SAndrew Jones return !cpumask_test_and_set_cpu(cpu, &on_cpu_info_lock); 98*01855004SAndrew Jones } 99*01855004SAndrew Jones 100*01855004SAndrew Jones static void put_on_cpu_info(int cpu) 101*01855004SAndrew Jones { 102*01855004SAndrew Jones int ret = cpumask_test_and_clear_cpu(cpu, &on_cpu_info_lock); 103*01855004SAndrew Jones assert(ret); 104*01855004SAndrew Jones } 105e713fd55SAndrew Jones 10612009dbbSAndrew Jones static void __deadlock_check(int cpu, const cpumask_t *waiters, bool *found) 10712009dbbSAndrew Jones { 10812009dbbSAndrew Jones int i; 10912009dbbSAndrew Jones 11012009dbbSAndrew Jones for_each_cpu(i, waiters) { 11112009dbbSAndrew Jones if (i == cpu) { 11212009dbbSAndrew Jones printf("CPU%d", cpu); 11312009dbbSAndrew Jones *found = true; 11412009dbbSAndrew Jones return; 11512009dbbSAndrew Jones } 11612009dbbSAndrew Jones __deadlock_check(cpu, &on_cpu_info[i].waiters, found); 11712009dbbSAndrew Jones if (*found) { 11812009dbbSAndrew Jones printf(" <=> CPU%d", i); 11912009dbbSAndrew Jones return; 12012009dbbSAndrew Jones } 12112009dbbSAndrew Jones } 12212009dbbSAndrew Jones } 12312009dbbSAndrew Jones 12412009dbbSAndrew Jones static void deadlock_check(int me, int cpu) 12512009dbbSAndrew Jones { 12612009dbbSAndrew Jones bool found = false; 12712009dbbSAndrew Jones 12812009dbbSAndrew Jones __deadlock_check(cpu, &on_cpu_info[me].waiters, &found); 12912009dbbSAndrew Jones if (found) { 13012009dbbSAndrew Jones printf(" <=> CPU%d deadlock detectd\n", me); 13112009dbbSAndrew Jones assert(0); 13212009dbbSAndrew Jones } 13312009dbbSAndrew Jones } 13412009dbbSAndrew Jones 135942d182bSAndrew Jones static void cpu_wait(int cpu) 136942d182bSAndrew Jones { 137942d182bSAndrew Jones int me = smp_processor_id(); 138942d182bSAndrew Jones 139942d182bSAndrew Jones if (cpu == me) 140942d182bSAndrew Jones return; 141942d182bSAndrew Jones 142942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 14312009dbbSAndrew Jones deadlock_check(me, cpu); 144942d182bSAndrew Jones while (!cpu_idle(cpu)) 14505a76472SAndrew Jones smp_wait_for_event(); 146942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 147942d182bSAndrew Jones } 148942d182bSAndrew Jones 1499246de4cSAndrew Jones void do_idle(void) 150543ce33cSAndrew Jones { 1519246de4cSAndrew Jones int cpu = smp_processor_id(); 152543ce33cSAndrew Jones 153ce024192SAndrew Jones if (cpu == 0) 154ce024192SAndrew Jones cpu0_calls_idle = true; 155ce024192SAndrew Jones 1569246de4cSAndrew Jones set_cpu_idle(cpu, true); 15705a76472SAndrew Jones smp_send_event(); 1589246de4cSAndrew Jones 1599246de4cSAndrew Jones for (;;) { 1609246de4cSAndrew Jones while (cpu_idle(cpu)) 16105a76472SAndrew Jones smp_wait_for_event(); 162e713fd55SAndrew Jones smp_rmb(); 163e713fd55SAndrew Jones on_cpu_info[cpu].func(on_cpu_info[cpu].data); 164e713fd55SAndrew Jones on_cpu_info[cpu].func = NULL; 165e713fd55SAndrew Jones smp_wmb(); 1669246de4cSAndrew Jones set_cpu_idle(cpu, true); 16705a76472SAndrew Jones smp_send_event(); 1689246de4cSAndrew Jones } 169543ce33cSAndrew Jones } 170543ce33cSAndrew Jones 171e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data) 172e713fd55SAndrew Jones { 173e713fd55SAndrew Jones if (cpu == smp_processor_id()) { 174e713fd55SAndrew Jones func(data); 175e713fd55SAndrew Jones return; 176e713fd55SAndrew Jones } 177e713fd55SAndrew Jones 178ce024192SAndrew Jones assert_msg(cpu != 0 || cpu0_calls_idle, "Waiting on CPU0, which is unlikely to idle. " 179ce024192SAndrew Jones "If this is intended set cpu0_calls_idle=1"); 180ce024192SAndrew Jones 181*01855004SAndrew Jones smp_boot_secondary_nofail(cpu, do_idle); 182e713fd55SAndrew Jones 183e713fd55SAndrew Jones for (;;) { 184942d182bSAndrew Jones cpu_wait(cpu); 185*01855004SAndrew Jones if (get_on_cpu_info(cpu)) { 186e713fd55SAndrew Jones if ((volatile void *)on_cpu_info[cpu].func == NULL) 187e713fd55SAndrew Jones break; 188*01855004SAndrew Jones put_on_cpu_info(cpu); 189e713fd55SAndrew Jones } 190*01855004SAndrew Jones } 191*01855004SAndrew Jones 192e713fd55SAndrew Jones on_cpu_info[cpu].func = func; 193e713fd55SAndrew Jones on_cpu_info[cpu].data = data; 194e713fd55SAndrew Jones set_cpu_idle(cpu, false); 195*01855004SAndrew Jones put_on_cpu_info(cpu); 19605a76472SAndrew Jones smp_send_event(); 197e713fd55SAndrew Jones } 198e713fd55SAndrew Jones 199e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data) 200e713fd55SAndrew Jones { 201e713fd55SAndrew Jones on_cpu_async(cpu, func, data); 202942d182bSAndrew Jones cpu_wait(cpu); 203e713fd55SAndrew Jones } 204e713fd55SAndrew Jones 20500b34f56SAndrew Jones void on_cpus(void (*func)(void *data), void *data) 206543ce33cSAndrew Jones { 207ce024192SAndrew Jones int cpu, me = smp_processor_id(); 208543ce33cSAndrew Jones 209543ce33cSAndrew Jones for_each_present_cpu(cpu) { 210ce024192SAndrew Jones if (cpu == me) 211543ce33cSAndrew Jones continue; 21200b34f56SAndrew Jones on_cpu_async(cpu, func, data); 213543ce33cSAndrew Jones } 21400b34f56SAndrew Jones func(data); 215543ce33cSAndrew Jones 216942d182bSAndrew Jones for_each_present_cpu(cpu) { 217942d182bSAndrew Jones if (cpu == me) 218942d182bSAndrew Jones continue; 219942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 22012009dbbSAndrew Jones deadlock_check(me, cpu); 221942d182bSAndrew Jones } 222ce024192SAndrew Jones while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1) 22305a76472SAndrew Jones smp_wait_for_event(); 224942d182bSAndrew Jones for_each_present_cpu(cpu) 225942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 226543ce33cSAndrew Jones } 227