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