]> err.no Git - linux-2.6/blobdiff - drivers/char/ipmi/ipmi_si_intf.c
[PATCH] ipmi: kcs error0 delay
[linux-2.6] / drivers / char / ipmi / ipmi_si_intf.c
index 1abec687865cf5b4862d26f4552abc39e748094d..d514df7c72830dc018d234db1b6e72fe36946168 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/list.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
+#include <linux/notifier.h>
 #include <asm/irq.h>
 #ifdef CONFIG_HIGH_RES_TIMERS
 #include <linux/hrtime.h>
@@ -222,6 +223,12 @@ struct smi_info
        unsigned long incoming_messages;
 };
 
+static struct notifier_block *xaction_notifier_list;
+static int register_xaction_notifier(struct notifier_block * nb)
+{
+       return notifier_chain_register(&xaction_notifier_list, nb);
+}
+
 static void si_restart_short_timer(struct smi_info *smi_info);
 
 static void deliver_recv_msg(struct smi_info *smi_info,
@@ -281,6 +288,11 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
                do_gettimeofday(&t);
                printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec);
 #endif
+               err = notifier_call_chain(&xaction_notifier_list, 0, smi_info);
+               if (err & NOTIFY_STOP_MASK) {
+                       rv = SI_SM_CALL_WITHOUT_DELAY;
+                       goto out;
+               }
                err = smi_info->handlers->start_transaction(
                        smi_info->si_sm,
                        smi_info->curr_msg->data,
@@ -291,6 +303,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
 
                rv = SI_SM_CALL_WITHOUT_DELAY;
        }
+       out:
        spin_unlock(&(smi_info->msg_lock));
 
        return rv;
@@ -819,7 +832,7 @@ static void smi_timeout(unsigned long data)
        enum si_sm_result smi_result;
        unsigned long     flags;
        unsigned long     jiffies_now;
-       unsigned long     time_diff;
+       long              time_diff;
 #ifdef DEBUG_TIMING
        struct timeval    t;
 #endif
@@ -835,7 +848,7 @@ static void smi_timeout(unsigned long data)
        printk("**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec);
 #endif
        jiffies_now = jiffies;
-       time_diff = ((jiffies_now - smi_info->last_timeout_jiffies)
+       time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
                     * SI_USEC_PER_JIFFY);
        smi_result = smi_event_handler(smi_info, time_diff);
 
@@ -1019,7 +1032,7 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
 #define IPMI_MEM_ADDR_SPACE 1
 #define IPMI_IO_ADDR_SPACE  2
 
-#if defined(CONFIG_ACPI_INTERPRETER) || defined(CONFIG_X86) || defined(CONFIG_PCI)
+#if defined(CONFIG_ACPI) || defined(CONFIG_X86) || defined(CONFIG_PCI)
 static int is_new_interface(int intf, u8 addr_space, unsigned long base_addr)
 {
        int i;
@@ -1395,7 +1408,7 @@ static int try_init_mem(int intf_num, struct smi_info **new_info)
 }
 
 
-#ifdef CONFIG_ACPI_INTERPRETER
+#ifdef CONFIG_ACPI
 
 #include <linux/acpi.h>
 
@@ -1517,6 +1530,9 @@ static int try_init_acpi(int intf_num, struct smi_info **new_info)
        char             *io_type;
        u8               addr_space;
 
+       if (acpi_disabled)
+               return -ENODEV;
+
        if (acpi_failure)
                return -ENODEV;
 
@@ -1916,9 +1932,9 @@ static int try_get_dev_id(struct smi_info *smi_info)
        smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
        for (;;)
        {
-               if (smi_result == SI_SM_CALL_WITH_DELAY) {
-                       set_current_state(TASK_UNINTERRUPTIBLE);
-                       schedule_timeout(1);
+               if (smi_result == SI_SM_CALL_WITH_DELAY ||
+                   smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
+                       schedule_timeout_uninterruptible(1);
                        smi_result = smi_info->handlers->event(
                                smi_info->si_sm, 100);
                }
@@ -2050,6 +2066,9 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info)
  * IPMI Version = 0x51             IPMI 1.5
  * Manufacturer ID = A2 02 00      Dell IANA
  *
+ * Additionally, PowerEdge systems with IPMI < 1.5 may also assert
+ * OEM0_DATA_AVAIL and needs to be treated as RECEIVE_MSG_AVAIL.
+ *
  */
 #define DELL_POWEREDGE_8G_BMC_DEVICE_ID  0x20
 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80
@@ -2059,16 +2078,87 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
 {
        struct ipmi_device_id *id = &smi_info->device_id;
        const char mfr[3]=DELL_IANA_MFR_ID;
-       if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))
-           && (id->device_id       == DELL_POWEREDGE_8G_BMC_DEVICE_ID)
-           && (id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV)
-           && (id->ipmi_version    == DELL_POWEREDGE_8G_BMC_IPMI_VERSION))
-       {
-               smi_info->oem_data_avail_handler =
-                       oem_data_avail_to_receive_msg_avail;
+       if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))) {
+               if (id->device_id       == DELL_POWEREDGE_8G_BMC_DEVICE_ID  &&
+                   id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV &&
+                   id->ipmi_version    == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) {
+                       smi_info->oem_data_avail_handler =
+                               oem_data_avail_to_receive_msg_avail;
+               }
+               else if (ipmi_version_major(id) < 1 ||
+                        (ipmi_version_major(id) == 1 &&
+                         ipmi_version_minor(id) < 5)) {
+                       smi_info->oem_data_avail_handler =
+                               oem_data_avail_to_receive_msg_avail;
+               }
        }
 }
 
+#define CANNOT_RETURN_REQUESTED_LENGTH 0xCA
+static void return_hosed_msg_badsize(struct smi_info *smi_info)
+{
+       struct ipmi_smi_msg *msg = smi_info->curr_msg;
+
+       /* Make it a reponse */
+       msg->rsp[0] = msg->data[0] | 4;
+       msg->rsp[1] = msg->data[1];
+       msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH;
+       msg->rsp_size = 3;
+       smi_info->curr_msg = NULL;
+       deliver_recv_msg(smi_info, msg);
+}
+
+/*
+ * dell_poweredge_bt_xaction_handler
+ * @info - smi_info.device_id must be populated
+ *
+ * Dell PowerEdge servers with the BT interface (x6xx and 1750) will
+ * not respond to a Get SDR command if the length of the data
+ * requested is exactly 0x3A, which leads to command timeouts and no
+ * data returned.  This intercepts such commands, and causes userspace
+ * callers to try again with a different-sized buffer, which succeeds.
+ */
+
+#define STORAGE_NETFN 0x0A
+#define STORAGE_CMD_GET_SDR 0x23
+static int dell_poweredge_bt_xaction_handler(struct notifier_block *self,
+                                            unsigned long unused,
+                                            void *in)
+{
+       struct smi_info *smi_info = in;
+       unsigned char *data = smi_info->curr_msg->data;
+       unsigned int size   = smi_info->curr_msg->data_size;
+       if (size >= 8 &&
+           (data[0]>>2) == STORAGE_NETFN &&
+           data[1] == STORAGE_CMD_GET_SDR &&
+           data[7] == 0x3A) {
+               return_hosed_msg_badsize(smi_info);
+               return NOTIFY_STOP;
+       }
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block dell_poweredge_bt_xaction_notifier = {
+       .notifier_call  = dell_poweredge_bt_xaction_handler,
+};
+
+/*
+ * setup_dell_poweredge_bt_xaction_handler
+ * @info - smi_info.device_id must be filled in already
+ *
+ * Fills in smi_info.device_id.start_transaction_pre_hook
+ * when we know what function to use there.
+ */
+static void
+setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
+{
+       struct ipmi_device_id *id = &smi_info->device_id;
+       const char mfr[3]=DELL_IANA_MFR_ID;
+       if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) &&
+           smi_info->si_type == SI_BT)
+               register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
+}
+
 /*
  * setup_oem_data_handler
  * @info - smi_info.device_id must be filled in already
@@ -2082,6 +2172,11 @@ static void setup_oem_data_handler(struct smi_info *smi_info)
        setup_dell_poweredge_oem_data_handler(smi_info);
 }
 
+static void setup_xaction_handlers(struct smi_info *smi_info)
+{
+       setup_dell_poweredge_bt_xaction_handler(smi_info);
+}
+
 /* Returns 0 if initialized, or negative on an error. */
 static int init_one_smi(int intf_num, struct smi_info **smi)
 {
@@ -2092,7 +2187,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi)
        rv = try_init_mem(intf_num, &new_smi);
        if (rv)
                rv = try_init_port(intf_num, &new_smi);
-#ifdef CONFIG_ACPI_INTERPRETER
+#ifdef CONFIG_ACPI
        if (rv && si_trydefaults)
                rv = try_init_acpi(intf_num, &new_smi);
 #endif
@@ -2177,6 +2272,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi)
                goto out_err;
 
        setup_oem_data_handler(new_smi);
+       setup_xaction_handlers(new_smi);
 
        /* Try to claim any interrupts. */
        new_smi->irq_setup(new_smi);
@@ -2253,10 +2349,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi)
 
        /* Wait for the timer to stop.  This avoids problems with race
           conditions removing the timer here. */
-       while (! new_smi->timer_stopped) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
-       }
+       while (!new_smi->timer_stopped)
+               schedule_timeout_uninterruptible(1);
 
  out_err:
        if (new_smi->intf)
@@ -2376,17 +2470,14 @@ static void __exit cleanup_one_si(struct smi_info *to_clean)
 
        /* Wait for the timer to stop.  This avoids problems with race
           conditions removing the timer here. */
-       while (! to_clean->timer_stopped) {
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
-       }
+       while (!to_clean->timer_stopped)
+               schedule_timeout_uninterruptible(1);
 
        /* Interrupts and timeouts are stopped, now make sure the
           interface is in a clean state. */
        while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) {
                poll(to_clean);
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
+               schedule_timeout_uninterruptible(1);
        }
 
        rv = ipmi_unregister_smi(to_clean->intf);