1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2025 Altera Corporation <www.altera.com>
4  */
5 
6 #include <bootretry.h>
7 #include <cli.h>
8 #include <command.h>
9 #include <console.h>
10 #include <dm.h>
11 #include <dw-i3c.h>
12 #include <edid.h>
13 #include <errno.h>
14 #include <hexdump.h>
15 #include <log.h>
16 #include <malloc.h>
17 #include <asm/byteorder.h>
18 #include <linux/compiler.h>
19 #include <linux/delay.h>
20 #include <u-boot/crc.h>
21 #include <linux/i3c/master.h>
22 #include <linux/printk.h>
23 #include <linux/types.h>
24 
25 static struct udevice *currdev;
26 static struct udevice *prevdev;
27 static struct dw_i3c_master *master;
28 
low_to_high_bytes(void * data,size_t size)29 static void low_to_high_bytes(void *data, size_t size)
30 {
31 	u8 *byte_data = data;
32 	size_t start = 0;
33 	size_t end = size - 1;
34 
35 	while (start < end) {
36 		u8 temp = byte_data[start];
37 
38 		byte_data[start] = byte_data[end];
39 		byte_data[end] = temp;
40 		start++;
41 		end--;
42 	}
43 }
44 
handle_i3c_select(const char * name)45 static int handle_i3c_select(const char *name)
46 {
47 	struct uclass *uc;
48 	struct udevice *dev_list;
49 	int ret = uclass_get_device_by_name(UCLASS_I3C, name, &currdev);
50 
51 	if (ret) {
52 		currdev = prevdev;
53 		if (!currdev) {
54 			ret = uclass_get(UCLASS_I3C, &uc);
55 			if (ret)
56 				return CMD_RET_FAILURE;
57 
58 			uclass_foreach_dev(dev_list, uc)
59 				printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
60 
61 			printf("i3c: Host controller not initialized: %s\n", name);
62 			return CMD_RET_FAILURE;
63 		}
64 	} else {
65 		master = dev_get_priv(currdev);
66 		printf("i3c: Current controller: %s\n", currdev->name);
67 		prevdev = currdev;
68 	}
69 
70 	return CMD_RET_SUCCESS;
71 }
72 
handle_i3c_list(void)73 static int handle_i3c_list(void)
74 {
75 	struct uclass *uc;
76 	struct udevice *dev_list;
77 	int ret = uclass_get(UCLASS_I3C, &uc);
78 
79 	if (ret)
80 		return CMD_RET_FAILURE;
81 
82 	uclass_foreach_dev(dev_list, uc)
83 		printf("%s (%s)\n", dev_list->name, dev_list->driver->name);
84 
85 	return CMD_RET_SUCCESS;
86 }
87 
handle_i3c_current(void)88 static int handle_i3c_current(void)
89 {
90 	if (!currdev)
91 		printf("i3c: No current controller selected\n");
92 	else
93 		printf("i3c: Current controller: %s\n", currdev->name);
94 
95 	return CMD_RET_SUCCESS;
96 }
97 
handle_i3c_device_list(void)98 static int handle_i3c_device_list(void)
99 {
100 	if (!master) {
101 		printf("i3c: No controller active\n");
102 		return CMD_RET_FAILURE;
103 	}
104 
105 	for (int i = 0; i < master->num_i3cdevs; i++) {
106 		struct i3c_device_info *info = &master->i3cdev[i]->info;
107 
108 		printf("Device %d:\n", i);
109 		printf("  Static Address  : 0x%02X\n", info->static_addr);
110 		printf("  Dynamic Address : 0x%X\n", info->dyn_addr);
111 		printf("  PID             : %016llx\n", info->pid);
112 		printf("  BCR             : 0x%X\n", info->bcr);
113 		printf("  DCR             : 0x%X\n", info->dcr);
114 		printf("  Max Read DS     : 0x%X\n", info->max_read_ds);
115 		printf("  Max Write DS    : 0x%X\n", info->max_write_ds);
116 		printf("\n");
117 	}
118 
119 	return CMD_RET_SUCCESS;
120 }
121 
handle_i3c_write(int argc,char * const argv[])122 static int handle_i3c_write(int argc, char *const argv[])
123 {
124 	u32 mem_addr, num_bytes, dev_num_val;
125 	u8 device_num;
126 	u8 *data;
127 	int ret;
128 
129 	if (argc < 5)
130 		return CMD_RET_USAGE;
131 
132 	if (!currdev) {
133 		printf("i3c: No I3C controller selected\n");
134 		return CMD_RET_FAILURE;
135 	}
136 
137 	mem_addr = hextoul(argv[2], NULL);
138 	num_bytes = hextoul(argv[3], NULL);
139 	dev_num_val = hextoul(argv[4], NULL);
140 
141 	if (num_bytes == 0 || num_bytes > 4) {
142 		printf("i3c: Length must be between 1 and 4\n");
143 		return CMD_RET_USAGE;
144 	}
145 
146 	if (dev_num_val > 0xFF) {
147 		printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
148 		return CMD_RET_USAGE;
149 	}
150 
151 	device_num = dev_num_val;
152 	data = malloc(num_bytes);
153 
154 	if (!data) {
155 		printf("i3c: Memory allocation failed\n");
156 		return -ENOMEM;
157 	}
158 
159 	memcpy(data, (void *)(uintptr_t)mem_addr, num_bytes);
160 	low_to_high_bytes(data, num_bytes);
161 
162 	ret = dm_i3c_write(currdev, device_num, data, num_bytes);
163 
164 	if (ret)
165 		printf("i3c: Write failed: %d\n", ret);
166 
167 	free(data);
168 	return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
169 }
170 
handle_i3c_read(int argc,char * const argv[])171 static int handle_i3c_read(int argc, char *const argv[])
172 {
173 	u32 mem_addr, read_len, dev_num_val;
174 	u8 device_num;
175 	u8 *rdata;
176 	int ret;
177 
178 	if (argc < 5)
179 		return CMD_RET_USAGE;
180 
181 	if (!currdev) {
182 		printf("i3c: No I3C controller selected\n");
183 		return CMD_RET_FAILURE;
184 	}
185 
186 	mem_addr = hextoul(argv[2], NULL);
187 	read_len = hextoul(argv[3], NULL);
188 	dev_num_val = hextoul(argv[4], NULL);
189 
190 	if (read_len == 0) {
191 		printf("i3c: Read length must be greater than 0\n");
192 		return CMD_RET_USAGE;
193 	}
194 
195 	if (dev_num_val > 0xFF) {
196 		printf("i3c: Device number 0x%x exceeds valid u8 range\n", dev_num_val);
197 		return CMD_RET_USAGE;
198 	}
199 
200 	device_num = dev_num_val;
201 	rdata = malloc(read_len);
202 
203 	if (!rdata) {
204 		printf("i3c: Memory allocation failed\n");
205 		return -ENOMEM;
206 	}
207 
208 	ret = dm_i3c_read(currdev, device_num, rdata, read_len);
209 
210 	if (ret) {
211 		printf("i3c: Read failed: %d\n", ret);
212 		free(rdata);
213 		return CMD_RET_FAILURE;
214 	}
215 
216 	memcpy((void *)(uintptr_t)mem_addr, rdata, read_len);
217 	print_hex_dump("i3c read: ", DUMP_PREFIX_OFFSET, 16, 1,
218 		       (void *)(uintptr_t)mem_addr, read_len, false);
219 
220 	free(rdata);
221 	return CMD_RET_SUCCESS;
222 }
223 
is_i3c_subcommand(const char * cmd)224 static bool is_i3c_subcommand(const char *cmd)
225 {
226 	return !strcmp(cmd, "write") ||
227 	       !strcmp(cmd, "read") ||
228 	       !strcmp(cmd, "device_list") ||
229 	       !strcmp(cmd, "list") ||
230 	       !strcmp(cmd, "current");
231 }
232 
do_i3c(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])233 static int do_i3c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
234 {
235 	if (argc < 2)
236 		return CMD_RET_USAGE;
237 
238 	const char *subcmd = argv[1];
239 
240 	if (!is_i3c_subcommand(subcmd))
241 		return handle_i3c_select(subcmd);
242 
243 	if (!currdev) {
244 		printf("i3c: No I3C controller selected\n");
245 		return CMD_RET_FAILURE;
246 	}
247 
248 	if (!strcmp(subcmd, "list"))
249 		return handle_i3c_list();
250 	else if (!strcmp(subcmd, "current"))
251 		return handle_i3c_current();
252 	else if (!strcmp(subcmd, "device_list"))
253 		return handle_i3c_device_list();
254 	else if (!strcmp(subcmd, "write"))
255 		return handle_i3c_write(argc, argv);
256 	else if (!strcmp(subcmd, "read"))
257 		return handle_i3c_read(argc, argv);
258 
259 	return CMD_RET_USAGE;
260 }
261 
262 U_BOOT_CMD(
263 	i3c, 5, 1, do_i3c,
264 	"access the system i3c",
265 	"i3c write <mem_addr> <length> <device_number> - write from memory to device\n"
266 	"i3c read <mem_addr> <length> <device_number> - read from device to memory\n"
267 	"i3c device_list - List valid target devices\n"
268 	"i3c <host_controller> - Select i3c controller\n"
269 	"i3c list - List all available i3c controllers\n"
270 	"i3c current - Show current i3c controller"
271 );
272