mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-12-18 16:01:32 +01:00
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>
272 lines
6.1 KiB
C
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"
|
|
);
|