[POWERPC] Setup RTAS values earlier, to enable rtas_call() earlier
[powerpc.git] / arch / powerpc / kernel / rtas.c
index b5b2add..061d8af 100644 (file)
@@ -25,6 +25,7 @@
 #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
 };
+EXPORT_SYMBOL(rtas);
 
 struct rtas_suspend_me_data {
        long waiting;
        struct rtas_args *args;
 };
 
-EXPORT_SYMBOL(rtas);
-
 DEFINE_SPINLOCK(rtas_data_buf_lock);
+EXPORT_SYMBOL(rtas_data_buf_lock);
+
 char rtas_data_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned;
+EXPORT_SYMBOL(rtas_data_buf);
+
 unsigned long rtas_rmo_buf;
 
 /*
@@ -234,6 +239,7 @@ int rtas_token(const char *service)
        tokp = (int *) get_property(rtas.dev, service, NULL);
        return tokp ? *tokp : RTAS_UNKNOWN_SERVICE;
 }
+EXPORT_SYMBOL(rtas_token);
 
 #ifdef CONFIG_RTAS_ERROR_LOGGING
 /*
@@ -326,7 +332,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
        char *buff_copy = NULL;
        int ret;
 
-       if (token == RTAS_UNKNOWN_SERVICE)
+       if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
                return -1;
 
        /* Gotta do something different here, use global lock for now... */
@@ -367,26 +373,41 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
        }
        return ret;
 }
+EXPORT_SYMBOL(rtas_call);
 
-/* Given an RTAS status code of 990n compute the hinted delay of 10^n
- * (last digit) milliseconds.  For now we bound at n=5 (100 sec).
+/* For RTAS_BUSY (-2), delay for 1 millisecond.  For an extended busy status
+ * code of 990n, perform the hinted delay of 10^n (last digit) milliseconds.
  */
-unsigned int rtas_extended_busy_delay_time(int status)
+unsigned int rtas_busy_delay_time(int status)
 {
-       int order = status - 9900;
-       unsigned long ms;
+       int order;
+       unsigned int ms = 0;
+
+       if (status == RTAS_BUSY) {
+               ms = 1;
+       } else if (status >= 9900 && status <= 9905) {
+               order = status - 9900;
+               for (ms = 1; order > 0; order--)
+                       ms *= 10;
+       }
+
+       return ms;
+}
+EXPORT_SYMBOL(rtas_busy_delay_time);
 
-       if (order < 0)
-               order = 0;      /* RTC depends on this for -2 clock busy */
-       else if (order > 5)
-               order = 5;      /* bound */
+/* For an RTAS busy status code, perform the hinted delay. */
+unsigned int rtas_busy_delay(int status)
+{
+       unsigned int ms;
 
-       /* Use microseconds for reasonable accuracy */
-       for (ms = 1; order > 0; order--)
-               ms *= 10;
+       might_sleep();
+       ms = rtas_busy_delay_time(status);
+       if (ms)
+               msleep(ms);
 
-       return ms; 
+       return ms;
 }
+EXPORT_SYMBOL(rtas_busy_delay);
 
 int rtas_error_rc(int rtas_rc)
 {
@@ -432,82 +453,61 @@ int rtas_get_power_level(int powerdomain, int *level)
                return rtas_error_rc(rc);
        return rc;
 }
+EXPORT_SYMBOL(rtas_get_power_level);
 
 int rtas_set_power_level(int powerdomain, int level, int *setlevel)
 {
        int token = rtas_token("set-power-level");
-       unsigned int wait_time;
        int rc;
 
        if (token == RTAS_UNKNOWN_SERVICE)
                return -ENOENT;
 
-       while (1) {
+       do {
                rc = rtas_call(token, 2, 2, setlevel, powerdomain, level);
-               if (rc == RTAS_BUSY)
-                       udelay(1);
-               else if (rtas_is_extended_busy(rc)) {
-                       wait_time = rtas_extended_busy_delay_time(rc);
-                       udelay(wait_time * 1000);
-               } else
-                       break;
-       }
+       } while (rtas_busy_delay(rc));
 
        if (rc < 0)
                return rtas_error_rc(rc);
        return rc;
 }
+EXPORT_SYMBOL(rtas_set_power_level);
 
 int rtas_get_sensor(int sensor, int index, int *state)
 {
        int token = rtas_token("get-sensor-state");
-       unsigned int wait_time;
        int rc;
 
        if (token == RTAS_UNKNOWN_SERVICE)
                return -ENOENT;
 
-       while (1) {
+       do {
                rc = rtas_call(token, 2, 2, state, sensor, index);
-               if (rc == RTAS_BUSY)
-                       udelay(1);
-               else if (rtas_is_extended_busy(rc)) {
-                       wait_time = rtas_extended_busy_delay_time(rc);
-                       udelay(wait_time * 1000);
-               } else
-                       break;
-       }
+       } while (rtas_busy_delay(rc));
 
        if (rc < 0)
                return rtas_error_rc(rc);
        return rc;
 }
+EXPORT_SYMBOL(rtas_get_sensor);
 
 int rtas_set_indicator(int indicator, int index, int new_value)
 {
        int token = rtas_token("set-indicator");
-       unsigned int wait_time;
        int rc;
 
        if (token == RTAS_UNKNOWN_SERVICE)
                return -ENOENT;
 
-       while (1) {
+       do {
                rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value);
-               if (rc == RTAS_BUSY)
-                       udelay(1);
-               else if (rtas_is_extended_busy(rc)) {
-                       wait_time = rtas_extended_busy_delay_time(rc);
-                       udelay(wait_time * 1000);
-               }
-               else
-                       break;
-       }
+       } while (rtas_busy_delay(rc));
 
        if (rc < 0)
                return rtas_error_rc(rc);
        return rc;
 }
+EXPORT_SYMBOL(rtas_set_indicator);
 
 void rtas_restart(char *cmd)
 {
@@ -553,13 +553,11 @@ void rtas_os_term(char *str)
        do {
                status = rtas_call(rtas_token("ibm,os-term"), 1, 1, NULL,
                                   __pa(rtas_os_term_buf));
+       } while (rtas_busy_delay(status));
 
-               if (status == RTAS_BUSY)
-                       udelay(1);
-               else if (status != 0)
-                       printk(KERN_EMERG "ibm,os-term call failed %d\n",
+       if (status != 0)
+               printk(KERN_EMERG "ibm,os-term call failed %d\n",
                               status);
-       } while (status == RTAS_BUSY);
 }
 
 static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
@@ -576,26 +574,26 @@ static void rtas_percpu_suspend_me(void *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,
+        * 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)
+       } while (rc == H_SUCCESS && data->waiting > 0);
+       if (rc == H_SUCCESS)
                goto out;
 
-       if (rc == H_Continue) {
+       if (rc == H_CONTINUE) {
                data->waiting = 0;
                data->args->args[data->args->nargs] =
                        rtas_call(ibm_suspend_me_token, 0, 1, NULL);
-               for_each_cpu(i)
+               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");
+               printk(KERN_ERR "Error on H_JOIN hypervisor call\n");
        }
 
 out:
@@ -606,9 +604,31 @@ out:
 static int rtas_ibm_suspend_me(struct rtas_args *args)
 {
        int i;
+       long state;
+       long rc;
+       unsigned long dummy;
 
        struct rtas_suspend_me_data data;
 
+       /* Make sure the state is valid */
+       rc = plpar_hcall(H_VASI_STATE,
+                        ((u64)args->args[0] << 32) | args->args[1],
+                        0, 0, 0,
+                        &state, &dummy, &dummy);
+
+       if (rc) {
+               printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned %ld\n",rc);
+               return rc;
+       } else if (state == H_VASI_ENABLED) {
+               args->args[args->nargs] = RTAS_NOT_SUSPENDABLE;
+               return 0;
+       } else if (state != H_VASI_SUSPENDING) {
+               printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned state %ld\n",
+                      state);
+               args->args[args->nargs] = -1;
+               return 0;
+       }
+
        data.waiting = 1;
        data.args = args;
 
@@ -622,9 +642,9 @@ static int rtas_ibm_suspend_me(struct rtas_args *args)
                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
+        * anyone we successfully put to sleep with H_JOIN.
         */
-       for_each_cpu(i)
+       for_each_possible_cpu(i)
                plpar_hcall_norets(H_PROD, i);
 
        return data.waiting;
@@ -767,7 +787,7 @@ 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");
        }
@@ -782,13 +802,24 @@ void __init rtas_initialize(void)
 #endif
 }
 
+int __init early_init_dt_scan_rtas(unsigned long node,
+               const char *uname, int depth, void *data)
+{
+       u32 *basep, *entryp, *sizep;
 
-EXPORT_SYMBOL(rtas_token);
-EXPORT_SYMBOL(rtas_call);
-EXPORT_SYMBOL(rtas_data_buf);
-EXPORT_SYMBOL(rtas_data_buf_lock);
-EXPORT_SYMBOL(rtas_extended_busy_delay_time);
-EXPORT_SYMBOL(rtas_get_sensor);
-EXPORT_SYMBOL(rtas_get_power_level);
-EXPORT_SYMBOL(rtas_set_power_level);
-EXPORT_SYMBOL(rtas_set_indicator);
+       if (depth != 1 || strcmp(uname, "rtas") != 0)
+               return 0;
+
+       basep  = of_get_flat_dt_prop(node, "linux,rtas-base", NULL);
+       entryp = of_get_flat_dt_prop(node, "linux,rtas-entry", NULL);
+       sizep  = of_get_flat_dt_prop(node, "rtas-size", NULL);
+
+       if (basep && entryp && sizep) {
+               rtas.base = *basep;
+               rtas.entry = *entryp;
+               rtas.size = *sizep;
+       }
+
+       /* break now */
+       return 1;
+}