summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-18 09:33:54 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-18 09:33:54 -0700
commit1e769656963e0329b91d32ec76955e077966b603 (patch)
tree4b8c2eb03a1c13d127c672f9472c8b8342f4fded /drivers
parentdf8f6181ab57d65a99e61fcfc5be22a42df58642 (diff)
parent75c486cb1bcaa1a3ec3a6438498176a3a4998ae4 (diff)
Merge tag 'for-linus-7.1-1' of https://github.com/cminyard/linux-ipmi
Pull ipmi updates from Corey Minyard: "Small updates and fixes (mostly to the BMC software): - Fix one issue in the host side driver where a kthread can be left running on a specific memory allocation failre at probe time - Replace system_wq with system_percpu_wq so system_wq can eventually go away" * tag 'for-linus-7.1-1' of https://github.com/cminyard/linux-ipmi: ipmi:ssif: Clean up kthread on errors ipmi:ssif: Remove unnecessary indention ipmi: ssif_bmc: Fix KUnit test link failure when KUNIT=m ipmi: ssif_bmc: add unit test for state machine ipmi: ssif_bmc: change log level to dbg in irq callback ipmi: ssif_bmc: fix message desynchronization after truncated response ipmi: ssif_bmc: fix missing check for copy_to_user() partial failure ipmi: ssif_bmc: cancel response timer on remove ipmi: Replace use of system_wq with system_percpu_wq
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/ipmi/Kconfig10
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c10
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c41
-rw-r--r--drivers/char/ipmi/ssif_bmc.c405
4 files changed, 435 insertions, 31 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 92bed266d07c..669f76000197 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -187,6 +187,16 @@ config SSIF_IPMI_BMC
The driver implements the BMC side of the SMBus system
interface (SSIF).
+config SSIF_IPMI_BMC_KUNIT_TEST
+ bool "KUnit tests for SSIF IPMI BMC driver" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ depends on SSIF_IPMI_BMC
+ default KUNIT_ALL_TESTS
+ help
+ This option builds unit tests that exercise the SSIF BMC state
+ machine, including request handling, response transmission,
+ and error paths such as aborted or truncated transfers.
+
config IPMB_DEVICE_INTERFACE
tristate 'IPMB Interface handler'
depends on I2C
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index c41f51c82edd..869ac87a4b6a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -987,7 +987,7 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
mutex_lock(&intf->user_msgs_mutex);
list_add_tail(&msg->link, &intf->user_msgs);
mutex_unlock(&intf->user_msgs_mutex);
- queue_work(system_wq, &intf->smi_work);
+ queue_work(system_percpu_wq, &intf->smi_work);
}
return rv;
@@ -4977,7 +4977,7 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
if (run_to_completion)
smi_work(&intf->smi_work);
else
- queue_work(system_wq, &intf->smi_work);
+ queue_work(system_percpu_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_msg_received);
@@ -4987,7 +4987,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf)
return;
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
- queue_work(system_wq, &intf->smi_work);
+ queue_work(system_percpu_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
@@ -5162,7 +5162,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
flags);
}
- queue_work(system_wq, &intf->smi_work);
+ queue_work(system_percpu_wq, &intf->smi_work);
return need_timer;
}
@@ -5218,7 +5218,7 @@ static void ipmi_timeout(struct timer_list *unused)
if (atomic_read(&stop_operation))
return;
- queue_work(system_wq, &ipmi_timer_work);
+ queue_work(system_percpu_wq, &ipmi_timer_work);
}
static void need_waiter(struct ipmi_smi *intf)
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 37a5cb5c53f1..b49500a1bd36 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1268,8 +1268,10 @@ static void shutdown_ssif(void *send_info)
ssif_info->stopping = true;
timer_delete_sync(&ssif_info->watch_timer);
timer_delete_sync(&ssif_info->retry_timer);
- if (ssif_info->thread)
+ if (ssif_info->thread) {
kthread_stop(ssif_info->thread);
+ ssif_info->thread = NULL;
+ }
}
static void ssif_remove(struct i2c_client *client)
@@ -1658,6 +1660,7 @@ static int ssif_probe(struct i2c_client *client)
int len = 0;
int i;
u8 slave_addr = 0;
+ unsigned int thread_num;
struct ssif_addr_info *addr_info = NULL;
mutex_lock(&ssif_infos_mutex);
@@ -1876,22 +1879,17 @@ static int ssif_probe(struct i2c_client *client)
ssif_info->handlers.request_events = request_events;
ssif_info->handlers.set_need_watch = ssif_set_need_watch;
- {
- unsigned int thread_num;
-
- thread_num = ((i2c_adapter_id(ssif_info->client->adapter)
- << 8) |
- ssif_info->client->addr);
- init_completion(&ssif_info->wake_thread);
- ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
- "kssif%4.4x", thread_num);
- if (IS_ERR(ssif_info->thread)) {
- rv = PTR_ERR(ssif_info->thread);
- dev_notice(&ssif_info->client->dev,
- "Could not start kernel thread: error %d\n",
- rv);
- goto out;
- }
+ thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) |
+ ssif_info->client->addr);
+ init_completion(&ssif_info->wake_thread);
+ ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
+ "kssif%4.4x", thread_num);
+ if (IS_ERR(ssif_info->thread)) {
+ rv = PTR_ERR(ssif_info->thread);
+ dev_notice(&ssif_info->client->dev,
+ "Could not start kernel thread: error %d\n",
+ rv);
+ goto out;
}
dev_set_drvdata(&ssif_info->client->dev, ssif_info);
@@ -1916,6 +1914,15 @@ static int ssif_probe(struct i2c_client *client)
out:
if (rv) {
+ /*
+ * If ipmi_register_smi() starts the interface, it will
+ * call shutdown and that will free the thread and set
+ * it to NULL. Otherwise it must be freed here.
+ */
+ if (ssif_info->thread) {
+ kthread_stop(ssif_info->thread);
+ ssif_info->thread = NULL;
+ }
if (addr_info)
addr_info->client = NULL;
diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c
index 7a52e3ea49ed..1df0e9284ad9 100644
--- a/drivers/char/ipmi/ssif_bmc.c
+++ b/drivers/char/ipmi/ssif_bmc.c
@@ -18,6 +18,9 @@
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/ipmi_ssif_bmc.h>
+#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST)
+#include <kunit/test.h>
+#endif
#define DEVICE_NAME "ipmi-ssif-host"
@@ -163,6 +166,8 @@ static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count,
spin_unlock_irqrestore(&ssif_bmc->lock, flags);
ret = copy_to_user(buf, &msg, count);
+ if (ret > 0)
+ ret = -EFAULT;
}
return (ret < 0) ? ret : count;
@@ -456,6 +461,15 @@ static bool supported_write_cmd(u8 cmd)
return false;
}
+static bool supported_write_start_cmd(u8 cmd)
+{
+ if (cmd == SSIF_IPMI_SINGLEPART_WRITE ||
+ cmd == SSIF_IPMI_MULTIPART_WRITE_START)
+ return true;
+
+ return false;
+}
+
/* Process the IPMI response that will be read by master */
static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
{
@@ -558,7 +572,7 @@ static void process_request_part(struct ssif_bmc_ctx *ssif_bmc)
len = ssif_bmc->request.len + part->length;
/* Do the bound check here, not allow the request len exceed 254 bytes */
if (len > IPMI_SSIF_PAYLOAD_MAX) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: Request exceeded 254 bytes, aborting");
/* Request too long, aborting */
ssif_bmc->aborting = true;
@@ -604,7 +618,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_RES_SENDING) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected READ REQUESTED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -613,7 +627,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
if (!supported_read_cmd(ssif_bmc->part_buf.smbus_cmd)) {
- dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x",
+ dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x",
ssif_bmc->part_buf.smbus_cmd);
ssif_bmc->aborting = true;
}
@@ -648,7 +662,7 @@ static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_SMBUS_CMD) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected READ PROCESSED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -673,7 +687,7 @@ static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_RES_SENDING) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected WRITE REQUEST in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -688,7 +702,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
{
if (ssif_bmc->state == SSIF_READY ||
ssif_bmc->state == SSIF_RES_SENDING) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected WRITE RECEIVED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -698,7 +712,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
if (!supported_write_cmd(ssif_bmc->part_buf.smbus_cmd)) {
- dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x",
+ dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x",
ssif_bmc->part_buf.smbus_cmd);
ssif_bmc->aborting = true;
}
@@ -707,6 +721,11 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state = SSIF_ABORTING;
else
ssif_bmc->state = SSIF_REQ_RECVING;
+ } else if (ssif_bmc->state == SSIF_ABORTING) {
+ if (supported_write_start_cmd(*val)) {
+ ssif_bmc->state = SSIF_SMBUS_CMD;
+ ssif_bmc->aborting = false;
+ }
}
/* This is response sending state */
@@ -722,7 +741,7 @@ static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_SMBUS_CMD ||
ssif_bmc->state == SSIF_ABORTING) {
- dev_warn(&ssif_bmc->client->dev,
+ dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected SLAVE STOP in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_READY;
@@ -789,7 +808,7 @@ static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8
break;
default:
- dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n");
+ dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n");
break;
}
@@ -843,6 +862,7 @@ static void ssif_bmc_remove(struct i2c_client *client)
{
struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client);
+ timer_delete_sync(&ssif_bmc->response_timer);
i2c_slave_unregister(client);
misc_deregister(&ssif_bmc->miscdev);
}
@@ -869,6 +889,373 @@ static struct i2c_driver ssif_bmc_driver = {
.id_table = ssif_bmc_id,
};
+#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST)
+struct ssif_bmc_test_ctx {
+ struct ssif_bmc_ctx ssif_bmc;
+ struct i2c_client client;
+ struct i2c_adapter adapter;
+ struct i2c_algorithm algo;
+};
+
+static int ssif_bmc_test_init(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx;
+
+ test_ctx = kunit_kzalloc(test, sizeof(*test_ctx), GFP_KERNEL);
+ if (!test_ctx)
+ return -ENOMEM;
+
+ test_ctx->adapter.algo = &test_ctx->algo;
+ test_ctx->client.addr = 0x20;
+ test_ctx->client.adapter = &test_ctx->adapter;
+
+ spin_lock_init(&test_ctx->ssif_bmc.lock);
+ init_waitqueue_head(&test_ctx->ssif_bmc.wait_queue);
+ test_ctx->ssif_bmc.client = &test_ctx->client;
+ i2c_set_clientdata(&test_ctx->client, &test_ctx->ssif_bmc);
+
+ test->priv = test_ctx;
+
+ return 0;
+}
+
+static void ssif_bmc_test_exit(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+
+ if (test_ctx->ssif_bmc.response_timer_inited)
+ timer_delete_sync(&test_ctx->ssif_bmc.response_timer);
+}
+
+static int ssif_bmc_test_run_event_val(struct ssif_bmc_test_ctx *test_ctx,
+ enum i2c_slave_event event,
+ u8 *value)
+{
+ return ssif_bmc_cb(&test_ctx->client, event, value);
+}
+
+static int ssif_bmc_test_run_event(struct ssif_bmc_test_ctx *test_ctx,
+ enum i2c_slave_event event, u8 value)
+{
+ return ssif_bmc_test_run_event_val(test_ctx, event, &value);
+}
+
+static void ssif_bmc_test_singlepart_req(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr));
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE);
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2);
+
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xaa);
+
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x55);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
+
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->busy);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 2);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xaa);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[1], 0x55);
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->response_timer_inited);
+}
+
+static void ssif_bmc_test_restart_write_without_stop(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xde), 0);
+
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING);
+
+ /* Write transaction, without stop, and new request coming */
+ ssif_bmc_test_singlepart_req(test);
+}
+
+
+static void ssif_bmc_test_restart_after_invalid_command(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xff), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
+
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING);
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting);
+
+ /* After An Invalid Command, expect could handle new request */
+ ssif_bmc_test_singlepart_req(test);
+}
+
+static void ssif_bmc_test_singlepart_read_response_completion(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+ u8 value;
+
+ ssif_bmc->state = SSIF_SMBUS_CMD;
+ ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
+ ssif_bmc->response.len = 2;
+ ssif_bmc->response.payload[0] = 0x11;
+ ssif_bmc->response.payload[1] = 0x22;
+ ssif_bmc->response_in_progress = true;
+ ssif_bmc->is_singlepart_read = true;
+ ssif_bmc->pec_support = true;
+
+ value = 0;
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, 2);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
+
+ value = 0;
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, 0x11);
+
+ value = 0;
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, 0x22);
+
+ value = 0;
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, ssif_bmc->part_buf.pec);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->response_in_progress);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->response.len, 0);
+}
+
+static void ssif_bmc_test_stop_during_start_discards_partial_request(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_START);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->msg_idx, 0);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x77), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
+
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x77);
+}
+
+static void ssif_bmc_test_read_interrupts_partial_write(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+ u8 value = 0xff;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xab), 0);
+
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 0);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xcd), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
+
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xcd);
+}
+
+static void ssif_bmc_test_write_interrupts_response_send(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+ u8 value = 0;
+
+ ssif_bmc->state = SSIF_SMBUS_CMD;
+ ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
+ ssif_bmc->response.len = 1;
+ ssif_bmc->response.payload[0] = 0x66;
+ ssif_bmc->response_in_progress = true;
+ ssif_bmc->is_singlepart_read = true;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
+
+ /* READ_REQUESTED transaction */
+ ssif_bmc_test_singlepart_req(test);
+}
+
+static void ssif_bmc_test_write_interrupts_response_sending(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+ u8 value = 0;
+
+ ssif_bmc->state = SSIF_SMBUS_CMD;
+ ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
+ ssif_bmc->response.len = 1;
+ ssif_bmc->response.payload[0] = 0x66;
+ ssif_bmc->response_in_progress = true;
+ ssif_bmc->is_singlepart_read = true;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
+ &value), 0);
+ KUNIT_EXPECT_EQ(test, value, 0x66);
+
+ /* READ_REQUESTED transaction */
+ ssif_bmc_test_singlepart_req(test);
+}
+
+static void ssif_bmc_test_timeout_interrupt_allows_retry(struct kunit *test)
+{
+ struct ssif_bmc_test_ctx *test_ctx = test->priv;
+ struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
+
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x21), 0);
+ KUNIT_ASSERT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
+
+ KUNIT_ASSERT_TRUE(test, timer_pending(&ssif_bmc->response_timer));
+ timer_delete_sync(&ssif_bmc->response_timer);
+ response_timeout(&ssif_bmc->response_timer);
+
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->busy);
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->response_timer_inited);
+
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
+ GET_8BIT_ADDR(test_ctx->client.addr)), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
+ SSIF_IPMI_SINGLEPART_WRITE), 0);
+ KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x22), 0);
+ KUNIT_EXPECT_EQ(test,
+ ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
+
+ KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
+ KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x22);
+}
+
+static struct kunit_case ssif_bmc_test_cases[] = {
+ KUNIT_CASE(ssif_bmc_test_singlepart_req),
+ KUNIT_CASE(ssif_bmc_test_restart_write_without_stop),
+ KUNIT_CASE(ssif_bmc_test_restart_after_invalid_command),
+ KUNIT_CASE(ssif_bmc_test_singlepart_read_response_completion),
+ KUNIT_CASE(ssif_bmc_test_stop_during_start_discards_partial_request),
+ KUNIT_CASE(ssif_bmc_test_read_interrupts_partial_write),
+ KUNIT_CASE(ssif_bmc_test_write_interrupts_response_send),
+ KUNIT_CASE(ssif_bmc_test_write_interrupts_response_sending),
+ KUNIT_CASE(ssif_bmc_test_timeout_interrupt_allows_retry),
+ {}
+};
+
+static struct kunit_suite ssif_bmc_test_suite = {
+ .name = "ssif_bmc_test",
+ .init = ssif_bmc_test_init,
+ .exit = ssif_bmc_test_exit,
+ .test_cases = ssif_bmc_test_cases,
+};
+
+kunit_test_suite(ssif_bmc_test_suite);
+#endif
+
module_i2c_driver(ssif_bmc_driver);
MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");