ipmi: Add alert handling to SSIF
authorCorey Minyard <cminyard@mvista.com>
Fri, 24 Apr 2015 12:46:06 +0000 (07:46 -0500)
committerCorey Minyard <cminyard@mvista.com>
Wed, 6 May 2015 00:36:38 +0000 (19:36 -0500)
The SSIF interface can optionally have an SMBus alert come in when
data is ready.  Unfortunately, the IPMI spec gives wiggle room to
the implementer to allow them to always have the alert enabled,
even if the driver doesn't enable it.  So implement alerts.
If you don't in this situation, the SMBus alert handling will
constantly complain.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
drivers/char/ipmi/ipmi_ssif.c

index f6ea4fa444b3ed1b077e23735548e22c486dd8cf..5b82d9947ec5cd412ed8f45137ba8b26eee151ea 100644 (file)
@@ -165,6 +165,9 @@ enum ssif_stat_indexes {
        /* Number of watchdog pretimeouts. */
        SSIF_STAT_watchdog_pretimeouts,
 
+       /* Number of alers received. */
+       SSIF_STAT_alerts,
+
        /* Always add statistics before this value, it must be last. */
        SSIF_NUM_STATS
 };
@@ -213,7 +216,16 @@ struct ssif_info {
 #define WDT_PRE_TIMEOUT_INT    0x08
        unsigned char       msg_flags;
 
+       u8                  global_enables;
        bool                has_event_buffer;
+       bool                supports_alert;
+
+       /*
+        * Used to tell what we should do with alerts.  If we are
+        * waiting on a response, read the data immediately.
+        */
+       bool                got_alert;
+       bool                waiting_alert;
 
        /*
         * If set to true, this will request events the next time the
@@ -517,14 +529,10 @@ static int ssif_i2c_send(struct ssif_info *ssif_info,
 static void msg_done_handler(struct ssif_info *ssif_info, int result,
                             unsigned char *data, unsigned int len);
 
-static void retry_timeout(unsigned long data)
+static void start_get(struct ssif_info *ssif_info)
 {
-       struct ssif_info *ssif_info = (void *) data;
        int rv;
 
-       if (ssif_info->stopping)
-               return;
-
        ssif_info->rtc_us_timer = 0;
 
        rv = ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ,
@@ -539,6 +547,46 @@ static void retry_timeout(unsigned long data)
        }
 }
 
+static void retry_timeout(unsigned long data)
+{
+       struct ssif_info *ssif_info = (void *) data;
+       unsigned long oflags, *flags;
+       bool waiting;
+
+       if (ssif_info->stopping)
+               return;
+
+       flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+       waiting = ssif_info->waiting_alert;
+       ssif_info->waiting_alert = false;
+       ipmi_ssif_unlock_cond(ssif_info, flags);
+
+       if (waiting)
+               start_get(ssif_info);
+}
+
+
+static void ssif_alert(struct i2c_client *client, unsigned int data)
+{
+       struct ssif_info *ssif_info = i2c_get_clientdata(client);
+       unsigned long oflags, *flags;
+       bool do_get = false;
+
+       ssif_inc_stat(ssif_info, alerts);
+
+       flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+       if (ssif_info->waiting_alert) {
+               ssif_info->waiting_alert = false;
+               del_timer(&ssif_info->retry_timer);
+               do_get = true;
+       } else if (ssif_info->curr_msg) {
+               ssif_info->got_alert = true;
+       }
+       ipmi_ssif_unlock_cond(ssif_info, flags);
+       if (do_get)
+               start_get(ssif_info);
+}
+
 static int start_resend(struct ssif_info *ssif_info);
 
 static void msg_done_handler(struct ssif_info *ssif_info, int result,
@@ -558,9 +606,12 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
                if (ssif_info->retries_left > 0) {
                        ssif_inc_stat(ssif_info, receive_retries);
 
+                       flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+                       ssif_info->waiting_alert = true;
+                       ssif_info->rtc_us_timer = SSIF_MSG_USEC;
                        mod_timer(&ssif_info->retry_timer,
                                  jiffies + SSIF_MSG_JIFFIES);
-                       ssif_info->rtc_us_timer = SSIF_MSG_USEC;
+                       ipmi_ssif_unlock_cond(ssif_info, flags);
                        return;
                }
 
@@ -649,7 +700,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
                        if (rv < 0) {
                                if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
                                        pr_info(PFX
-                                               "Error from i2c_non_blocking_op(2)\n");
+                                               "Error from ssif_i2c_send\n");
 
                                result = -EIO;
                        } else
@@ -863,15 +914,32 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
                        msg_done_handler(ssif_info, -EIO, NULL, 0);
                }
        } else {
+               unsigned long oflags, *flags;
+               bool got_alert;
+
                ssif_inc_stat(ssif_info, sent_messages);
                ssif_inc_stat(ssif_info, sent_messages_parts);
 
-               /* Wait a jiffie then request the next message */
-               ssif_info->retries_left = SSIF_RECV_RETRIES;
-               ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC;
-               mod_timer(&ssif_info->retry_timer,
-                         jiffies + SSIF_MSG_PART_JIFFIES);
-               return;
+               flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+               got_alert = ssif_info->got_alert;
+               if (got_alert) {
+                       ssif_info->got_alert = false;
+                       ssif_info->waiting_alert = false;
+               }
+
+               if (got_alert) {
+                       ipmi_ssif_unlock_cond(ssif_info, flags);
+                       /* The alert already happened, try now. */
+                       retry_timeout((unsigned long) ssif_info);
+               } else {
+                       /* Wait a jiffie then request the next message */
+                       ssif_info->waiting_alert = true;
+                       ssif_info->retries_left = SSIF_RECV_RETRIES;
+                       ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC;
+                       mod_timer(&ssif_info->retry_timer,
+                                 jiffies + SSIF_MSG_PART_JIFFIES);
+                       ipmi_ssif_unlock_cond(ssif_info, flags);
+               }
        }
 }
 
@@ -880,6 +948,8 @@ static int start_resend(struct ssif_info *ssif_info)
        int rv;
        int command;
 
+       ssif_info->got_alert = false;
+
        if (ssif_info->data_len > 32) {
                command = SSIF_IPMI_MULTI_PART_REQUEST_START;
                ssif_info->multi_data = ssif_info->data;
@@ -1242,6 +1312,8 @@ static int smi_stats_proc_show(struct seq_file *m, void *v)
                   ssif_get_stat(ssif_info, events));
        seq_printf(m, "watchdog_pretimeouts:   %u\n",
                   ssif_get_stat(ssif_info, watchdog_pretimeouts));
+       seq_printf(m, "alerts:                 %u\n",
+                  ssif_get_stat(ssif_info, alerts));
        return 0;
 }
 
@@ -1324,6 +1396,12 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev)
        return false;
 }
 
+/*
+ * Global enables we care about.
+ */
+#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
+                            IPMI_BMC_EVT_MSG_INTR)
+
 static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        unsigned char     msg[3];
@@ -1454,6 +1532,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
                goto found;
        }
 
+       ssif_info->global_enables = resp[3];
+
        if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) {
                ssif_info->has_event_buffer = true;
                /* buffer is already enabled, nothing to do. */
@@ -1462,18 +1542,37 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        msg[0] = IPMI_NETFN_APP_REQUEST << 2;
        msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
-       msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF;
+       msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF;
        rv = do_cmd(client, 3, msg, &len, resp);
        if (rv || (len < 2)) {
-               pr_warn(PFX "Error getting global enables: %d %d %2.2x\n",
+               pr_warn(PFX "Error setting global enables: %d %d %2.2x\n",
                        rv, len, resp[2]);
                rv = 0; /* Not fatal */
                goto found;
        }
 
-       if (resp[2] == 0)
+       if (resp[2] == 0) {
                /* A successful return means the event buffer is supported. */
                ssif_info->has_event_buffer = true;
+               ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF;
+       }
+
+       msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+       msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
+       msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
+       rv = do_cmd(client, 3, msg, &len, resp);
+       if (rv || (len < 2)) {
+               pr_warn(PFX "Error setting global enables: %d %d %2.2x\n",
+                       rv, len, resp[2]);
+               rv = 0; /* Not fatal */
+               goto found;
+       }
+
+       if (resp[2] == 0) {
+               /* A successful return means the alert is supported. */
+               ssif_info->supports_alert = true;
+               ssif_info->global_enables |= IPMI_BMC_RCV_MSG_INTR;
+       }
 
  found:
        ssif_info->intf_num = atomic_inc_return(&next_intf);
@@ -1831,6 +1930,7 @@ static struct i2c_driver ssif_i2c_driver = {
        },
        .probe          = ssif_probe,
        .remove         = ssif_remove,
+       .alert          = ssif_alert,
        .id_table       = ssif_id,
        .detect         = ssif_detect
 };