1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2016
4  * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
5  *
6  * (C) Copyright 2017, 2018
7  * Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
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>
16 #include <dm.h>
17 #include <log.h>
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  */
show_bus(struct udevice * bus)32 static void show_bus(struct udevice *bus)
33 {
34 	struct udevice *dev;
35 
36 	printf("Bus %d:\t%s", dev_seq(bus), bus->name);
37 	if (device_active(bus))
38 		printf("  (active)");
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  */
axi_set_cur_bus(unsigned int busnum)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  */
axi_get_cur_bus(struct udevice ** busp)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 
do_axi_show_bus(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])100 static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc,
101 			   char *const argv[])
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 */
123 		i = simple_strtoul(argv[1], NULL, 10);
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 
do_axi_bus_num(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])139 static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc,
140 			  char *const argv[])
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))
150 			bus_no = dev_seq(bus);
151 		else
152 			bus_no = -1;
153 
154 		printf("Current bus is %d\n", bus_no);
155 	} else {
156 		bus_no = simple_strtoul(argv[1], NULL, 10);
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 
do_axi_md(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])167 static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc,
168 		     char *const argv[])
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) {
196 		size = simple_strtoul(argv[1], NULL, 10);
197 
198 		/*
199 		 * Address is specified since argc >= 3
200 		 */
201 		addr = simple_strtoul(argv[2], NULL, 16);
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)
208 			length = simple_strtoul(argv[3], NULL, 16);
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 
do_axi_mw(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])266 static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc,
267 		     char *const argv[])
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 
276 	size = simple_strtoul(argv[1], NULL, 10);
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 */
294 	addr = simple_strtoul(argv[2], NULL, 16);
295 
296 	/* Get the value to write */
297 	writeval = simple_strtoul(argv[3], NULL, 16);
298 
299 	/* Count ? */
300 	if (argc == 5)
301 		count = simple_strtoul(argv[4], NULL, 16);
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 
318 static struct cmd_tbl cmd_axi_sub[] = {
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 
do_ihs_axi(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])325 static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc,
326 		      char *const argv[])
327 {
328 	struct cmd_tbl *c;
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 
346 static char axi_help_text[] =
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"
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";
351 
352 U_BOOT_CMD(axi, 7, 1, do_ihs_axi,
353 	   "AXI sub-system",
354 	   axi_help_text
355 );
356