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

Reply via email to