static int __rtas_suspend_cpu(struct rtas_suspend_me_data *data, int wake_when_done) { long rc = H_SUCCESS; unsigned long msr_save; int cpu; stop_topology_update(); atomic_inc(&data->working); /* really need to ensure MSR.EE is off for H_JOIN */ msr_save = mfmsr(); mtmsr(msr_save & ~(MSR_EE)); while (rc == H_SUCCESS && !data->done) rc = plpar_hcall_norets(H_JOIN); mtmsr(msr_save); if (rc == H_SUCCESS) { /* This cpu was prodded and the suspend is complete. */ goto out; } else if (rc == H_CONTINUE) { /* All other cpus are in H_JOIN, this cpu does * the suspend. */ return __rtas_suspend_last_cpu(data, wake_when_done); } printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n", smp_processor_id(), rc); data->error = rc; if (wake_when_done) { smp_wmb(); data->done = 1; /* Ensure data->done is seen on all CPUs that are about to wake up as a result of the H_PROD below */ mb(); start_topology_update(); /* This cpu did the suspend or got an error; in either case, * we need to prod all other other cpus out of join state. * Extra prods are harmless. */ for_each_online_cpu(cpu) plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu)); } out: if (atomic_dec_return(&data->working) == 0) complete(data->complete); return rc; }
int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data) { atomic_inc(&data->working); return __rtas_suspend_last_cpu(data, 0); }