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) |
