Skip to content

Commit

Permalink
ipmi: Add alert handling to SSIF
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
cminyard committed May 6, 2015
1 parent 9f81270 commit 9162052
Showing 1 changed file with 116 additions and 16 deletions.
132 changes: 116 additions & 16 deletions drivers/char/ipmi/ipmi_ssif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
}

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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. */
Expand All @@ -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);
Expand Down Expand Up @@ -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
};
Expand Down

0 comments on commit 9162052

Please sign in to comment.