drivers: i3c: shell: add i2c scan command
This adds a workaround command to perform an I2C scan on
an I3C bus.
Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
diff --git a/drivers/i3c/i3c_shell.c b/drivers/i3c/i3c_shell.c
index 8cd2c2b..a519165 100644
--- a/drivers/i3c/i3c_shell.c
+++ b/drivers/i3c/i3c_shell.c
@@ -1343,6 +1343,103 @@
return ret;
}
+/*
+ * This is a workaround command to perform an I2C Scan which is not as
+ * simple on an I3C bus as it is with the I2C Shell.
+ *
+ * This will print "I3" if an address is already assigned for an I3C
+ * device and it will print "I2" if an address is already assigned for
+ * an I2C device.
+ *
+ * This sends I2C messages without any data (i.e. stop condition after
+ * sending just the address). If there is an ACK for the address, it
+ * is assumed there is a device present.
+ *
+ * WARNING: As there is no standard I2C detection command, this code
+ * uses arbitrary SMBus commands (namely SMBus quick write and SMBus
+ * receive byte) to probe for devices. This operation can confuse
+ * your I2C bus, cause data loss, and is known to corrupt the Atmel
+ * AT24RF08 EEPROM found on many IBM Thinkpad laptops.
+ *
+ * https://manpages.debian.org/buster/i2c-tools/i2cdetect.8.en.html
+ */
+/* i3c i2c_scan <device> */
+static int cmd_i3c_i2c_scan(const struct shell *shell_ctx, size_t argc, char **argv)
+{
+ const struct device *dev;
+ struct i3c_driver_data *data;
+ enum i3c_addr_slot_status slot;
+ uint8_t cnt = 0, first = 0x04, last = 0x77;
+
+ dev = device_get_binding(argv[ARGV_DEV]);
+
+ if (!dev) {
+ shell_error(shell_ctx, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
+ return -ENODEV;
+ }
+
+ data = (struct i3c_driver_data *)dev->data;
+
+ shell_print(shell_ctx, " 0 1 2 3 4 5 6 7 8 9 a b c d e f");
+ for (uint8_t i = 0; i <= last; i += 16) {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "%02x: ", i);
+ for (uint8_t j = 0; j < 16; j++) {
+ if (i + j < first || i + j > last) {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, " ");
+ continue;
+ }
+
+ slot = i3c_addr_slots_status(&data->attached_dev.addr_slots, i + j);
+ if (slot == I3C_ADDR_SLOT_STATUS_FREE) {
+ struct i2c_msg msgs[1];
+ uint8_t dst;
+ int ret;
+ struct i3c_i2c_device_desc desc = {
+ .bus = dev,
+ .addr = i + j,
+ .lvr = 0x00,
+ };
+
+ ret = i3c_attach_i2c_device(&desc);
+ if (ret < 0) {
+ shell_error(shell_ctx,
+ "I3C: unable to attach I2C addr 0x%02x.",
+ desc.addr);
+ }
+
+ /* Send the address to read from */
+ msgs[0].buf = &dst;
+ msgs[0].len = 0U;
+ msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
+ if (i2c_transfer(dev, &msgs[0], 1, i + j) == 0) {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "%02x ", i + j);
+ ++cnt;
+ } else {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "-- ");
+ }
+
+ ret = i3c_detach_i2c_device(&desc);
+ if (ret < 0) {
+ shell_error(shell_ctx,
+ "I3C: unable to detach I2C addr 0x%02x.",
+ desc.addr);
+ }
+ } else if (slot == I3C_ADDR_SLOT_STATUS_I3C_DEV) {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "I3 ");
+ } else if (slot == I3C_ADDR_SLOT_STATUS_I2C_DEV) {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "I2 ");
+ } else {
+ shell_fprintf(shell_ctx, SHELL_NORMAL, "-- ");
+ }
+ }
+ shell_print(shell_ctx, "");
+ }
+
+ shell_print(shell_ctx, "%u additional devices found on %s", cnt, argv[ARGV_DEV]);
+
+ return 0;
+}
+
static void i3c_device_list_target_name_get(size_t idx, struct shell_static_entry *entry)
{
if (idx < ARRAY_SIZE(i3c_list)) {
@@ -1518,6 +1615,10 @@
"Detach I2C device from the bus\n"
"Usage: i2c_detach <device> <addr>",
cmd_i3c_i2c_detach, 3, 0),
+ SHELL_CMD_ARG(i2c_scan, &dsub_i3c_device_name,
+ "Scan I2C devices\n"
+ "Usage: i2c_scan <device>",
+ cmd_i3c_i2c_scan, 2, 0),
SHELL_CMD_ARG(ccc, &sub_i3c_ccc_cmds,
"Send I3C CCC\n"
"Usage: ccc <sub cmd>",