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> 968ea0e0bSAndrew Jones #include <asm/thread_info.h> 10ff942175SAndrew Jones #include <asm/spinlock.h> 1168ea0e0bSAndrew Jones #include <asm/cpumask.h> 121742c67aSAndrew Jones #include <asm/barrier.h> 1368ea0e0bSAndrew Jones #include <asm/mmu.h> 1468ea0e0bSAndrew Jones #include <asm/psci.h> 1568ea0e0bSAndrew Jones #include <asm/smp.h> 1668ea0e0bSAndrew Jones 17ce024192SAndrew Jones bool cpu0_calls_idle; 18ce024192SAndrew Jones 1968ea0e0bSAndrew Jones cpumask_t cpu_present_mask; 2068ea0e0bSAndrew Jones cpumask_t cpu_online_mask; 219246de4cSAndrew Jones cpumask_t cpu_idle_mask; 22ff942175SAndrew Jones 23ff942175SAndrew Jones struct secondary_data { 24ff942175SAndrew Jones void *stack; /* must be first member of struct */ 25ff942175SAndrew Jones secondary_entry_fn entry; 26ff942175SAndrew Jones }; 2768ea0e0bSAndrew Jones struct secondary_data secondary_data; 28ff942175SAndrew Jones static struct spinlock lock; 2968ea0e0bSAndrew 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); 361742c67aSAndrew Jones mmu_mark_enabled(ti->cpu); 3768ea0e0bSAndrew Jones 3868ea0e0bSAndrew Jones /* 3968ea0e0bSAndrew Jones * Save secondary_data.entry locally to avoid opening a race 4068ea0e0bSAndrew Jones * window between marking ourselves online and calling it. 4168ea0e0bSAndrew Jones */ 4268ea0e0bSAndrew Jones entry = secondary_data.entry; 4368ea0e0bSAndrew Jones set_cpu_online(ti->cpu, true); 4468ea0e0bSAndrew Jones sev(); 4568ea0e0bSAndrew Jones 4668ea0e0bSAndrew Jones /* 4768ea0e0bSAndrew Jones * Return to the assembly stub, allowing entry to be called 4868ea0e0bSAndrew Jones * from there with an empty stack. 4968ea0e0bSAndrew Jones */ 5068ea0e0bSAndrew Jones return entry; 5168ea0e0bSAndrew Jones } 5268ea0e0bSAndrew Jones 53e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry) 5468ea0e0bSAndrew Jones { 5518ab6cadSAndrew Jones int ret; 5668ea0e0bSAndrew Jones 57632f935cSAndrew Jones secondary_data.stack = thread_stack_alloc(); 5868ea0e0bSAndrew Jones secondary_data.entry = entry; 591742c67aSAndrew Jones mmu_mark_disabled(cpu); 6018ab6cadSAndrew Jones ret = cpu_psci_cpu_boot(cpu); 6118ab6cadSAndrew Jones assert(ret == 0); 6268ea0e0bSAndrew Jones 6368ea0e0bSAndrew Jones while (!cpu_online(cpu)) 6468ea0e0bSAndrew Jones wfe(); 65e713fd55SAndrew Jones } 66ff942175SAndrew Jones 67e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry) 68e713fd55SAndrew Jones { 69e713fd55SAndrew Jones spin_lock(&lock); 70e713fd55SAndrew Jones assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu); 71e713fd55SAndrew Jones __smp_boot_secondary(cpu, entry); 72ff942175SAndrew Jones spin_unlock(&lock); 7368ea0e0bSAndrew Jones } 74543ce33cSAndrew Jones 75e713fd55SAndrew Jones typedef void (*on_cpu_func)(void *); 76e713fd55SAndrew Jones struct on_cpu_info { 77e713fd55SAndrew Jones on_cpu_func func; 78e713fd55SAndrew Jones void *data; 79*942d182bSAndrew Jones cpumask_t waiters; 80e713fd55SAndrew Jones }; 81e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS]; 82e713fd55SAndrew Jones 83*942d182bSAndrew Jones static void cpu_wait(int cpu) 84*942d182bSAndrew Jones { 85*942d182bSAndrew Jones int me = smp_processor_id(); 86*942d182bSAndrew Jones 87*942d182bSAndrew Jones if (cpu == me) 88*942d182bSAndrew Jones return; 89*942d182bSAndrew Jones 90*942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 91*942d182bSAndrew Jones assert_msg(!cpumask_test_cpu(cpu, &on_cpu_info[me].waiters), "CPU%d <=> CPU%d deadlock detected", me, cpu); 92*942d182bSAndrew Jones while (!cpu_idle(cpu)) 93*942d182bSAndrew Jones wfe(); 94*942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 95*942d182bSAndrew Jones } 96*942d182bSAndrew Jones 979246de4cSAndrew Jones void do_idle(void) 98543ce33cSAndrew Jones { 999246de4cSAndrew Jones int cpu = smp_processor_id(); 100543ce33cSAndrew Jones 101ce024192SAndrew Jones if (cpu == 0) 102ce024192SAndrew Jones cpu0_calls_idle = true; 103ce024192SAndrew Jones 1049246de4cSAndrew Jones set_cpu_idle(cpu, true); 1059246de4cSAndrew Jones sev(); 1069246de4cSAndrew Jones 1079246de4cSAndrew Jones for (;;) { 1089246de4cSAndrew Jones while (cpu_idle(cpu)) 1099246de4cSAndrew Jones wfe(); 110e713fd55SAndrew Jones smp_rmb(); 111e713fd55SAndrew Jones on_cpu_info[cpu].func(on_cpu_info[cpu].data); 112e713fd55SAndrew Jones on_cpu_info[cpu].func = NULL; 113e713fd55SAndrew Jones smp_wmb(); 1149246de4cSAndrew Jones set_cpu_idle(cpu, true); 1159246de4cSAndrew Jones sev(); 1169246de4cSAndrew Jones } 117543ce33cSAndrew Jones } 118543ce33cSAndrew Jones 119e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data) 120e713fd55SAndrew Jones { 121e713fd55SAndrew Jones if (cpu == smp_processor_id()) { 122e713fd55SAndrew Jones func(data); 123e713fd55SAndrew Jones return; 124e713fd55SAndrew Jones } 125e713fd55SAndrew Jones 126ce024192SAndrew Jones assert_msg(cpu != 0 || cpu0_calls_idle, "Waiting on CPU0, which is unlikely to idle. " 127ce024192SAndrew Jones "If this is intended set cpu0_calls_idle=1"); 128ce024192SAndrew Jones 129e713fd55SAndrew Jones spin_lock(&lock); 130e713fd55SAndrew Jones if (!cpu_online(cpu)) 131e713fd55SAndrew Jones __smp_boot_secondary(cpu, do_idle); 132e713fd55SAndrew Jones spin_unlock(&lock); 133e713fd55SAndrew Jones 134e713fd55SAndrew Jones for (;;) { 135*942d182bSAndrew Jones cpu_wait(cpu); 136e713fd55SAndrew Jones spin_lock(&lock); 137e713fd55SAndrew Jones if ((volatile void *)on_cpu_info[cpu].func == NULL) 138e713fd55SAndrew Jones break; 139e713fd55SAndrew Jones spin_unlock(&lock); 140e713fd55SAndrew Jones } 141e713fd55SAndrew Jones on_cpu_info[cpu].func = func; 142e713fd55SAndrew Jones on_cpu_info[cpu].data = data; 143e713fd55SAndrew Jones spin_unlock(&lock); 144e713fd55SAndrew Jones set_cpu_idle(cpu, false); 145e713fd55SAndrew Jones sev(); 146e713fd55SAndrew Jones } 147e713fd55SAndrew Jones 148e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data) 149e713fd55SAndrew Jones { 150e713fd55SAndrew Jones on_cpu_async(cpu, func, data); 151*942d182bSAndrew Jones cpu_wait(cpu); 152e713fd55SAndrew Jones } 153e713fd55SAndrew Jones 1546f447945SAndrew Jones void on_cpus(void (*func)(void)) 155543ce33cSAndrew Jones { 156ce024192SAndrew Jones int cpu, me = smp_processor_id(); 157543ce33cSAndrew Jones 158543ce33cSAndrew Jones for_each_present_cpu(cpu) { 159ce024192SAndrew Jones if (cpu == me) 160543ce33cSAndrew Jones continue; 1616f447945SAndrew Jones on_cpu_async(cpu, (on_cpu_func)func, NULL); 162543ce33cSAndrew Jones } 163543ce33cSAndrew Jones func(); 164543ce33cSAndrew Jones 165*942d182bSAndrew Jones for_each_present_cpu(cpu) { 166*942d182bSAndrew Jones if (cpu == me) 167*942d182bSAndrew Jones continue; 168*942d182bSAndrew Jones cpumask_set_cpu(me, &on_cpu_info[cpu].waiters); 169*942d182bSAndrew Jones assert_msg(!cpumask_test_cpu(cpu, &on_cpu_info[me].waiters), "CPU%d <=> CPU%d deadlock detected", me, cpu); 170*942d182bSAndrew Jones } 171ce024192SAndrew Jones while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1) 1729246de4cSAndrew Jones wfe(); 173*942d182bSAndrew Jones for_each_present_cpu(cpu) 174*942d182bSAndrew Jones cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters); 175543ce33cSAndrew Jones } 176