diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
-rw-r--r-- | arch/powerpc/kernel/rtas.c | 105 |
1 files changed, 70 insertions, 35 deletions
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index d0516dbee762..41048de3c6c3 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c | |||
@@ -47,14 +47,6 @@ struct rtas_t rtas = { | |||
47 | }; | 47 | }; |
48 | EXPORT_SYMBOL(rtas); | 48 | EXPORT_SYMBOL(rtas); |
49 | 49 | ||
50 | struct rtas_suspend_me_data { | ||
51 | atomic_t working; /* number of cpus accessing this struct */ | ||
52 | atomic_t done; | ||
53 | int token; /* ibm,suspend-me */ | ||
54 | int error; | ||
55 | struct completion *complete; /* wait on this until working == 0 */ | ||
56 | }; | ||
57 | |||
58 | DEFINE_SPINLOCK(rtas_data_buf_lock); | 50 | DEFINE_SPINLOCK(rtas_data_buf_lock); |
59 | EXPORT_SYMBOL(rtas_data_buf_lock); | 51 | EXPORT_SYMBOL(rtas_data_buf_lock); |
60 | 52 | ||
@@ -714,14 +706,53 @@ void rtas_os_term(char *str) | |||
714 | 706 | ||
715 | static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; | 707 | static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; |
716 | #ifdef CONFIG_PPC_PSERIES | 708 | #ifdef CONFIG_PPC_PSERIES |
717 | static void rtas_percpu_suspend_me(void *info) | 709 | static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_when_done) |
710 | { | ||
711 | u16 slb_size = mmu_slb_size; | ||
712 | int rc = H_MULTI_THREADS_ACTIVE; | ||
713 | int cpu; | ||
714 | |||
715 | slb_set_size(SLB_MIN_SIZE); | ||
716 | printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id()); | ||
717 | |||
718 | while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) && | ||
719 | !atomic_read(&data->error)) | ||
720 | rc = rtas_call(data->token, 0, 1, NULL); | ||
721 | |||
722 | if (rc || atomic_read(&data->error)) { | ||
723 | printk(KERN_DEBUG "ibm,suspend-me returned %d\n", rc); | ||
724 | slb_set_size(slb_size); | ||
725 | } | ||
726 | |||
727 | if (atomic_read(&data->error)) | ||
728 | rc = atomic_read(&data->error); | ||
729 | |||
730 | atomic_set(&data->error, rc); | ||
731 | |||
732 | if (wake_when_done) { | ||
733 | atomic_set(&data->done, 1); | ||
734 | |||
735 | for_each_online_cpu(cpu) | ||
736 | plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu)); | ||
737 | } | ||
738 | |||
739 | if (atomic_dec_return(&data->working) == 0) | ||
740 | complete(data->complete); | ||
741 | |||
742 | return rc; | ||
743 | } | ||
744 | |||
745 | int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data) | ||
746 | { | ||
747 | atomic_inc(&data->working); | ||
748 | return __rtas_suspend_last_cpu(data, 0); | ||
749 | } | ||
750 | |||
751 | static int __rtas_suspend_cpu(struct rtas_suspend_me_data *data, int wake_when_done) | ||
718 | { | 752 | { |
719 | long rc = H_SUCCESS; | 753 | long rc = H_SUCCESS; |
720 | unsigned long msr_save; | 754 | unsigned long msr_save; |
721 | u16 slb_size = mmu_slb_size; | ||
722 | int cpu; | 755 | int cpu; |
723 | struct rtas_suspend_me_data *data = | ||
724 | (struct rtas_suspend_me_data *)info; | ||
725 | 756 | ||
726 | atomic_inc(&data->working); | 757 | atomic_inc(&data->working); |
727 | 758 | ||
@@ -729,7 +760,7 @@ static void rtas_percpu_suspend_me(void *info) | |||
729 | msr_save = mfmsr(); | 760 | msr_save = mfmsr(); |
730 | mtmsr(msr_save & ~(MSR_EE)); | 761 | mtmsr(msr_save & ~(MSR_EE)); |
731 | 762 | ||
732 | while (rc == H_SUCCESS && !atomic_read(&data->done)) | 763 | while (rc == H_SUCCESS && !atomic_read(&data->done) && !atomic_read(&data->error)) |
733 | rc = plpar_hcall_norets(H_JOIN); | 764 | rc = plpar_hcall_norets(H_JOIN); |
734 | 765 | ||
735 | mtmsr(msr_save); | 766 | mtmsr(msr_save); |
@@ -741,33 +772,37 @@ static void rtas_percpu_suspend_me(void *info) | |||
741 | /* All other cpus are in H_JOIN, this cpu does | 772 | /* All other cpus are in H_JOIN, this cpu does |
742 | * the suspend. | 773 | * the suspend. |
743 | */ | 774 | */ |
744 | slb_set_size(SLB_MIN_SIZE); | 775 | return __rtas_suspend_last_cpu(data, wake_when_done); |
745 | printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", | ||
746 | smp_processor_id()); | ||
747 | data->error = rtas_call(data->token, 0, 1, NULL); | ||
748 | |||
749 | if (data->error) { | ||
750 | printk(KERN_DEBUG "ibm,suspend-me returned %d\n", | ||
751 | data->error); | ||
752 | slb_set_size(slb_size); | ||
753 | } | ||
754 | } else { | 776 | } else { |
755 | printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n", | 777 | printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n", |
756 | smp_processor_id(), rc); | 778 | smp_processor_id(), rc); |
757 | data->error = rc; | 779 | atomic_set(&data->error, rc); |
758 | } | 780 | } |
759 | 781 | ||
760 | atomic_set(&data->done, 1); | 782 | if (wake_when_done) { |
783 | atomic_set(&data->done, 1); | ||
761 | 784 | ||
762 | /* This cpu did the suspend or got an error; in either case, | 785 | /* This cpu did the suspend or got an error; in either case, |
763 | * we need to prod all other other cpus out of join state. | 786 | * we need to prod all other other cpus out of join state. |
764 | * Extra prods are harmless. | 787 | * Extra prods are harmless. |
765 | */ | 788 | */ |
766 | for_each_online_cpu(cpu) | 789 | for_each_online_cpu(cpu) |
767 | plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu)); | 790 | plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu)); |
791 | } | ||
768 | out: | 792 | out: |
769 | if (atomic_dec_return(&data->working) == 0) | 793 | if (atomic_dec_return(&data->working) == 0) |
770 | complete(data->complete); | 794 | complete(data->complete); |
795 | return rc; | ||
796 | } | ||
797 | |||
798 | int rtas_suspend_cpu(struct rtas_suspend_me_data *data) | ||
799 | { | ||
800 | return __rtas_suspend_cpu(data, 0); | ||
801 | } | ||
802 | |||
803 | static void rtas_percpu_suspend_me(void *info) | ||
804 | { | ||
805 | __rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1); | ||
771 | } | 806 | } |
772 | 807 | ||
773 | static int rtas_ibm_suspend_me(struct rtas_args *args) | 808 | static int rtas_ibm_suspend_me(struct rtas_args *args) |
@@ -802,22 +837,22 @@ static int rtas_ibm_suspend_me(struct rtas_args *args) | |||
802 | 837 | ||
803 | atomic_set(&data.working, 0); | 838 | atomic_set(&data.working, 0); |
804 | atomic_set(&data.done, 0); | 839 | atomic_set(&data.done, 0); |
840 | atomic_set(&data.error, 0); | ||
805 | data.token = rtas_token("ibm,suspend-me"); | 841 | data.token = rtas_token("ibm,suspend-me"); |
806 | data.error = 0; | ||
807 | data.complete = &done; | 842 | data.complete = &done; |
808 | 843 | ||
809 | /* Call function on all CPUs. One of us will make the | 844 | /* Call function on all CPUs. One of us will make the |
810 | * rtas call | 845 | * rtas call |
811 | */ | 846 | */ |
812 | if (on_each_cpu(rtas_percpu_suspend_me, &data, 0)) | 847 | if (on_each_cpu(rtas_percpu_suspend_me, &data, 0)) |
813 | data.error = -EINVAL; | 848 | atomic_set(&data.error, -EINVAL); |
814 | 849 | ||
815 | wait_for_completion(&done); | 850 | wait_for_completion(&done); |
816 | 851 | ||
817 | if (data.error != 0) | 852 | if (atomic_read(&data.error) != 0) |
818 | printk(KERN_ERR "Error doing global join\n"); | 853 | printk(KERN_ERR "Error doing global join\n"); |
819 | 854 | ||
820 | return data.error; | 855 | return atomic_read(&data.error); |
821 | } | 856 | } |
822 | #else /* CONFIG_PPC_PSERIES */ | 857 | #else /* CONFIG_PPC_PSERIES */ |
823 | static int rtas_ibm_suspend_me(struct rtas_args *args) | 858 | static int rtas_ibm_suspend_me(struct rtas_args *args) |