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; + + 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")) { + 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); + bytes = hextoul(argv[2], NULL); + + 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); + 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); + + rdata = (u8 *) malloc(sizeof(u8) * length); + if (!rdata) { + debug("Memory allocation for rdata failed\n"); + return -ENOMEM; + } + + device_num = hextoul(argv[3], NULL); + + ret = dm_i3c_read(currdev, device_num, rdata, length); + if (ret) { + ret = CMD_RET_FAILURE; + } else { + printf("Read buff: "); + for (size_t i = 0; i < length; ++i) + printf("%02x", rdata[i]); + + 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" + "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> + i3c current + i3c list + i3c device_list + i3c write <data> <length> <device_number> + i3c read <length> <device_number> + + +Description +----------- + +The ``i3c`` command is used to probe i3c master controller and perform read +and write on the i3c devices. + +i3c current +------------ + +* Display the current probed i3c controller. + +i3c list +---------- + +List all the i3c controllers defined in the DT. + +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. + +i3c write +----------- + +Perform write to the i3c device. + +i3c read +----------- + +Perform read from the i3c device. + +i3c name + i3c name defined in DT + +length + Size of the data. + +device_number + device number that connected to i3c controller. + +data + Bytes to be written to device. + +Examples +-------- + +Probe i3c0:: + + => i3c i3c0 + +Check cuurent i3c controller:: + + => i3c current + +Check the device number and PID of the connected devices:: + + => i3c device_list + +Perform write AA byte to a device 0:: + + => i3c write AA 1 0 + +Perform read from a device 0:: + + => i3c read 1 0 + +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) -- 2.35.3