diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
-rw-r--r-- | arch/powerpc/kernel/rtas.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 1fd6e7b2f390..52add6f3e201 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/capability.h> | 20 | #include <linux/capability.h> |
21 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
22 | #include <linux/cpu.h> | ||
22 | #include <linux/smp.h> | 23 | #include <linux/smp.h> |
23 | #include <linux/completion.h> | 24 | #include <linux/completion.h> |
24 | #include <linux/cpumask.h> | 25 | #include <linux/cpumask.h> |
@@ -807,6 +808,95 @@ static void rtas_percpu_suspend_me(void *info) | |||
807 | __rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1); | 808 | __rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1); |
808 | } | 809 | } |
809 | 810 | ||
811 | enum rtas_cpu_state { | ||
812 | DOWN, | ||
813 | UP, | ||
814 | }; | ||
815 | |||
816 | #ifndef CONFIG_SMP | ||
817 | static int rtas_cpu_state_change_mask(enum rtas_cpu_state state, | ||
818 | cpumask_var_t cpus) | ||
819 | { | ||
820 | if (!cpumask_empty(cpus)) { | ||
821 | cpumask_clear(cpus); | ||
822 | return -EINVAL; | ||
823 | } else | ||
824 | return 0; | ||
825 | } | ||
826 | #else | ||
827 | /* On return cpumask will be altered to indicate CPUs changed. | ||
828 | * CPUs with states changed will be set in the mask, | ||
829 | * CPUs with status unchanged will be unset in the mask. */ | ||
830 | static int rtas_cpu_state_change_mask(enum rtas_cpu_state state, | ||
831 | cpumask_var_t cpus) | ||
832 | { | ||
833 | int cpu; | ||
834 | int cpuret = 0; | ||
835 | int ret = 0; | ||
836 | |||
837 | if (cpumask_empty(cpus)) | ||
838 | return 0; | ||
839 | |||
840 | for_each_cpu(cpu, cpus) { | ||
841 | switch (state) { | ||
842 | case DOWN: | ||
843 | cpuret = cpu_down(cpu); | ||
844 | break; | ||
845 | case UP: | ||
846 | cpuret = cpu_up(cpu); | ||
847 | break; | ||
848 | } | ||
849 | if (cpuret) { | ||
850 | pr_debug("%s: cpu_%s for cpu#%d returned %d.\n", | ||
851 | __func__, | ||
852 | ((state == UP) ? "up" : "down"), | ||
853 | cpu, cpuret); | ||
854 | if (!ret) | ||
855 | ret = cpuret; | ||
856 | if (state == UP) { | ||
857 | /* clear bits for unchanged cpus, return */ | ||
858 | cpumask_shift_right(cpus, cpus, cpu); | ||
859 | cpumask_shift_left(cpus, cpus, cpu); | ||
860 | break; | ||
861 | } else { | ||
862 | /* clear bit for unchanged cpu, continue */ | ||
863 | cpumask_clear_cpu(cpu, cpus); | ||
864 | } | ||
865 | } | ||
866 | } | ||
867 | |||
868 | return ret; | ||
869 | } | ||
870 | #endif | ||
871 | |||
872 | int rtas_online_cpus_mask(cpumask_var_t cpus) | ||
873 | { | ||
874 | int ret; | ||
875 | |||
876 | ret = rtas_cpu_state_change_mask(UP, cpus); | ||
877 | |||
878 | if (ret) { | ||
879 | cpumask_var_t tmp_mask; | ||
880 | |||
881 | if (!alloc_cpumask_var(&tmp_mask, GFP_TEMPORARY)) | ||
882 | return ret; | ||
883 | |||
884 | /* Use tmp_mask to preserve cpus mask from first failure */ | ||
885 | cpumask_copy(tmp_mask, cpus); | ||
886 | rtas_offline_cpus_mask(tmp_mask); | ||
887 | free_cpumask_var(tmp_mask); | ||
888 | } | ||
889 | |||
890 | return ret; | ||
891 | } | ||
892 | EXPORT_SYMBOL(rtas_online_cpus_mask); | ||
893 | |||
894 | int rtas_offline_cpus_mask(cpumask_var_t cpus) | ||
895 | { | ||
896 | return rtas_cpu_state_change_mask(DOWN, cpus); | ||
897 | } | ||
898 | EXPORT_SYMBOL(rtas_offline_cpus_mask); | ||
899 | |||
810 | int rtas_ibm_suspend_me(struct rtas_args *args) | 900 | int rtas_ibm_suspend_me(struct rtas_args *args) |
811 | { | 901 | { |
812 | long state; | 902 | long state; |
@@ -814,6 +904,8 @@ int rtas_ibm_suspend_me(struct rtas_args *args) | |||
814 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; | 904 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; |
815 | struct rtas_suspend_me_data data; | 905 | struct rtas_suspend_me_data data; |
816 | DECLARE_COMPLETION_ONSTACK(done); | 906 | DECLARE_COMPLETION_ONSTACK(done); |
907 | cpumask_var_t offline_mask; | ||
908 | int cpuret; | ||
817 | 909 | ||
818 | if (!rtas_service_present("ibm,suspend-me")) | 910 | if (!rtas_service_present("ibm,suspend-me")) |
819 | return -ENOSYS; | 911 | return -ENOSYS; |
@@ -837,11 +929,24 @@ int rtas_ibm_suspend_me(struct rtas_args *args) | |||
837 | return 0; | 929 | return 0; |
838 | } | 930 | } |
839 | 931 | ||
932 | if (!alloc_cpumask_var(&offline_mask, GFP_TEMPORARY)) | ||
933 | return -ENOMEM; | ||
934 | |||
840 | atomic_set(&data.working, 0); | 935 | atomic_set(&data.working, 0); |
841 | atomic_set(&data.done, 0); | 936 | atomic_set(&data.done, 0); |
842 | atomic_set(&data.error, 0); | 937 | atomic_set(&data.error, 0); |
843 | data.token = rtas_token("ibm,suspend-me"); | 938 | data.token = rtas_token("ibm,suspend-me"); |
844 | data.complete = &done; | 939 | data.complete = &done; |
940 | |||
941 | /* All present CPUs must be online */ | ||
942 | cpumask_andnot(offline_mask, cpu_present_mask, cpu_online_mask); | ||
943 | cpuret = rtas_online_cpus_mask(offline_mask); | ||
944 | if (cpuret) { | ||
945 | pr_err("%s: Could not bring present CPUs online.\n", __func__); | ||
946 | atomic_set(&data.error, cpuret); | ||
947 | goto out; | ||
948 | } | ||
949 | |||
845 | stop_topology_update(); | 950 | stop_topology_update(); |
846 | 951 | ||
847 | /* Call function on all CPUs. One of us will make the | 952 | /* Call function on all CPUs. One of us will make the |
@@ -857,6 +962,14 @@ int rtas_ibm_suspend_me(struct rtas_args *args) | |||
857 | 962 | ||
858 | start_topology_update(); | 963 | start_topology_update(); |
859 | 964 | ||
965 | /* Take down CPUs not online prior to suspend */ | ||
966 | cpuret = rtas_offline_cpus_mask(offline_mask); | ||
967 | if (cpuret) | ||
968 | pr_warn("%s: Could not restore CPUs to offline state.\n", | ||
969 | __func__); | ||
970 | |||
971 | out: | ||
972 | free_cpumask_var(offline_mask); | ||
860 | return atomic_read(&data.error); | 973 | return atomic_read(&data.error); |
861 | } | 974 | } |
862 | #else /* CONFIG_PPC_PSERIES */ | 975 | #else /* CONFIG_PPC_PSERIES */ |