From: Dinesh Maniyam <dinesh.mani...@altera.com> Add i3c command file to support select, get i3c device target list, read and write operation.
Signed-off-by: Dinesh Maniyam <dinesh.mani...@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