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