Merge branch 'drm-patches' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[pandora-kernel.git] / drivers / char / ipmi / ipmi_kcs_sm.c
index d21853a..c1b8228 100644 (file)
  */
 
 #include <linux/kernel.h> /* For printk. */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/string.h>
+#include <linux/jiffies.h>
 #include <linux/ipmi_msgdefs.h>                /* for completion codes */
 #include "ipmi_si_sm.h"
 
-/* Set this if you want a printout of why the state machine was hosed
-   when it gets hosed. */
-#define DEBUG_HOSED_REASON
+/* kcs_debug is a bit-field
+ *     KCS_DEBUG_ENABLE -      turned on for now
+ *     KCS_DEBUG_MSG    -      commands and their responses
+ *     KCS_DEBUG_STATES -      state machine
+ */
+#define KCS_DEBUG_STATES       4
+#define KCS_DEBUG_MSG          2
+#define        KCS_DEBUG_ENABLE        1
 
-/* Print the state machine state on entry every time. */
-#undef DEBUG_STATE
+static int kcs_debug;
+module_param(kcs_debug, int, 0644);
+MODULE_PARM_DESC(kcs_debug, "debug bitmask, 1=enable, 2=messages, 4=states");
 
 /* The states the KCS driver may be in. */
 enum kcs_states {
@@ -84,13 +93,14 @@ enum kcs_states {
                                   state machine. */
 };
 
-#define MAX_KCS_READ_SIZE 80
-#define MAX_KCS_WRITE_SIZE 80
+#define MAX_KCS_READ_SIZE IPMI_MAX_MSG_LENGTH
+#define MAX_KCS_WRITE_SIZE IPMI_MAX_MSG_LENGTH
 
 /* Timeouts in microseconds. */
 #define IBF_RETRY_TIMEOUT 1000000
 #define OBF_RETRY_TIMEOUT 1000000
 #define MAX_ERROR_RETRIES 10
+#define ERROR0_OBF_WAIT_JIFFIES (2*HZ)
 
 struct si_sm_data
 {
@@ -107,6 +117,7 @@ struct si_sm_data
        unsigned int  error_retries;
        long          ibf_timeout;
        long          obf_timeout;
+       unsigned long  error0_timeout;
 };
 
 static unsigned int init_kcs_data(struct si_sm_data *kcs,
@@ -175,11 +186,11 @@ static inline void start_error_recovery(struct si_sm_data *kcs, char *reason)
 {
        (kcs->error_retries)++;
        if (kcs->error_retries > MAX_ERROR_RETRIES) {
-#ifdef DEBUG_HOSED_REASON
-               printk("ipmi_kcs_sm: kcs hosed: %s\n", reason);
-#endif
+               if (kcs_debug & KCS_DEBUG_ENABLE)
+                       printk(KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n", reason);
                kcs->state = KCS_HOSED;
        } else {
+               kcs->error0_timeout = jiffies + ERROR0_OBF_WAIT_JIFFIES;
                kcs->state = KCS_ERROR0;
        }
 }
@@ -216,7 +227,7 @@ static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
 static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
                            long time)
 {
-       if (! GET_STATUS_OBF(status)) {
+       if (!GET_STATUS_OBF(status)) {
                kcs->obf_timeout -= time;
                if (kcs->obf_timeout < 0) {
                    start_error_recovery(kcs, "OBF not ready in time");
@@ -248,14 +259,23 @@ static void restart_kcs_transaction(struct si_sm_data *kcs)
 static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data,
                                 unsigned int size)
 {
-       if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) {
-               return -1;
-       }
+       unsigned int i;
 
-       if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) {
-               return -2;
-       }
+       if (size < 2)
+               return IPMI_REQ_LEN_INVALID_ERR;
+       if (size > MAX_KCS_WRITE_SIZE)
+               return IPMI_REQ_LEN_EXCEEDED_ERR;
+
+       if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED))
+               return IPMI_NOT_IN_MY_STATE_ERR;
 
+       if (kcs_debug & KCS_DEBUG_MSG) {
+               printk(KERN_DEBUG "start_kcs_transaction -");
+               for (i = 0; i < size; i ++) {
+                       printk(" %02x", (unsigned char) (data [i]));
+               }
+               printk ("\n");
+       }
        kcs->error_retries = 0;
        memcpy(kcs->write_data, data, size);
        kcs->write_count = size;
@@ -305,9 +325,9 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
 
        status = read_status(kcs);
 
-#ifdef DEBUG_STATE
-       printk("  State = %d, %x\n", kcs->state, status);
-#endif
+       if (kcs_debug & KCS_DEBUG_STATES)
+               printk(KERN_DEBUG "KCS: State = %d, %x\n", kcs->state, status);
+
        /* All states wait for ibf, so just do it here. */
        if (!check_ibf(kcs, status, time))
                return SI_SM_CALL_WITH_DELAY;
@@ -389,7 +409,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
                }
 
                if (state == KCS_READ_STATE) {
-                       if (! check_obf(kcs, status, time))
+                       if (!check_obf(kcs, status, time))
                                return SI_SM_CALL_WITH_DELAY;
                        read_next_byte(kcs);
                } else {
@@ -409,6 +429,10 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
 
        case KCS_ERROR0:
                clear_obf(kcs, status);
+               status = read_status(kcs);
+               if  (GET_STATUS_OBF(status)) /* controller isn't responding */
+                       if (time_before(jiffies, kcs->error0_timeout))
+                               return SI_SM_CALL_WITH_TICK_DELAY;
                write_cmd(kcs, KCS_GET_STATUS_ABORT);
                kcs->state = KCS_ERROR1;
                break;
@@ -425,7 +449,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
                                             "Not in read state for error2");
                        break;
                }
-               if (! check_obf(kcs, status, time))
+               if (!check_obf(kcs, status, time))
                        return SI_SM_CALL_WITH_DELAY;
 
                clear_obf(kcs, status);
@@ -440,7 +464,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
                        break;
                }
 
-               if (! check_obf(kcs, status, time))
+               if (!check_obf(kcs, status, time))
                        return SI_SM_CALL_WITH_DELAY;
 
                clear_obf(kcs, status);