// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_ssif.c * * The interface to the IPMI driver for SMBus access to a SMBus * compliant device. Called SSIF by the IPMI spec. * * Author: Intel Corporation * Todd Davis <todd.c.davis@intel.com> * * Rewritten by Corey Minyard <minyard@acm.org> to support the * non-blocking I2C interface, add support for multi-part * transactions, add PEC support, and general clenaup. * * Copyright 2003 Intel Corporation * Copyright 2005 MontaVista Software
*/
/* * This file holds the "policy" for the interface to the SSIF state * machine. It does the configuration, handles timers and interrupts, * and drives the real SSIF state machine.
*/
/* ssif_debug is a bit-field * SSIF_DEBUG_MSG - commands and their responses * SSIF_DEBUG_STATES - message states * SSIF_DEBUG_TIMING - Measure times between events in the driver
*/ #define SSIF_DEBUG_TIMING 4 #define SSIF_DEBUG_STATE 2 #define SSIF_DEBUG_MSG 1 #define SSIF_NODEBUG 0 #define SSIF_DEFAULT_DEBUG (SSIF_NODEBUG)
/* * Timer values
*/ #define SSIF_MSG_USEC 60000 /* 60ms between message tries (T3). */ #define SSIF_REQ_RETRY_USEC 60000 /* 60ms between send retries (T6). */ #define SSIF_MSG_PART_USEC 5000 /* 5ms for a message part */
/* How many times to we retry sending/receiving the message. */ #define SSIF_SEND_RETRIES 5 #define SSIF_RECV_RETRIES 250
/* * Timeout for the watch, only used for get flag timer.
*/ #define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10) #define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250)
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ union ipmi_smi_info_union addr_info;
/* * Flags from the last GET_MSG_FLAGS command, used when an ATTN * is set to hold the flags until we are done handling everything * from the flags.
*/ #define RECEIVE_MSG_AVAIL 0x01 #define EVENT_MSG_BUFFER_FULL 0x02 #define WDT_PRE_TIMEOUT_INT 0x08 unsignedchar msg_flags;
long watch_timeout; /* Timeout for flags check, 0 if off. */ struct timer_list watch_timer; /* Flag fetch timer. */
/* Info from SSIF cmd */ unsignedchar max_xmit_msg_size; unsignedchar max_recv_msg_size; bool cmd8_works; /* See test_multipart_messages() for details. */ unsignedint multi_support; int supports_pec;
/* Make it a response */
msg->rsp[0] = msg->data[0] | 4;
msg->rsp[1] = msg->data[1];
msg->rsp[2] = 0xFF; /* Unknown error. */
msg->rsp_size = 3;
deliver_recv_msg(ssif_info, msg);
}
/* * Must be called with the message lock held. This will release the * message lock. Note that the caller will check IS_SSIF_IDLE and * start a new operation, so there is no need to check for new * messages to start in here.
*/ staticvoid start_clear_flags(struct ssif_info *ssif_info, unsignedlong *flags)
{ unsignedchar msg[3];
/* Make sure the watchdog pre-timeout flag is not set at startup. */
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;
msg[2] = WDT_PRE_TIMEOUT_INT;
if (start_send(ssif_info, msg, 3) != 0) { /* Error, just go to normal state. */
ssif_info->ssif_state = SSIF_IDLE;
}
}
/* * Must be called with the message lock held. This will release the * message lock. Note that the caller will check IS_SSIF_IDLE and * start a new operation, so there is no need to check for new * messages to start in here.
*/ staticvoid handle_flags(struct ssif_info *ssif_info, unsignedlong *flags)
{ if (ssif_info->msg_flags & WDT_PRE_TIMEOUT_INT) { /* Watchdog pre-timeout */
ssif_inc_stat(ssif_info, watchdog_pretimeouts);
start_clear_flags(ssif_info, flags);
ipmi_smi_watchdog_pretimeout(ssif_info->intf);
} elseif (ssif_info->msg_flags & RECEIVE_MSG_AVAIL) /* Messages available. */
start_recv_msg_fetch(ssif_info, flags); elseif (ssif_info->msg_flags & EVENT_MSG_BUFFER_FULL) /* Events available. */
start_event_fetch(ssif_info, flags); else {
ssif_info->ssif_state = SSIF_IDLE;
ipmi_ssif_unlock_cond(ssif_info, flags);
}
}
/* Wait for something to do */
result = wait_for_completion_interruptible(
&ssif_info->wake_thread); if (result == -ERESTARTSYS) continue;
init_completion(&ssif_info->wake_thread);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "%s: Error %d\n", __func__, result);
len = 0; goto continue_op;
}
if ((len > 1) && (ssif_info->multi_pos == 0)
&& (data[0] == 0x00) && (data[1] == 0x01)) { /* Start of multi-part read. Start the next transaction. */ int i;
ssif_inc_stat(ssif_info, received_message_parts);
/* Remove the multi-part read marker. */
len -= 2;
data += 2; for (i = 0; i < len; i++)
ssif_info->data[i] = data[i];
ssif_info->multi_len = len;
ssif_info->multi_pos = 1;
ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ,
SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE,
ssif_info->recv, I2C_SMBUS_BLOCK_DATA); return;
} elseif (ssif_info->multi_pos) { /* Middle of multi-part read. Start the next transaction. */ int i; unsignedchar blocknum;
if (len == 0) {
result = -EIO; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "Middle message with no data\n");
goto continue_op;
}
blocknum = data[0];
len--;
data++;
if (blocknum != 0xff && len != 31) { /* All blocks but the last must have 31 data bytes. */
result = -EIO; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "Received middle message <31\n");
goto continue_op;
}
if (ssif_info->multi_len + len > IPMI_MAX_MSG_LENGTH) { /* Received message too big, abort the operation. */
result = -E2BIG; if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "Received message too big\n");
goto continue_op;
}
for (i = 0; i < len; i++)
ssif_info->data[i + ssif_info->multi_len] = data[i];
ssif_info->multi_len += len; if (blocknum == 0xff) { /* End of read */
len = ssif_info->multi_len;
data = ssif_info->data;
} elseif (blocknum + 1 != ssif_info->multi_pos) { /* * Out of sequence block, just abort. Block * numbers start at zero for the second block, * but multi_pos starts at one, so the +1.
*/ if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "Received message out of sequence, expected %u, got %u\n",
ssif_info->multi_pos - 1, blocknum);
result = -EIO;
} else {
ssif_inc_stat(ssif_info, received_message_parts);
case SSIF_GETTING_EVENTS: if (!msg) { /* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev, "No message set while getting events\n");
ipmi_ssif_unlock_cond(ssif_info, flags); break;
}
/* Take off the event flag. */
ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
handle_flags(ssif_info, flags);
} elseif (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) {
dev_warn(&ssif_info->client->dev, "Invalid response getting events: %x %x\n",
msg->rsp[0], msg->rsp[1]);
msg->done(msg); /* Take off the event flag. */
ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
handle_flags(ssif_info, flags);
} else {
handle_flags(ssif_info, flags);
ssif_inc_stat(ssif_info, events);
deliver_recv_msg(ssif_info, msg);
} break;
case SSIF_GETTING_MESSAGES: if (!msg) { /* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev, "No message set while getting messages\n");
ipmi_ssif_unlock_cond(ssif_info, flags); break;
}
/* Take off the msg flag. */
ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
handle_flags(ssif_info, flags);
} else {
ssif_inc_stat(ssif_info, incoming_messages);
handle_flags(ssif_info, flags);
deliver_recv_msg(ssif_info, msg);
} break;
default: /* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev, "Invalid state in message done handling: %d\n",
ssif_info->ssif_state);
ipmi_ssif_unlock_cond(ssif_info, flags);
}
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
dev_dbg(&ssif_info->client->dev, "DONE 2: state = %d.\n", ssif_info->ssif_state);
}
staticvoid msg_written_handler(struct ssif_info *ssif_info, int result, unsignedchar *data, unsignedint len)
{ /* We are single-threaded here, so no need for a lock. */ if (result < 0) {
ssif_info->retries_left--; if (ssif_info->retries_left > 0) { /* * Wait the retry timeout time per the spec, * then redo the send.
*/
ssif_info->do_resend = true;
mod_timer(&ssif_info->retry_timer,
jiffies + SSIF_REQ_RETRY_JIFFIES); return;
}
ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
dev_dbg(&ssif_info->client->dev, "%s: Out of retries\n", __func__);
if (ssif_info->multi_data) { /* * In the middle of a multi-data write. See the comment * in the SSIF_MULTI_n_PART case in the probe function * for details on the intricacies of this.
*/ int left, to_write; unsignedchar *data_to_send; unsignedchar cmd;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags); if (ssif_info->got_alert) { /* The result is already ready, just start it. */
ssif_info->got_alert = false;
ipmi_ssif_unlock_cond(ssif_info, flags);
start_get(ssif_info);
} else { /* Wait a jiffy then request the next message */
ssif_info->waiting_alert = true;
ssif_info->retries_left = SSIF_RECV_RETRIES; if (!ssif_info->stopping)
mod_timer(&ssif_info->retry_timer,
jiffies + SSIF_MSG_PART_JIFFIES);
ipmi_ssif_unlock_cond(ssif_info, flags);
}
}
}
staticvoid start_resend(struct ssif_info *ssif_info)
{ 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;
ssif_info->multi_len = ssif_info->data_len; /* * Subtle thing, this is 32, not 33, because we will * overwrite the thing at position 32 (which was just * transmitted) with the new length.
*/
ssif_info->multi_pos = 32;
ssif_info->data[0] = 32;
} else {
ssif_info->multi_data = NULL;
command = SSIF_IPMI_REQUEST;
ssif_info->data[0] = ssif_info->data_len;
}
/* Must be called with the message lock held. */ staticvoid start_next_msg(struct ssif_info *ssif_info, unsignedlong *flags)
{ struct ipmi_smi_msg *msg; unsignedlong oflags;
restart: if (!IS_SSIF_IDLE(ssif_info)) {
ipmi_ssif_unlock_cond(ssif_info, flags); return;
}
if (!ssif_info->waiting_msg) {
ssif_info->curr_msg = NULL;
ipmi_ssif_unlock_cond(ssif_info, flags);
} else { int rv;
/* * Upper layer is changing the flag saying whether we need to request * flags periodically or not.
*/ staticvoid ssif_set_need_watch(void *send_info, unsignedint watch_mask)
{ struct ssif_info *ssif_info = send_info; unsignedlong oflags, *flags; long timeout = 0;
staticunsignedshort addr[MAX_SSIF_BMCS]; staticint num_addrs;
module_param_array(addr, ushort, &num_addrs, 0);
MODULE_PARM_DESC(addr, "The addresses to scan for IPMI BMCs on the SSIFs.");
staticchar *adapter_name[MAX_SSIF_BMCS]; staticint num_adapter_names;
module_param_array(adapter_name, charp, &num_adapter_names, 0);
MODULE_PARM_DESC(adapter_name, "The string name of the I2C device that has the BMC. By default all devices are scanned.");
staticint slave_addrs[MAX_SSIF_BMCS]; staticint num_slave_addrs;
module_param_array(slave_addrs, int, &num_slave_addrs, 0);
MODULE_PARM_DESC(slave_addrs, "The default IPMB slave address for the controller.");
staticbool alerts_broken;
module_param(alerts_broken, bool, 0);
MODULE_PARM_DESC(alerts_broken, "Don't enable alerts for the controller.");
/* * Bit 0 enables message debugging, bit 1 enables state debugging, and * bit 2 enables timing debugging. This is an array indexed by * interface number"
*/ staticint dbg[MAX_SSIF_BMCS]; staticint num_dbg;
module_param_array(dbg, int, &num_dbg, 0);
MODULE_PARM_DESC(dbg, "Turn on debugging.");
staticbool ssif_dbg_probe;
module_param_named(dbg_probe, ssif_dbg_probe, bool, 0);
MODULE_PARM_DESC(dbg_probe, "Enable debugging of probing of adapters.");
staticbool ssif_tryacpi = true;
module_param_named(tryacpi, ssif_tryacpi, bool, 0);
MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the default scan of the interfaces identified via ACPI");
staticbool ssif_trydmi = true;
module_param_named(trydmi, ssif_trydmi, bool, 0);
MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of the interfaces identified via DMI (SMBIOS)");
/* * After this point, we won't deliver anything asynchronously * to the message handler. We can unregister ourself.
*/
ipmi_unregister_smi(ssif_info->intf);
restart:
list_for_each_entry(info, &ssif_infos, link) { if (info->binfo.addr == addr) { if (info->addr_src == SI_SMBIOS && !info->adapter_name)
info->adapter_name = kstrdup(adapter_name,
GFP_KERNEL);
if (info->adapter_name || adapter_name) { if (!info->adapter_name != !adapter_name) { /* One is NULL and one is not */ continue;
} if (adapter_name &&
strcmp_nospace(info->adapter_name,
adapter_name)) /* Names do not match */ continue;
}
found = info; break;
}
}
if (!found && match_null_name) { /* Try to get an exact match first, then try with a NULL name */
adapter_name = NULL;
match_null_name = false; goto restart;
}
retry_write:
ret = i2c_smbus_write_block_data(client,
SSIF_IPMI_MULTI_PART_REQUEST_START,
32, msg); if (ret) {
retry_cnt--; if (retry_cnt > 0) {
msleep(SSIF_REQ_RETRY_MSEC); goto retry_write;
}
dev_err(&client->dev, "Could not write multi-part start, though the BMC said it could handle it. Just limit sends to one part.\n"); return ret;
}
if (!do_middle) return 0;
ret = i2c_smbus_write_block_data(client,
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
32, msg + 32); if (ret) {
dev_err(&client->dev, "Could not write multi-part middle, though the BMC said it could handle it. Just limit sends to one part.\n"); return ret;
}
/* * The specification is all messed up dealing with sending * multi-part messages. Per what the specification says, it * is impossible to send a message that is a multiple of 32 * bytes, except for 32 itself. It talks about a "start" * transaction (cmd=6) that must be 32 bytes, "middle" * transaction (cmd=7) that must be 32 bytes, and an "end" * transaction. The "end" transaction is shown as cmd=7 in * the text, but if that's the case there is no way to * differentiate between a middle and end part except the * length being less than 32. But there is a table at the far * end of the section (that I had never noticed until someone * pointed it out to me) that mentions it as cmd=8. * * After some thought, I think the example is wrong and the * end transaction should be cmd=8. But some systems don't * implement cmd=8, they use a zero-length end transaction, * even though that violates the SMBus specification. * * So, to work around this, this code tests if cmd=8 works. * If it does, then we use that. If not, it tests zero- * byte end transactions. If that works, good. If not, * we only allow 63-byte transactions max.
*/
ret = start_multipart_test(client, msg, do_middle); if (ret) goto out_no_multi_part;
ret = i2c_smbus_write_block_data(client,
SSIF_IPMI_MULTI_PART_REQUEST_END,
1, msg + 64);
if (!ret)
ret = read_response(client, resp);
if (ret > 0) { /* End transactions work, we are good. */
ssif_info->cmd8_works = true; return;
}
ret = start_multipart_test(client, msg, do_middle); if (ret) {
dev_err(&client->dev, "Second multipart test failed.\n"); goto out_no_multi_part;
}
ret = i2c_smbus_write_block_data(client,
SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE,
0, msg + 64); if (!ret)
ret = read_response(client, resp); if (ret > 0) /* Zero-size end parts work, use those. */ return;
/* Limit to 63 bytes and use a short middle command to mark the end. */ if (ssif_info->max_xmit_msg_size > 63)
ssif_info->max_xmit_msg_size = 63; return;
/* * Prefer ACPI over SMBIOS, if both are available. * So if we get an ACPI interface and have already registered a SMBIOS * interface at the same address, remove the SMBIOS and add the ACPI one.
*/ staticint ssif_check_and_remove(struct i2c_client *client, struct ssif_info *ssif_info)
{ struct ssif_addr_info *info;
list_for_each_entry(info, &ssif_infos, link) { if (!info->client) return 0; if (!strcmp(info->adapter_name, client->adapter->name) &&
info->binfo.addr == client->addr) { if (info->addr_src == SI_ACPI) return -EEXIST;
if (ssif_info->addr_source == SI_ACPI &&
info->addr_src == SI_SMBIOS) {
dev_info(&client->dev, "Removing %s-specified SSIF interface in favor of ACPI\n",
ipmi_addr_src_to_str(info->addr_src));
ssif_remove_dup(info->client); return 0;
}
}
} return 0;
}
staticint ssif_probe(struct i2c_client *client)
{ unsignedchar msg[3]; unsignedchar *resp; struct ssif_info *ssif_info; int rv = 0; int len = 0; int i;
u8 slave_addr = 0; struct ssif_addr_info *addr_info = NULL;
/* * Send a get device id command and validate its response to * make sure a valid BMC is there.
*/
rv = ssif_detect(client, NULL); if (rv) {
dev_err(&client->dev, "Not present\n"); goto out;
}
/* Now check for system interface capabilities */
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD;
msg[2] = 0; /* SSIF */
rv = do_cmd(client, 3, msg, &len, resp); if (!rv && (len >= 3) && (resp[2] == 0)) { if (len < 7) { if (ssif_dbg_probe)
dev_dbg(&ssif_info->client->dev, "SSIF info too short: %d\n", len); goto no_support;
}
/* Got a good SSIF response, handle it. */
ssif_info->max_xmit_msg_size = resp[5];
ssif_info->max_recv_msg_size = resp[6];
ssif_info->multi_support = (resp[4] >> 6) & 0x3;
ssif_info->supports_pec = (resp[4] >> 3) & 0x1;
/* Sanitize the data */ switch (ssif_info->multi_support) { case SSIF_NO_MULTI: if (ssif_info->max_xmit_msg_size > 32)
ssif_info->max_xmit_msg_size = 32; if (ssif_info->max_recv_msg_size > 32)
ssif_info->max_recv_msg_size = 32; break;
case SSIF_MULTI_2_PART: if (ssif_info->max_xmit_msg_size > 63)
ssif_info->max_xmit_msg_size = 63; if (ssif_info->max_recv_msg_size > 62)
ssif_info->max_recv_msg_size = 62; break;
case SSIF_MULTI_n_PART: /* We take whatever size given, but do some testing. */ break;
default: /* Data is not sane, just give up. */ goto no_support;
}
} else {
no_support: /* Assume no multi-part or PEC support */
dev_info(&ssif_info->client->dev, "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
rv, len, resp[2]);
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;
}
/* Some systems don't behave well if you enable alerts. */ if (alerts_broken) 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: if (ssif_dbg_probe) {
dev_dbg(&ssif_info->client->dev, "%s: i2c_probe found device at i2c address %x\n",
__func__, client->addr);
}
list_for_each_entry(info, &ssif_infos, link) { unsignedshort addr = info->binfo.addr; int j;
for (j = 0; j < i; j++) { if (address_list[j] == addr) /* Found a dup. */ break;
} if (j == i) /* Didn't find it in the list. */
address_list[i++] = addr;
}
address_list[i] = I2C_CLIENT_END;
staticint __init init_ipmi_ssif(void)
{ int i; int rv;
if (initialized) return 0;
pr_info("IPMI SSIF Interface driver\n");
/* build list for i2c from addr list */ for (i = 0; i < num_addrs; i++) {
rv = new_ssif_client(addr[i], adapter_name[i],
dbg[i], slave_addrs[i],
SI_HARDCODED, NULL); if (rv)
pr_err("Couldn't add hardcoded device at addr 0x%x\n",
addr[i]);
}
if (ssif_tryacpi)
ssif_i2c_driver.driver.acpi_match_table =
ACPI_PTR(ssif_acpi_match);
if (ssif_trydmi) {
rv = platform_driver_register(&ipmi_driver); if (rv)
pr_err("Unable to register driver: %d\n", rv); else
platform_registered = true;
}
MODULE_ALIAS("platform:dmi-ipmi-ssif");
MODULE_AUTHOR("Todd C Davis , Corey Minyard ");
MODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus");
MODULE_LICENSE("GPL");
Messung V0.5
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.