u-boot/cmd/i3c.c
Dinesh Maniyam b875409da7 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>
2025-08-06 08:41:30 +02:00

272 lines
6.1 KiB
C

// 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"
);