Merge ../linux-2.6
[pandora-kernel.git] / arch / powerpc / kernel / rtas.c
index 0112318..77f1e06 100644 (file)
 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;
 
 /*
@@ -106,11 +109,71 @@ static void call_rtas_display_status_delay(char c)
        }
 }
 
-void __init udbg_init_rtas(void)
+void __init udbg_init_rtas_panel(void)
 {
        udbg_putc = call_rtas_display_status_delay;
 }
 
+#ifdef CONFIG_UDBG_RTAS_CONSOLE
+
+/* If you think you're dying before early_init_dt_scan_rtas() does its
+ * work, you can hard code the token values for your firmware here and
+ * hardcode rtas.base/entry etc.
+ */
+static unsigned int rtas_putchar_token = RTAS_UNKNOWN_SERVICE;
+static unsigned int rtas_getchar_token = RTAS_UNKNOWN_SERVICE;
+
+static void udbg_rtascon_putc(char c)
+{
+       int tries;
+
+       if (!rtas.base)
+               return;
+
+       /* Add CRs before LFs */
+       if (c == '\n')
+               udbg_rtascon_putc('\r');
+
+       /* if there is more than one character to be displayed, wait a bit */
+       for (tries = 0; tries < 16; tries++) {
+               if (rtas_call(rtas_putchar_token, 1, 1, NULL, c) == 0)
+                       break;
+               udelay(1000);
+       }
+}
+
+static int udbg_rtascon_getc_poll(void)
+{
+       int c;
+
+       if (!rtas.base)
+               return -1;
+
+       if (rtas_call(rtas_getchar_token, 0, 2, &c))
+               return -1;
+
+       return c;
+}
+
+static int udbg_rtascon_getc(void)
+{
+       int c;
+
+       while ((c = udbg_rtascon_getc_poll()) == -1)
+               ;
+
+       return c;
+}
+
+
+void __init udbg_init_rtas_console(void)
+{
+       udbg_putc = udbg_rtascon_putc;
+       udbg_getc = udbg_rtascon_getc;
+       udbg_getc_poll = udbg_rtascon_getc_poll;
+}
+#endif /* CONFIG_UDBG_RTAS_CONSOLE */
+
 void rtas_progress(char *s, unsigned short hex)
 {
        struct device_node *root;
@@ -236,6 +299,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
 /*
@@ -328,7 +392,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... */
@@ -369,26 +433,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)
 {
@@ -434,82 +513,82 @@ 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);
+
+/*
+ * Ignoring RTAS extended delay
+ */
+int rtas_set_indicator_fast(int indicator, int index, int new_value)
+{
+       int rc;
+       int token = rtas_token("set-indicator");
+
+       if (token == RTAS_UNKNOWN_SERVICE)
+               return -ENOENT;
+
+       rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value);
+
+       WARN_ON(rc == -2 || (rc >= 9900 && rc <= 9905));
+
+       if (rc < 0)
+               return rtas_error_rc(rc);
+
+       return rc;
+}
 
 void rtas_restart(char *cmd)
 {
@@ -555,13 +634,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;
@@ -608,9 +685,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;
 
@@ -784,13 +883,34 @@ 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;
+       }
+
+#ifdef CONFIG_UDBG_RTAS_CONSOLE
+       basep = of_get_flat_dt_prop(node, "put-term-char", NULL);
+       if (basep)
+               rtas_putchar_token = *basep;
+
+       basep = of_get_flat_dt_prop(node, "get-term-char", NULL);
+       if (basep)
+               rtas_getchar_token = *basep;
+#endif
+
+       /* break now */
+       return 1;
+}