[PATCH v5 09/12] cmd: Add i3c command support.

dinesh.maniyam at altera.com dinesh.maniyam at altera.com
Tue May 13 12:19:10 CEST 2025


From: Dinesh Maniyam <dinesh.maniyam at altera.com>

Add i3c command file to support select, get i3c device
target list, read and write operation.

Signed-off-by: Dinesh Maniyam <dinesh.maniyam at altera.com>
---
 cmd/Kconfig                        |   6 +
 cmd/Makefile                       |   1 +
 cmd/i3c.c                          | 261 +++++++++++++++++++++++++++++
 doc/usage/cmd/i3c.rst              | 146 ++++++++++++++++
 doc/usage/index.rst                |   1 +
 drivers/i3c/master/dw-i3c-master.c |  35 +++-
 include/dw-i3c.h                   |   2 +
 include/i3c.h                      |  28 +++-
 8 files changed, 478 insertions(+), 2 deletions(-)
 create mode 100644 cmd/i3c.c
 create mode 100644 doc/usage/cmd/i3c.rst

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 642cc1116e8..551959731f0 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1327,6 +1327,12 @@ config CMD_I2C
 	help
 	  I2C support.
 
+config CMD_I3C
+	bool "i3c"
+	help
+	  Enable command to list i3c devices connected to the i3c controller
+	  and perform read and write on the connected i3c devices.
+
 config CMD_W1
 	depends on W1
 	default y if W1
diff --git a/cmd/Makefile b/cmd/Makefile
index 8410be576bb..082470fa104 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_CMD_GPIO) += gpio.o
 obj-$(CONFIG_CMD_HISTORY) += history.o
 obj-$(CONFIG_CMD_HVC) += smccc.o
 obj-$(CONFIG_CMD_I2C) += i2c.o
+obj-$(CONFIG_CMD_I3C) += i3c.o
 obj-$(CONFIG_CMD_IOTRACE) += iotrace.o
 obj-$(CONFIG_CMD_HASH) += hash.o
 obj-$(CONFIG_CMD_IDE) += ide.o disk.o
diff --git a/cmd/i3c.c b/cmd/i3c.c
new file mode 100644
index 00000000000..7d2bde714cd
--- /dev/null
+++ b/cmd/i3c.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ */
+
+#include <bootretry.h>
+#include <cli.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <dw-i3c.h>
+#include <edid.h>
+#include <errno.h>
+#include <hexdump.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <u-boot/crc.h>
+#include <linux/i3c/master.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+static struct udevice *currdev;
+static struct udevice *prevdev;
+static struct dw_i3c_master *master;
+
+static void low_to_high_bytes(void *data, size_t size)
+{
+	u8 *byte_data = data;
+	size_t start = 0;
+	size_t end = size - 1;
+
+	while (start < end) {
+		u8 temp = byte_data[start];
+
+		byte_data[start] = byte_data[end];
+		byte_data[end] = temp;
+		start++;
+		end--;
+	}
+}
+
+static int handle_i3c_select(const char *name)
+{
+	struct uclass *uc;
+	struct udevice *dev_list;
+	int ret = uclass_get_device_by_name(UCLASS_I3C, name, &currdev);
+
+	if (ret) {
+		currdev = prevdev;
+		if (!currdev) {
+			ret = uclass_get(UCLASS_I3C, &uc);
+			if (ret)
+				return CMD_RET_FAILURE;
+
+			uclass_foreach_dev(dev_list, uc)
+				printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
+
+			printf("i3c: Host controller not initialized: %s\n", name);
+			return CMD_RET_FAILURE;
+		}
+	} else {
+		master = dev_get_priv(currdev);
+		printf("i3c: Current controller: %s\n", currdev->name);
+		prevdev = currdev;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_list(void)
+{
+	struct uclass *uc;
+	struct udevice *dev_list;
+	int ret = uclass_get(UCLASS_I3C, &uc);
+
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	uclass_foreach_dev(dev_list, uc)
+		printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_current(void)
+{
+	if (!currdev)
+		printf("i3c: No current controller selected\n");
+	else
+		printf("i3c: Current controller: %s\n", currdev->name);
+
+	return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_device_list(void)
+{
+	if (!master) {
+		printf("i3c: No controller active\n");
+		return CMD_RET_FAILURE;
+	}
+
+	for (int i = 0; i < master->num_i3cdevs; i++) {
+		struct i3c_device_info *info = &master->i3cdev[i]->info;
+
+		printf("Device %d:\n", i);
+		printf("  Static Address  : 0x%02X\n", info->static_addr);
+		printf("  Dynamic Address : 0x%X\n", info->dyn_addr);
+		printf("  PID             : %016llx\n", info->pid);
+		printf("  BCR             : 0x%X\n", info->bcr);
+		printf("  DCR             : 0x%X\n", info->dcr);
+		printf("  Max Read DS     : 0x%X\n", info->max_read_ds);
+		printf("  Max Write DS    : 0x%X\n", info->max_write_ds);
+		printf("\n");
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_write(int argc, char *const argv[])
+{
+	if (argc < 5)
+		return CMD_RET_USAGE;
+
+	if (!currdev) {
+		printf("i3c: No I3C controller selected\n");
+		return CMD_RET_FAILURE;
+	}
+
+	u32 mem_addr = hextoul(argv[2], NULL);
+	u32 num_bytes = hextoul(argv[3], NULL);
+	u32 dev_num_val = hextoul(argv[4], NULL);
+
+	if (num_bytes == 0 || num_bytes > 4) {
+		printf("i3c: Length must be between 1 and 4\n");
+		return CMD_RET_USAGE;
+	}
+
+	if (dev_num_val > 0xFF) {
+		printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
+		return CMD_RET_USAGE;
+	}
+
+	u8 device_num = dev_num_val;
+	u8 *data = malloc(num_bytes);
+
+	if (!data) {
+		printf("i3c: Memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	memcpy(data, (void *)(uintptr_t)mem_addr, num_bytes);
+	low_to_high_bytes(data, num_bytes);
+
+	int ret = dm_i3c_write(currdev, device_num, data, num_bytes);
+
+	if (ret)
+		printf("i3c: Write failed: %d\n", ret);
+
+	free(data);
+	return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int handle_i3c_read(int argc, char *const argv[])
+{
+	if (argc < 5)
+		return CMD_RET_USAGE;
+
+	if (!currdev) {
+		printf("i3c: No I3C controller selected\n");
+		return CMD_RET_FAILURE;
+	}
+
+	u32 mem_addr = hextoul(argv[2], NULL);
+	u32 read_len = hextoul(argv[3], NULL);
+	u32 dev_num_val = hextoul(argv[4], NULL);
+
+	if (read_len == 0) {
+		printf("i3c: Read length must be greater than 0\n");
+		return CMD_RET_USAGE;
+	}
+
+	if (dev_num_val > 0xFF) {
+		printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
+		return CMD_RET_USAGE;
+	}
+
+	u8 device_num = dev_num_val;
+	u8 *rdata = malloc(read_len);
+
+	if (!rdata) {
+		printf("i3c: Memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	int ret = dm_i3c_read(currdev, device_num, rdata, read_len);
+
+	if (ret) {
+		printf("i3c: Read failed: %d\n", ret);
+		free(rdata);
+		return CMD_RET_FAILURE;
+	}
+
+	memcpy((void *)(uintptr_t)mem_addr, rdata, read_len);
+	print_hex_dump("i3c read: ", DUMP_PREFIX_OFFSET, 16, 1,
+		       (void *)(uintptr_t)mem_addr, read_len, false);
+
+	free(rdata);
+	return CMD_RET_SUCCESS;
+}
+
+static bool is_i3c_subcommand(const char *cmd)
+{
+	return !strcmp(cmd, "write") ||
+	       !strcmp(cmd, "read") ||
+	       !strcmp(cmd, "device_list") ||
+	       !strcmp(cmd, "list") ||
+	       !strcmp(cmd, "current");
+}
+
+static int do_i3c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	const char *subcmd = argv[1];
+
+	if (!is_i3c_subcommand(subcmd))
+		return handle_i3c_select(subcmd);
+
+	if (!currdev) {
+		printf("i3c: No I3C controller selected\n");
+		return CMD_RET_FAILURE;
+	}
+
+	if (!strcmp(subcmd, "list"))
+		return handle_i3c_list();
+	else if (!strcmp(subcmd, "current"))
+		return handle_i3c_current();
+	else if (!strcmp(subcmd, "device_list"))
+		return handle_i3c_device_list();
+	else if (!strcmp(subcmd, "write"))
+		return handle_i3c_write(argc, argv);
+	else if (!strcmp(subcmd, "read"))
+		return handle_i3c_read(argc, argv);
+
+	return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+	i3c, 5, 1, do_i3c,
+	"access the system i3c",
+	"i3c write <mem_addr> <length> <device_number> - write from memory to device\n"
+	"i3c read <mem_addr> <length> <device_number> - read from device to memory\n"
+	"i3c device_list - List valid target devices\n"
+	"i3c <host_controller> - Select i3c controller\n"
+	"i3c list - List all available i3c controllers\n"
+	"i3c current - Show current i3c controller"
+);
diff --git a/doc/usage/cmd/i3c.rst b/doc/usage/cmd/i3c.rst
new file mode 100644
index 00000000000..922fa3ea37c
--- /dev/null
+++ b/doc/usage/cmd/i3c.rst
@@ -0,0 +1,146 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. index::
+   single: i3c (command)
+
+i3c command
+===========
+
+Synopsis
+--------
+
+::
+
+    i3c <host_controller>
+    i3c current
+    i3c list
+    i3c device_list
+    i3c write <mem_addr> <length> <device_number>
+    i3c read <mem_addr> <length> <device_number>
+
+Description
+-----------
+
+The ``i3c`` command is used to probe the i3c host controller and perform
+read and write operations on the connected i3c devices.
+
+i3c current
+------------
+
+Display the currently selected i3c host controller.
+
+i3c list
+---------
+
+List all the i3c hosts defined in the device-tree.
+
+i3c device_list
+----------------
+
+List all the i3c devices' device number, static address, dynamic address,
+PID, BCR, DCR, Max Write Speed, and Max Read Speed of the probed i3c host
+controller.
+
+i3c write
+----------
+
+Perform a write operation from memory to the connected i3c device. The data
+is read from a specified memory address and written to the selected i3c
+device, which is identified by its device number.
+
+You need to provide the memory address (``mem_addr``), the length of data
+to be written (``length``), and the device number (``device_number``). The
+data in memory will be transferred to the device in the specified order.
+
+i3c read
+---------
+
+Perform a read operation from the connected i3c device to memory. The data
+is read from the selected i3c device and stored at the specified memory
+address. You need to provide the memory address (``mem_addr``), the length
+of data to be read (``length``), and the device number (``device_number``).
+
+The device will send the requested data, which is then written to the memory
+location you specified. This operation allows you to retrieve information
+from the device and use it in your application. The data is read in the
+order specified and can be multiple bytes.
+
+host_controller
+    The name of the i3c host controller defined in the device-tree.
+
+length
+    The size of the data to be read or written.
+
+device_number
+    The device number in the driver model of the device connected to the i3c
+    host controller.
+
+mem_addr
+    The start address in memory from which to read or write the data.
+
+Examples
+--------
+
+Probe the ``i3c0`` controller::
+
+    => i3c i3c0
+
+Display the current i3c host controller::
+
+    => i3c current
+
+Check the device number and PID of the connected devices::
+
+    => i3c device_list
+
+Perform write operations on the connected i3c device (device 0) from memory::
+
+    => i3c write 0x1000 4 0
+
+    This command reads 4 bytes of data from memory starting at address
+    ``0x1000`` and writes them to device 0, which is identified by its device
+    number in the driver model. Example data from memory could look like this:
+
+    ```
+    Data at 0x1000: 0xAA 0xBB 0xCC 0xDD
+    ```
+
+    The bytes `0xAA`, `0xBB`, `0xCC`, and `0xDD` will be written to device 0.
+
+Perform a read operation from device 0 to memory (multiple bytes)::
+
+    => i3c read 0x1000 4 0
+
+    This command reads 4 bytes of data from device 0 and writes them to
+    memory starting at address ``0x1000``.
+
+    Example output after reading 4 bytes from device 0:
+
+    ```
+    i3c Read:
+    00000000  AA BB CC DD
+    ```
+
+    The bytes `0xAA`, `0xBB`, `0xCC`, and `0xDD` are read from device 0
+    and written to memory at address `0x1000`.
+
+Configuration
+-------------
+
+The ``i3c`` command is only available if CONFIG_CMD_I3C=y.
+
+Return value
+------------
+
+If the command succeeds, the return value ``$?`` is set to 0. If an error
+occurs, the return value ``$?`` is set to 1.
+
+Note
+----
+
+When specifying the data to be written to the i3c device (for example, with
+the ``i3c write`` command), the data can be provided in either uppercase
+or lowercase hexadecimal format. Both are valid and will be processed
+correctly. Similarly, when reading data with ``i3c read``, the data will be
+retrieved in the specified length and can include multiple bytes, all
+formatted in the same way.
\ No newline at end of file
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index bf2335dc8f0..718b606b162 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -80,6 +80,7 @@ Shell commands
    cmd/if
    cmd/itest
    cmd/imxtract
+   cmd/i3c
    cmd/load
    cmd/loadb
    cmd/loadm
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 6d3d2ab5e71..d96eb9b134e 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -674,8 +674,11 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
 	newdevs &= ~olddevs;
 
 	for (pos = 0; pos < master->maxdevs; pos++) {
-		if (newdevs & BIT(pos))
+		if (newdevs & BIT(pos)) {
 			i3c_master_add_i3c_dev_locked(m, master->addrs[pos]);
+			master->i3cdev[pos] = m->this;
+			master->num_i3cdevs++;
+		}
 	}
 
 	dw_i3c_master_free_xfer(xfer);
@@ -1010,8 +1013,38 @@ err_assert_rst:
 	return ret;
 }
 
+static int dw_i3c_master_priv_read(struct udevice *dev, u32 dev_number,
+				   u8 *buf, u32 buf_size)
+{
+	struct dw_i3c_master *master = dev_get_priv(dev);
+	struct i3c_dev_desc *i3cdev = master->i3cdev[dev_number];
+	struct i3c_priv_xfer i3c_xfers;
+
+	i3c_xfers.data.in = buf;
+	i3c_xfers.len = buf_size;
+	i3c_xfers.rnw = I3C_MSG_READ;
+
+	return dw_i3c_master_priv_xfers(i3cdev, &i3c_xfers, 1);
+}
+
+static int dw_i3c_master_priv_write(struct udevice *dev, u32 dev_number,
+				    u8 *buf, u32 buf_size)
+{
+	struct dw_i3c_master *master = dev_get_priv(dev);
+	struct i3c_dev_desc *i3cdev = master->i3cdev[dev_number];
+	struct i3c_priv_xfer i3c_xfers;
+
+	i3c_xfers.data.out = buf;
+	i3c_xfers.len = buf_size;
+	i3c_xfers.rnw = I3C_MSG_WRITE;
+
+	return dw_i3c_master_priv_xfers(i3cdev, &i3c_xfers, 1);
+}
+
 static const struct dm_i3c_ops dw_i3c_ops = {
 	.i3c_xfers = dw_i3c_master_priv_xfers,
+	.read = dw_i3c_master_priv_read,
+	.write = dw_i3c_master_priv_write,
 };
 
 static const struct udevice_id dw_i3c_ids[] = {
diff --git a/include/dw-i3c.h b/include/dw-i3c.h
index 42c37d6dfa2..c652de77404 100644
--- a/include/dw-i3c.h
+++ b/include/dw-i3c.h
@@ -241,6 +241,8 @@ struct dw_i3c_master {
 	char type[5];
 	u8 addrs[MAX_DEVS];
 	bool first_broadcast;
+	struct i3c_dev_desc *i3cdev[I3C_BUS_MAX_DEVS];
+	u16 num_i3cdevs;
 };
 
 struct dw_i3c_i2c_dev_data {
diff --git a/include/i3c.h b/include/i3c.h
index 59c787c21db..62d049232a7 100644
--- a/include/i3c.h
+++ b/include/i3c.h
@@ -25,6 +25,32 @@ struct dm_i3c_ops {
 	int (*i3c_xfers)(struct i3c_dev_desc *dev,
 			 struct i3c_priv_xfer *xfers,
 			 u32 nxfers);
+
+/**
+ * @i3c_xfers: Perform I3C read transaction.
+ *
+ * @dev: Chip to read from
+ * @dev_number: The target device number from the driver model.
+ * @buf: Place to put data
+ * @num_bytes: Number of bytes to read.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+	int (*read)(struct udevice *dev, u32 dev_number,
+		    u8 *buf, u32 num_bytes);
+
+/**
+ * @i3c_xfers: Perform I3C write transaction.
+ *
+ * @dev: Chip to write to
+ * @dev_number: The target device number from the driver model.
+ * @buf: Buffer containing data to write
+ * @num_bytes: Number of bytes to write.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+	int (*write)(struct udevice *dev, u32 dev_number,
+		     u8 *buf, u32 num_bytes);
 };
 
 /**
@@ -64,4 +90,4 @@ int dm_i3c_write(struct udevice *dev, u32 dev_number,
  * Return: 0 on success, negative error code on failure.
  */
 int dm_i3c_read(struct udevice *dev, u32 dev_number,
-		u8 *buf, u32 num_bytes);
\ No newline at end of file
+		u8 *buf, u32 num_bytes);
-- 
2.26.2



More information about the U-Boot mailing list