]>
Commit | Line | Data |
---|---|---|
37c4a5f6 MS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2016 | |
4 | * Dirk Eibach, Guntermann & Drunck GmbH, [email protected] | |
5 | * | |
6 | * (C) Copyright 2017, 2018 | |
7 | * Mario Six, Guntermann & Drunck GmbH, [email protected] | |
8 | * | |
9 | * SPDX-License-Identifier: GPL-2.0+ | |
10 | */ | |
11 | ||
37c4a5f6 MS |
12 | #include <axi.h> |
13 | #include <command.h> | |
14 | #include <console.h> | |
4e4bf944 | 15 | #include <display_options.h> |
37c4a5f6 | 16 | #include <dm.h> |
f7ae49fc | 17 | #include <log.h> |
37c4a5f6 MS |
18 | |
19 | /* Currently selected AXI bus device */ | |
20 | static struct udevice *axi_cur_bus; | |
21 | /* Transmission size from last command */ | |
22 | static uint dp_last_size; | |
23 | /* Address from last command */ | |
24 | static uint dp_last_addr; | |
25 | /* Number of bytes to display from last command; default = 64 */ | |
26 | static uint dp_last_length = 0x40; | |
27 | ||
28 | /** | |
29 | * show_bus() - Show devices on a single AXI bus | |
30 | * @bus: The AXI bus device to printt information for | |
31 | */ | |
32 | static void show_bus(struct udevice *bus) | |
33 | { | |
34 | struct udevice *dev; | |
35 | ||
36c03d18 | 36 | printf("Bus %d:\t%s", dev_seq(bus), bus->name); |
37c4a5f6 | 37 | if (device_active(bus)) |
36c03d18 | 38 | printf(" (active)"); |
37c4a5f6 MS |
39 | printf("\n"); |
40 | for (device_find_first_child(bus, &dev); | |
41 | dev; | |
42 | device_find_next_child(&dev)) | |
43 | printf(" %s\n", dev->name); | |
44 | } | |
45 | ||
46 | /** | |
47 | * axi_set_cur_bus() - Set the currently active AXI bus | |
48 | * @busnum: The number of the bus (i.e. its sequence number) that should be | |
49 | * made active | |
50 | * | |
51 | * The operations supplied by this command operate only on the currently active | |
52 | * bus. | |
53 | * | |
54 | * Return: 0 if OK, -ve on error | |
55 | */ | |
56 | static int axi_set_cur_bus(unsigned int busnum) | |
57 | { | |
58 | struct udevice *bus; | |
59 | struct udevice *dummy; | |
60 | int ret; | |
61 | ||
62 | /* Make sure that all sequence numbers are initialized */ | |
63 | for (uclass_first_device(UCLASS_AXI, &dummy); | |
64 | dummy; | |
65 | uclass_next_device(&dummy)) | |
66 | ; | |
67 | ||
68 | ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus); | |
69 | if (ret) { | |
70 | debug("%s: No bus %d\n", __func__, busnum); | |
71 | return ret; | |
72 | } | |
73 | axi_cur_bus = bus; | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
78 | /** | |
79 | * axi_get_cur_bus() - Retrieve the currently active AXI bus device | |
80 | * @busp: Pointer to a struct udevice that receives the currently active bus | |
81 | * device | |
82 | * | |
83 | * Return: 0 if OK, -ve on error | |
84 | */ | |
85 | static int axi_get_cur_bus(struct udevice **busp) | |
86 | { | |
87 | if (!axi_cur_bus) { | |
88 | puts("No AXI bus selected\n"); | |
89 | return -ENODEV; | |
90 | } | |
91 | *busp = axi_cur_bus; | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | /* | |
97 | * Command handlers | |
98 | */ | |
99 | ||
09140113 SG |
100 | static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc, |
101 | char *const argv[]) | |
37c4a5f6 MS |
102 | { |
103 | struct udevice *dummy; | |
104 | ||
105 | /* Make sure that all sequence numbers are initialized */ | |
106 | for (uclass_first_device(UCLASS_AXI, &dummy); | |
107 | dummy; | |
108 | uclass_next_device(&dummy)) | |
109 | ; | |
110 | ||
111 | if (argc == 1) { | |
112 | /* show all busses */ | |
113 | struct udevice *bus; | |
114 | ||
115 | for (uclass_first_device(UCLASS_AXI, &bus); | |
116 | bus; | |
117 | uclass_next_device(&bus)) | |
118 | show_bus(bus); | |
119 | } else { | |
120 | int i; | |
121 | ||
122 | /* show specific bus */ | |
0b1284eb | 123 | i = dectoul(argv[1], NULL); |
37c4a5f6 MS |
124 | |
125 | struct udevice *bus; | |
126 | int ret; | |
127 | ||
128 | ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus); | |
129 | if (ret) { | |
130 | printf("Invalid bus %d: err=%d\n", i, ret); | |
131 | return CMD_RET_FAILURE; | |
132 | } | |
133 | show_bus(bus); | |
134 | } | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
09140113 SG |
139 | static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc, |
140 | char *const argv[]) | |
37c4a5f6 MS |
141 | { |
142 | int ret = 0; | |
143 | int bus_no; | |
144 | ||
145 | if (argc == 1) { | |
146 | /* querying current setting */ | |
147 | struct udevice *bus; | |
148 | ||
149 | if (!axi_get_cur_bus(&bus)) | |
8b85dfc6 | 150 | bus_no = dev_seq(bus); |
37c4a5f6 MS |
151 | else |
152 | bus_no = -1; | |
153 | ||
154 | printf("Current bus is %d\n", bus_no); | |
155 | } else { | |
0b1284eb | 156 | bus_no = dectoul(argv[1], NULL); |
37c4a5f6 MS |
157 | printf("Setting bus to %d\n", bus_no); |
158 | ||
159 | ret = axi_set_cur_bus(bus_no); | |
160 | if (ret) | |
161 | printf("Failure changing bus number (%d)\n", ret); | |
162 | } | |
163 | ||
164 | return ret ? CMD_RET_FAILURE : 0; | |
165 | } | |
166 | ||
09140113 SG |
167 | static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc, |
168 | char *const argv[]) | |
37c4a5f6 MS |
169 | { |
170 | /* Print that many bytes per line */ | |
171 | const uint DISP_LINE_LEN = 16; | |
172 | u8 linebuf[DISP_LINE_LEN]; | |
173 | unsigned int k; | |
174 | ulong addr, length, size; | |
175 | ulong nbytes; | |
176 | enum axi_size_t axisize; | |
177 | int unitsize; | |
178 | ||
179 | /* | |
180 | * We use the last specified parameters, unless new ones are | |
181 | * entered. | |
182 | */ | |
183 | size = dp_last_size; | |
184 | addr = dp_last_addr; | |
185 | length = dp_last_length; | |
186 | ||
187 | if (argc < 3) | |
188 | return CMD_RET_USAGE; | |
189 | ||
190 | if (!axi_cur_bus) { | |
191 | puts("No AXI bus selected\n"); | |
192 | return CMD_RET_FAILURE; | |
193 | } | |
194 | ||
195 | if ((flag & CMD_FLAG_REPEAT) == 0) { | |
0b1284eb | 196 | size = dectoul(argv[1], NULL); |
37c4a5f6 MS |
197 | |
198 | /* | |
199 | * Address is specified since argc >= 3 | |
200 | */ | |
7e5f460e | 201 | addr = hextoul(argv[2], NULL); |
37c4a5f6 MS |
202 | |
203 | /* | |
204 | * If there's another parameter, it is the length to display; | |
205 | * length is the number of objects, not number of bytes | |
206 | */ | |
207 | if (argc > 3) | |
7e5f460e | 208 | length = hextoul(argv[3], NULL); |
37c4a5f6 MS |
209 | } |
210 | ||
211 | switch (size) { | |
212 | case 8: | |
213 | axisize = AXI_SIZE_8; | |
214 | unitsize = 1; | |
215 | break; | |
216 | case 16: | |
217 | axisize = AXI_SIZE_16; | |
218 | unitsize = 2; | |
219 | break; | |
220 | case 32: | |
221 | axisize = AXI_SIZE_32; | |
222 | unitsize = 4; | |
223 | break; | |
224 | default: | |
225 | printf("Unknown read size '%lu'\n", size); | |
226 | return CMD_RET_USAGE; | |
227 | }; | |
228 | ||
229 | nbytes = length * unitsize; | |
230 | do { | |
231 | ulong linebytes = (nbytes > DISP_LINE_LEN) ? | |
232 | DISP_LINE_LEN : nbytes; | |
233 | ||
234 | for (k = 0; k < linebytes / unitsize; ++k) { | |
235 | int ret = axi_read(axi_cur_bus, addr + k * unitsize, | |
236 | linebuf + k * unitsize, axisize); | |
237 | ||
238 | if (!ret) /* Continue if axi_read was successful */ | |
239 | continue; | |
240 | ||
241 | if (ret == -ENOSYS) | |
242 | printf("axi_read failed; read size not supported?\n"); | |
243 | else | |
244 | printf("axi_read failed: err = %d\n", ret); | |
245 | ||
246 | return CMD_RET_FAILURE; | |
247 | } | |
248 | print_buffer(addr, (void *)linebuf, unitsize, | |
249 | linebytes / unitsize, | |
250 | DISP_LINE_LEN / unitsize); | |
251 | ||
252 | nbytes -= max(linebytes, 1UL); | |
253 | addr += linebytes; | |
254 | ||
255 | if (ctrlc()) | |
256 | break; | |
257 | } while (nbytes > 0); | |
258 | ||
259 | dp_last_size = size; | |
260 | dp_last_addr = addr; | |
261 | dp_last_length = length; | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
09140113 SG |
266 | static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc, |
267 | char *const argv[]) | |
37c4a5f6 MS |
268 | { |
269 | u32 writeval; | |
270 | ulong addr, count, size; | |
271 | enum axi_size_t axisize; | |
272 | ||
273 | if (argc <= 3 || argc >= 6) | |
274 | return CMD_RET_USAGE; | |
275 | ||
0b1284eb | 276 | size = dectoul(argv[1], NULL); |
37c4a5f6 MS |
277 | |
278 | switch (size) { | |
279 | case 8: | |
280 | axisize = AXI_SIZE_8; | |
281 | break; | |
282 | case 16: | |
283 | axisize = AXI_SIZE_16; | |
284 | break; | |
285 | case 32: | |
286 | axisize = AXI_SIZE_32; | |
287 | break; | |
288 | default: | |
289 | printf("Unknown write size '%lu'\n", size); | |
290 | return CMD_RET_USAGE; | |
291 | }; | |
292 | ||
293 | /* Address is specified since argc > 4 */ | |
7e5f460e | 294 | addr = hextoul(argv[2], NULL); |
37c4a5f6 MS |
295 | |
296 | /* Get the value to write */ | |
7e5f460e | 297 | writeval = hextoul(argv[3], NULL); |
37c4a5f6 MS |
298 | |
299 | /* Count ? */ | |
300 | if (argc == 5) | |
7e5f460e | 301 | count = hextoul(argv[4], NULL); |
37c4a5f6 MS |
302 | else |
303 | count = 1; | |
304 | ||
305 | while (count-- > 0) { | |
306 | int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32), | |
307 | &writeval, axisize); | |
308 | ||
309 | if (ret) { | |
310 | printf("axi_write failed: err = %d\n", ret); | |
311 | return CMD_RET_FAILURE; | |
312 | } | |
313 | } | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
09140113 | 318 | static struct cmd_tbl cmd_axi_sub[] = { |
37c4a5f6 MS |
319 | U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""), |
320 | U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""), | |
321 | U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""), | |
322 | U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""), | |
323 | }; | |
324 | ||
09140113 SG |
325 | static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc, |
326 | char *const argv[]) | |
37c4a5f6 | 327 | { |
09140113 | 328 | struct cmd_tbl *c; |
37c4a5f6 MS |
329 | |
330 | if (argc < 2) | |
331 | return CMD_RET_USAGE; | |
332 | ||
333 | /* Strip off leading 'axi' command argument */ | |
334 | argc--; | |
335 | argv++; | |
336 | ||
337 | /* Hand off rest of command line to sub-commands */ | |
338 | c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub)); | |
339 | ||
340 | if (c) | |
341 | return c->cmd(cmdtp, flag, argc, argv); | |
342 | else | |
343 | return CMD_RET_USAGE; | |
344 | } | |
345 | ||
3616218b | 346 | U_BOOT_LONGHELP(axi, |
37c4a5f6 MS |
347 | "bus - show AXI bus info\n" |
348 | "axi dev [bus] - show or set current AXI bus to bus number [bus]\n" | |
349 | "axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n" | |
3616218b | 350 | "axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"); |
37c4a5f6 MS |
351 | |
352 | U_BOOT_CMD(axi, 7, 1, do_ihs_axi, | |
353 | "AXI sub-system", | |
354 | axi_help_text | |
355 | ); |