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 1768ea0e0bSAndrew Jones cpumask_t cpu_present_mask; 1868ea0e0bSAndrew Jones cpumask_t cpu_online_mask; 199246de4cSAndrew Jones cpumask_t cpu_idle_mask; 20ff942175SAndrew Jones 21ff942175SAndrew Jones struct secondary_data { 22ff942175SAndrew Jones void *stack; /* must be first member of struct */ 23ff942175SAndrew Jones secondary_entry_fn entry; 24ff942175SAndrew Jones }; 2568ea0e0bSAndrew Jones struct secondary_data secondary_data; 26ff942175SAndrew Jones static struct spinlock lock; 2768ea0e0bSAndrew Jones 2868ea0e0bSAndrew Jones secondary_entry_fn secondary_cinit(void) 2968ea0e0bSAndrew Jones { 3068ea0e0bSAndrew Jones struct thread_info *ti = current_thread_info(); 3168ea0e0bSAndrew Jones secondary_entry_fn entry; 3268ea0e0bSAndrew Jones 3368ea0e0bSAndrew Jones thread_info_init(ti, 0); 341742c67aSAndrew Jones mmu_mark_enabled(ti->cpu); 3568ea0e0bSAndrew Jones 3668ea0e0bSAndrew Jones /* 3768ea0e0bSAndrew Jones * Save secondary_data.entry locally to avoid opening a race 3868ea0e0bSAndrew Jones * window between marking ourselves online and calling it. 3968ea0e0bSAndrew Jones */ 4068ea0e0bSAndrew Jones entry = secondary_data.entry; 4168ea0e0bSAndrew Jones set_cpu_online(ti->cpu, true); 4268ea0e0bSAndrew Jones sev(); 4368ea0e0bSAndrew Jones 4468ea0e0bSAndrew Jones /* 4568ea0e0bSAndrew Jones * Return to the assembly stub, allowing entry to be called 4668ea0e0bSAndrew Jones * from there with an empty stack. 4768ea0e0bSAndrew Jones */ 4868ea0e0bSAndrew Jones return entry; 4968ea0e0bSAndrew Jones } 5068ea0e0bSAndrew Jones 51e713fd55SAndrew Jones static void __smp_boot_secondary(int cpu, secondary_entry_fn entry) 5268ea0e0bSAndrew Jones { 5318ab6cadSAndrew Jones int ret; 5468ea0e0bSAndrew Jones 55632f935cSAndrew Jones secondary_data.stack = thread_stack_alloc(); 5668ea0e0bSAndrew Jones secondary_data.entry = entry; 571742c67aSAndrew Jones mmu_mark_disabled(cpu); 5818ab6cadSAndrew Jones ret = cpu_psci_cpu_boot(cpu); 5918ab6cadSAndrew Jones assert(ret == 0); 6068ea0e0bSAndrew Jones 6168ea0e0bSAndrew Jones while (!cpu_online(cpu)) 6268ea0e0bSAndrew Jones wfe(); 63e713fd55SAndrew Jones } 64ff942175SAndrew Jones 65e713fd55SAndrew Jones void smp_boot_secondary(int cpu, secondary_entry_fn entry) 66e713fd55SAndrew Jones { 67e713fd55SAndrew Jones spin_lock(&lock); 68e713fd55SAndrew Jones assert_msg(!cpu_online(cpu), "CPU%d already boot once", cpu); 69e713fd55SAndrew Jones __smp_boot_secondary(cpu, entry); 70ff942175SAndrew Jones spin_unlock(&lock); 7168ea0e0bSAndrew Jones } 72543ce33cSAndrew Jones 73e713fd55SAndrew Jones typedef void (*on_cpu_func)(void *); 74e713fd55SAndrew Jones struct on_cpu_info { 75e713fd55SAndrew Jones on_cpu_func func; 76e713fd55SAndrew Jones void *data; 77e713fd55SAndrew Jones }; 78e713fd55SAndrew Jones static struct on_cpu_info on_cpu_info[NR_CPUS]; 79e713fd55SAndrew Jones 809246de4cSAndrew Jones void do_idle(void) 81543ce33cSAndrew Jones { 829246de4cSAndrew Jones int cpu = smp_processor_id(); 83543ce33cSAndrew Jones 849246de4cSAndrew Jones set_cpu_idle(cpu, true); 859246de4cSAndrew Jones sev(); 869246de4cSAndrew Jones 879246de4cSAndrew Jones for (;;) { 889246de4cSAndrew Jones while (cpu_idle(cpu)) 899246de4cSAndrew Jones wfe(); 90e713fd55SAndrew Jones smp_rmb(); 91e713fd55SAndrew Jones on_cpu_info[cpu].func(on_cpu_info[cpu].data); 92e713fd55SAndrew Jones on_cpu_info[cpu].func = NULL; 93e713fd55SAndrew Jones smp_wmb(); 949246de4cSAndrew Jones set_cpu_idle(cpu, true); 959246de4cSAndrew Jones sev(); 969246de4cSAndrew Jones } 97543ce33cSAndrew Jones } 98543ce33cSAndrew Jones 99e713fd55SAndrew Jones void on_cpu_async(int cpu, void (*func)(void *data), void *data) 100e713fd55SAndrew Jones { 101e713fd55SAndrew Jones if (cpu == smp_processor_id()) { 102e713fd55SAndrew Jones func(data); 103e713fd55SAndrew Jones return; 104e713fd55SAndrew Jones } 105e713fd55SAndrew Jones 106e713fd55SAndrew Jones spin_lock(&lock); 107e713fd55SAndrew Jones if (!cpu_online(cpu)) 108e713fd55SAndrew Jones __smp_boot_secondary(cpu, do_idle); 109e713fd55SAndrew Jones spin_unlock(&lock); 110e713fd55SAndrew Jones 111e713fd55SAndrew Jones for (;;) { 112e713fd55SAndrew Jones while (!cpu_idle(cpu)) 113e713fd55SAndrew Jones wfe(); 114e713fd55SAndrew Jones spin_lock(&lock); 115e713fd55SAndrew Jones if ((volatile void *)on_cpu_info[cpu].func == NULL) 116e713fd55SAndrew Jones break; 117e713fd55SAndrew Jones spin_unlock(&lock); 118e713fd55SAndrew Jones } 119e713fd55SAndrew Jones on_cpu_info[cpu].func = func; 120e713fd55SAndrew Jones on_cpu_info[cpu].data = data; 121e713fd55SAndrew Jones spin_unlock(&lock); 122e713fd55SAndrew Jones set_cpu_idle(cpu, false); 123e713fd55SAndrew Jones sev(); 124e713fd55SAndrew Jones } 125e713fd55SAndrew Jones 126e713fd55SAndrew Jones void on_cpu(int cpu, void (*func)(void *data), void *data) 127e713fd55SAndrew Jones { 128e713fd55SAndrew Jones on_cpu_async(cpu, func, data); 129e713fd55SAndrew Jones 130e713fd55SAndrew Jones while (!cpu_idle(cpu)) 131e713fd55SAndrew Jones wfe(); 132e713fd55SAndrew Jones } 133e713fd55SAndrew Jones 134*6f447945SAndrew Jones void on_cpus(void (*func)(void)) 135543ce33cSAndrew Jones { 136543ce33cSAndrew Jones int cpu; 137543ce33cSAndrew Jones 138543ce33cSAndrew Jones for_each_present_cpu(cpu) { 139543ce33cSAndrew Jones if (cpu == 0) 140543ce33cSAndrew Jones continue; 141*6f447945SAndrew Jones on_cpu_async(cpu, (on_cpu_func)func, NULL); 142543ce33cSAndrew Jones } 143543ce33cSAndrew Jones func(); 144543ce33cSAndrew Jones 1459246de4cSAndrew Jones set_cpu_idle(0, true); 1469246de4cSAndrew Jones while (!cpumask_full(&cpu_idle_mask)) 1479246de4cSAndrew Jones wfe(); 1489246de4cSAndrew Jones set_cpu_idle(0, false); 149543ce33cSAndrew Jones } 150