diff options
Diffstat (limited to 'cmd/i3c.c')
-rw-r--r-- | cmd/i3c.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/cmd/i3c.c b/cmd/i3c.c new file mode 100644 index 00000000000..08957f4d447 --- /dev/null +++ b/cmd/i3c.c @@ -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" +); |