> -----Original Message----- > From: Heinrich Schuchardt <xypron.g...@gmx.de> > Sent: Thursday, 17 April 2025 3:25 pm > To: Maniyam, Dinesh <dinesh.mani...@altera.com> > Cc: Marek <ma...@denx.de>; Simon <simon.k.r.goldschm...@gmail.com>; > Simon Glass <s...@chromium.org>; Tom Rini <tr...@konsulko.com>; Dario > Binacchi <dario.binac...@amarulasolutions.com>; Ilias Apalodimas > <ilias.apalodi...@linaro.org>; Jerome Forissier <jerome.foriss...@linaro.org>; > Mattijs Korpershoek <mkorpersh...@baylibre.com>; Ibai Erkiaga <ibai.erkiaga- > elo...@amd.com>; Michal Simek <michal.si...@amd.com>; Dmitry Rokosov > <ddroko...@salutedevices.com>; Jonas Karlman <jo...@kwiboo.se>; Sebastian > Reichel <sebastian.reic...@collabora.com>; Meng, Tingting > <tingting.m...@altera.com>; Chee, Tien Fong <tien.fong.c...@altera.com>; > Hea, Kok Kiang <kok.kiang....@altera.com>; Ng, Boon Khai > <boon.khai...@altera.com>; Yuslaimi, Alif Zakuan > <alif.zakuan.yusla...@altera.com>; Zamri, Muhammad Hazim Izzat > <muhammad.hazim.izzat.za...@altera.com>; Lim, Jit Loon > <jit.loon....@altera.com>; Tang, Sieu Mun <sieu.mun.t...@altera.com>; u- > b...@lists.denx.de > Subject: Re: [resend v4 09/12] cmd: Add i3c command support. > > [CAUTION: This email is from outside your organization. Unless you trust the > sender, do not click on links or open attachments as it may be a fraudulent > email > attempting to steal your information and/or compromise your computer.] > > On 4/17/25 04:18, dinesh.mani...@altera.com wrote: > > 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 | 193 +++++++++++++++++++++++++++++ > > doc/usage/cmd/i3c.rst | 98 +++++++++++++++ > > doc/usage/index.rst | 1 + > > drivers/i3c/master/dw-i3c-master.c | 35 +++++- > > include/dw-i3c.h | 2 + > > include/i3c.h | 4 + > > 8 files changed, 339 insertions(+), 1 deletion(-) > > 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..5631f487cc2 > > --- /dev/null > > +++ b/cmd/i3c.c > > @@ -0,0 +1,193 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright 2023 Intel Coporation. > > + */ > > + > > +#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 <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> > > + > > +static struct udevice *currdev; > > +static struct udevice *prevdev; > > +static struct dw_i3c_master *master; > > + > > +void low_to_high_bytes(void *data, size_t size) { > > + unsigned char *byte_data = (unsigned char *)data; > > + size_t start = 0; > > + size_t end = size - 1; > > + > > + while (start < end) { > > + unsigned char temp = byte_data[start]; > > + > > + byte_data[start] = byte_data[end]; > > + byte_data[end] = temp; > > + start++; > > + end--; > > + } > > +} > > + > > +/** > > + * do_i3c() - Handle the "i3c" command-line command > > + * @cmdtp: Command data struct pointer > > + * @flag: Command flag > > + * @argc: Command-line argument count > > + * @argv: Array of command-line arguments > > + * > > + * Returns zero on success, CMD_RET_USAGE in case of misuse and > > +negative > > + * on error. > > + */ > > +static int do_i3c(struct cmd_tbl *cmdtp, int flag, int argc, char > > +*const argv[]) { > > + struct uclass *uc; > > + int ret; > > + struct udevice *dev_list; > > + u32 length, bytes; > > + u8 *data; > > + u8 *rdata; > > + u8 device_num; > > + > > + if (argc > 1) { > > + ret = uclass_get_device_by_name(UCLASS_I3C, argv[1], > > &currdev); > > + if (ret) { > > + if (!strcmp(argv[1], "help")) > > + return CMD_RET_USAGE; > > You don't need to check for "help". Just return CMD_RET_USAGE if the first > argument does not match any of the subcommands.
Will remove this and return CMD_RET_USAGE. > > > + > > + 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 master controller is not > > initialized: %s\n", argv[1]); > > + return CMD_RET_FAILURE; > > + } > > + } else { > > + master = dev_get_priv(currdev); > > + printf("current dev: %s\n", currdev->name); > > + prevdev = currdev; > > + } > > + } else { > > + if (!currdev) { > > + printf("No i3c device set!\n"); > > + return CMD_RET_FAILURE; > > + } > > + printf("dev: %s\n", currdev->name); > > + } > > + > > + if (!strcmp(argv[1], "list")) { > > Why would you want to reach this if statment for argc == 1? To list how many instances bind to the drivers. > > > + 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); > > + } > > + > > + if (!strcmp(argv[1], "current")) { > > + if (!currdev) > > + printf("no current dev!\n"); > > + else > > + printf("current dev: %s\n", currdev->name); > > + } > > + > > + if ((!strcmp(argv[1], "write")) && !(argv[2] == NULL) && !(argv[3] == > > NULL) > && > > + !(argv[4] == NULL)) { > > + length = hextoul(argv[3], NULL); > > + > > + data = (u8 *)malloc(sizeof(u8) * length); > > + if (!data) { > > + debug("Memory allocation for data failed\n"); > > + return -ENOMEM; > > + } > > + > > + device_num = hextoul(argv[4], NULL); > > device_num is u8. You should check that the value returned by hextoul does not > exceed 0xff. > > > + bytes = hextoul(argv[2], NULL); > > Bytes is only 32bit wide. But the number returned by hextoul can be larger > than > UINT_MAX leading to truncation. > > Please, check the value range. > Sure, I will check the value range and update it. > > + > > + for (int i = 0; i < length; i++) > > + data[i] = (u8)((bytes >> (8 * i)) & 0xFF); > > + > > + low_to_high_bytes(data, length); > > + ret = dm_i3c_write(currdev, device_num, data, length); > > For unknown reasons you have defined the num_bytes parameter as signed int. > So length > INT_MAX will be transferred as a negative number. > > Please, be consistent. > Noted, I will take note on this. > > + if (ret) { > > + ret = CMD_RET_FAILURE; > > + goto write_out; > > + } > > + } > > + > > + if (!strcmp(argv[1], "read") && !(argv[2] == NULL) && !(argv[3] == > > NULL)) { > > + length = hextoul(argv[2], NULL); > > Each subcommand should be handled in a separate static function to avoid > sphaghetti code. > Sure, will create a separate static functions for each subcommand. > > + > > + rdata = (u8 *) malloc(sizeof(u8) * length); > > This is not C++ code. Please, remove the (u8 *) conversion. > > I would not expect sizeof(u8) to differ from 1. So remove it. > Will remove it. > > + if (!rdata) { > > + debug("Memory allocation for rdata failed\n"); > > + return -ENOMEM; > > + } > > + > > + device_num = hextoul(argv[3], NULL); > > Check that hextoul() returns a number in the 0 - 255 range. > > > + > > + ret = dm_i3c_read(currdev, device_num, rdata, length); > > + if (ret) { > > + ret = CMD_RET_FAILURE; > > Please, avoid duplicate space after '='. Will correct it. > > > + } else { > > + printf("Read buff: "); > > + for (size_t i = 0; i < length; ++i) > > length is u32. Why use a larger type for the index? Not supposed to use larger type of index, will take note on this and be more consistent. > > > + printf("%02x", rdata[i]); > > Please, use print_hex_dump() here. This is especially helpful when reading > hundres of bytes. > Noted. Will replace with print_hex_dump() > To be honest, it would make more sense to provide an address in memory to > receive the data than printing up to INT_MAX bytes. > You are right. To handle larger data byte, better using address in memory to send and receive the data. Will rework. > > + > > + printf("\n"); > > + } > > + > > + goto read_out; > > + } > > + > > + if (!strcmp(argv[1], "device_list")) { > > + if (master) { > > + for (int i = 0; i < master->num_i3cdevs; i++) { > > + printf("device:%d\n", i); > > + printf("dynamic address:0x%X\n", > > master->i3cdev[i]- > >info.dyn_addr); > > + printf("PID:%llx\n", > > master->i3cdev[i]->info.pid); > > + printf("BCR:%X\n", > > master->i3cdev[i]->info.bcr); > > + printf("DCR:%X\n", > > master->i3cdev[i]->info.dcr); > > + printf("Max Read DS:%X\n", master->i3cdev[i]- > >info.max_read_ds); > > + printf("Max Write DS:%X\n", master->i3cdev[i]- > >info.max_write_ds); > > + printf("\n"); > > + } > > + } > > + } > > + > > + return CMD_RET_SUCCESS; > > + > > +read_out: > > + free(rdata); > > + return ret; > > + > > +write_out: > > + free(data); > > + return ret; > > +} > > + > > +U_BOOT_CMD( > > + i3c, 5, 1, do_i3c, > > + "access the system i3c\n", > > + "i3c write<data><length><device_number> - write to device.\n" > > + "i3c read<length><device_number> - read from device.\n" > > + "i3c device_list - List valid target devices\n" > > + "i3c <i3c name> - Select i3c controller.\n" > > %s/i3c name/host controller/ > Will be replaced. > > + "i3c list - List all the available i3c controller.\n" > > + "i3c current - print current i3c controller.\n" > > +); > > diff --git a/doc/usage/cmd/i3c.rst b/doc/usage/cmd/i3c.rst new file > > mode 100644 index 00000000000..ade7b5c7fc4 > > --- /dev/null > > +++ b/doc/usage/cmd/i3c.rst > > @@ -0,0 +1,98 @@ > > +.. SPDX-License-Identifier: GPL-2.0 > > + > > +.. index:: > > + single: i3c (command) > > + > > +i3c command > > +=========== > > + > > +Synopsis > > +-------- > > + > > +:: > > + > > + i3c <i3c name> > > %s/i3c name/host_controller/ > Will be replaced. > > + i3c current > > + i3c list > > + i3c device_list > > + i3c write <data> <length> <device_number> > > For sending large amounts of data it would be preferable to replace direct > data by > a memory start address. > > I would not want to type all bytes needed for displaying an image on an LCD > screen. > You are right. To handle larger data byte, better using address in memory to send and receive the data. Will rework. > > > + i3c read <length> <device_number> > > Same for reading. When reading a camera image you don't want megabytes of > data flooding the screen. > > > + > > + > > +Description > > +----------- > > + > > +The ``i3c`` command is used to probe i3c master controller and > > +perform read > > %s/i3c master/an I3C host/ > Will be replaced. > > +and write on the i3c devices. > > %s/write on the i3c/write operations on the connected I3C/ > Will be replaced. > i3c > --- > > Probe an I3C host controller and select it for the other sub-commands. > > host_controller > name of the host controller to be probed and selected > > > + > > +i3c current > > +------------ > > + > > +* Display the current probed i3c controller. > > %s/current probed i3c/currently selected I3C host/ > Will be replaced. > > + > > +i3c list > > +---------- > > + > > +List all the i3c controllers defined in the DT. > > %s/i3c/I3C/ > Will be replaced. > %s/DT/device-tree/ Will be replaced. > > > + > > +i3c device_list > > +---------------- > > + > > +List all the i3c devices' device number, dynamic address, PID, BCR, > > +DCR, > > +Max Write Speed and Max Read Speed of the probed i3c controller. > > List all the I3C devices connected to the current host controller, displaying > > * device number > * dynamic address > * provisional ID (PID) > * value of the Bus Characteristics Register (BCR) > * value of the Device Characteristics Register (DCR) > * maximum write speed in MHz > * maximum read speed in MHz > > Why wouldn't you print the static address, too? > Will include the static address. > > + > > +i3c write > > +----------- > > + > > +Perform write to the i3c device. > > + > > +i3c read > > +----------- > > + > > +Perform read from the i3c device. > > > Parameters > ---------- > > > + > > +i3c name > > host_controller > > > + i3c name defined in DT > > Name of the host controller as defined in the device-tree > This will be replaced. > > + > > +length > > + Size of the data. > > + > > +device_number > > + device number that connected to i3c controller. > > It is unclear what is meant by device number. Is it the static address or a > device > number in the driver model? > It should be a device number. > > + > > +data > > + Bytes to be written to device. > > + > > +Examples > > +-------- > > + > > +Probe i3c0:: > > + > > + => i3c i3c0 > > + > > +Check cuurent i3c controller:: > > Display current I3C host controller:: > Will be replaced. > > + > > + => i3c current > > + > > +Check the device number and PID of the connected devices:: > > + > > + => i3c device_list > > + > > +Perform write AA byte to a device 0:: > > Do you mean: > > Write a single byte 0xaa to the device with static address 0 on the selected > host > controller. > No. device 0 is the device number in the driver model. > > + > > + => i3c write AA 1 0 > > It is unclear how to format multiple bytes to be written. Please, provide an > example with multiple bytes. > > Does the data have to be capitalized or is lower case working, too? > Will provide the example for multiple bytes on next version. Lower case is already working and taken care of. > > + > > +Perform read from a device 0:: > > + > > + => i3c read 1 0 > > Where is the example output? > Missing output. Will add it. > Best regards > > Heinrich > Please expect v5, which I will rework based on the comments. Regards Dinesh > > > + > > +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. > > 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 bf5d57937c7..623f5b01dbb 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, u8 dev_number, > > + u8 *buf, int 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, u8 dev_number, > > + u8 *buf, int 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 > > 6ef4982dcf6..969753ae49d 100644 > > --- a/include/i3c.h > > +++ b/include/i3c.h > > @@ -28,6 +28,10 @@ struct dm_i3c_ops { > > int (*i3c_xfers)(struct i3c_dev_desc *dev, > > struct i3c_priv_xfer *i3c_xfers, > > int i3c_nxfers); > > + int (*read)(struct udevice *dev, u8 dev_number, > > + u8 *buf, int num_bytes); > > + int (*write)(struct udevice *dev, u8 dev_number, > > + u8 *buf, int num_bytes); > > }; > > > > #define i3c_get_ops(dev) ((struct dm_i3c_ops *)(dev)->driver->ops)