cmd: Add i3c command support.
Add i3c command file to support select, get i3c device target list, read and write operation. Signed-off-by: Dinesh Maniyam <dinesh.maniyam@altera.com>
This commit is contained in:
committed by
Heiko Schocher
parent
82cc368cd2
commit
b875409da7
@@ -1343,6 +1343,13 @@ config CMD_I2C
|
||||
help
|
||||
I2C support.
|
||||
|
||||
config CMD_I3C
|
||||
bool "i3c"
|
||||
depends on 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
|
||||
|
||||
@@ -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
|
||||
|
||||
271
cmd/i3c.c
Normal file
271
cmd/i3c.c
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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[])
|
||||
{
|
||||
u32 mem_addr, num_bytes, dev_num_val;
|
||||
u8 device_num;
|
||||
u8 *data;
|
||||
int ret;
|
||||
|
||||
if (argc < 5)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
if (!currdev) {
|
||||
printf("i3c: No I3C controller selected\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
mem_addr = hextoul(argv[2], NULL);
|
||||
num_bytes = hextoul(argv[3], NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
device_num = dev_num_val;
|
||||
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);
|
||||
|
||||
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[])
|
||||
{
|
||||
u32 mem_addr, read_len, dev_num_val;
|
||||
u8 device_num;
|
||||
u8 *rdata;
|
||||
int ret;
|
||||
|
||||
if (argc < 5)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
if (!currdev) {
|
||||
printf("i3c: No I3C controller selected\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
mem_addr = hextoul(argv[2], NULL);
|
||||
read_len = hextoul(argv[3], NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
device_num = dev_num_val;
|
||||
rdata = malloc(read_len);
|
||||
|
||||
if (!rdata) {
|
||||
printf("i3c: Memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
146
doc/usage/cmd/i3c.rst
Normal file
146
doc/usage/cmd/i3c.rst
Normal file
@@ -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.
|
||||
@@ -82,6 +82,7 @@ Shell commands
|
||||
cmd/if
|
||||
cmd/itest
|
||||
cmd/imxtract
|
||||
cmd/i3c
|
||||
cmd/load
|
||||
cmd/loadb
|
||||
cmd/loadm
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,6 +25,32 @@ struct dm_i3c_ops {
|
||||
int (*i3c_xfers)(struct i3c_dev_desc *dev,
|
||||
struct i3c_priv_xfer *xfers,
|
||||
u32 nxfers);
|
||||
|
||||
/**
|
||||
* @read: 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);
|
||||
|
||||
/**
|
||||
* @write: 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);
|
||||
u8 *buf, u32 num_bytes);
|
||||
|
||||
Reference in New Issue
Block a user