]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/kernel/rtas.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/kyle/parisc-2.6
[linux-2.6-omap-h63xx.git] / arch / powerpc / kernel / rtas.c
index 68bcd2824bc6352ed8507b9dc4d66312d852fbe1..0112318213ab5aada8c00e5ec6877c801501ccd3 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/capability.h>
 #include <linux/delay.h>
 
 #include <asm/prom.h>
 #include <asm/rtas.h>
+#include <asm/hvcall.h>
 #include <asm/semaphore.h>
 #include <asm/machdep.h>
+#include <asm/firmware.h>
 #include <asm/page.h>
 #include <asm/param.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/lmb.h>
 #include <asm/udbg.h>
+#include <asm/syscalls.h>
 
 struct rtas_t rtas = {
        .lock = SPIN_LOCK_UNLOCKED
 };
 
+struct rtas_suspend_me_data {
+       long waiting;
+       struct rtas_args *args;
+};
+
 EXPORT_SYMBOL(rtas);
 
 DEFINE_SPINLOCK(rtas_data_buf_lock);
@@ -555,6 +564,79 @@ void rtas_os_term(char *str)
        } while (status == RTAS_BUSY);
 }
 
+static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
+#ifdef CONFIG_PPC_PSERIES
+static void rtas_percpu_suspend_me(void *info)
+{
+       int i;
+       long rc;
+       long flags;
+       struct rtas_suspend_me_data *data =
+               (struct rtas_suspend_me_data *)info;
+
+       /*
+        * We use "waiting" to indicate our state.  As long
+        * as it is >0, we are still trying to all join up.
+        * If it goes to 0, we have successfully joined up and
+        * one thread got H_CONTINUE.  If any error happens,
+        * we set it to <0.
+        */
+       local_irq_save(flags);
+       do {
+               rc = plpar_hcall_norets(H_JOIN);
+               smp_rmb();
+       } while (rc == H_SUCCESS && data->waiting > 0);
+       if (rc == H_SUCCESS)
+               goto out;
+
+       if (rc == H_CONTINUE) {
+               data->waiting = 0;
+               data->args->args[data->args->nargs] =
+                       rtas_call(ibm_suspend_me_token, 0, 1, NULL);
+               for_each_possible_cpu(i)
+                       plpar_hcall_norets(H_PROD,i);
+       } else {
+               data->waiting = -EBUSY;
+               printk(KERN_ERR "Error on H_JOIN hypervisor call\n");
+       }
+
+out:
+       local_irq_restore(flags);
+       return;
+}
+
+static int rtas_ibm_suspend_me(struct rtas_args *args)
+{
+       int i;
+
+       struct rtas_suspend_me_data data;
+
+       data.waiting = 1;
+       data.args = args;
+
+       /* Call function on all CPUs.  One of us will make the
+        * rtas call
+        */
+       if (on_each_cpu(rtas_percpu_suspend_me, &data, 1, 0))
+               data.waiting = -EINVAL;
+
+       if (data.waiting != 0)
+               printk(KERN_ERR "Error doing global join\n");
+
+       /* Prod each CPU.  This won't hurt, and will wake
+        * anyone we successfully put to sleep with H_JOIN.
+        */
+       for_each_possible_cpu(i)
+               plpar_hcall_norets(H_PROD, i);
+
+       return data.waiting;
+}
+#else /* CONFIG_PPC_PSERIES */
+static int rtas_ibm_suspend_me(struct rtas_args *args)
+{
+       return -ENOSYS;
+}
+#endif
 
 asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
 {
@@ -562,6 +644,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
        unsigned long flags;
        char *buff_copy, *errbuf = NULL;
        int nargs;
+       int rc;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -580,6 +663,17 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
                           nargs * sizeof(rtas_arg_t)) != 0)
                return -EFAULT;
 
+       if (args.token == RTAS_UNKNOWN_SERVICE)
+               return -EINVAL;
+
+       /* Need to handle ibm,suspend_me call specially */
+       if (args.token == ibm_suspend_me_token) {
+               rc = rtas_ibm_suspend_me(&args);
+               if (rc)
+                       return rc;
+               goto copy_return;
+       }
+
        buff_copy = get_errorlog_buffer();
 
        spin_lock_irqsave(&rtas.lock, flags);
@@ -603,6 +697,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
                kfree(buff_copy);
        }
 
+ copy_return:
        /* Copy out args. */
        if (copy_to_user(uargs->args + nargs,
                         args.args + nargs,
@@ -674,8 +769,10 @@ void __init rtas_initialize(void)
         * the stop-self token if any
         */
 #ifdef CONFIG_PPC64
-       if (_machine == PLATFORM_PSERIES_LPAR)
+       if (machine_is(pseries) && firmware_has_feature(FW_FEATURE_LPAR)) {
                rtas_region = min(lmb.rmo_size, RTAS_INSTANTIATE_MAX);
+               ibm_suspend_me_token = rtas_token("ibm,suspend-me");
+       }
 #endif
        rtas_rmo_buf = lmb_alloc_base(RTAS_RMOBUF_MAX, PAGE_SIZE, rtas_region);