aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorCorey Minyard <cminyard@mvista.com>2015-04-24 07:46:06 -0500
committerCorey Minyard <cminyard@mvista.com>2015-05-05 19:36:38 -0500
commit9162052173d2381e2bbabc224c3c1457acb4c54c (patch)
treefac1fbe196644fc3d1cbbc91bc77160c896bc0b0 /drivers/char
parentipmi: Fix a problem that messages are not issued in run_to_completion mode (diff)
downloadlinux-dev-9162052173d2381e2bbabc224c3c1457acb4c54c.tar.xz
linux-dev-9162052173d2381e2bbabc224c3c1457acb4c54c.zip
ipmi: Add alert handling to SSIF
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>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c132
1 files changed, 116 insertions, 16 deletions
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index f6ea4fa444b3..5b82d9947ec5 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -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
};