// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2025 Altera Corporation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 - write from memory to device\n" "i3c read - read from device to memory\n" "i3c device_list - List valid target devices\n" "i3c - Select i3c controller\n" "i3c list - List all available i3c controllers\n" "i3c current - Show current i3c controller" );