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 */ |
