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 <display_options.h>
17#include <dm.h>
18#include <log.h>
19
20/* Currently selected AXI bus device */
21static struct udevice *axi_cur_bus;
22/* Transmission size from last command */
23static uint dp_last_size;
24/* Address from last command */
25static uint dp_last_addr;
26/* Number of bytes to display from last command; default = 64 */
27static 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 */
33static void show_bus(struct udevice *bus)
34{
35	struct udevice *dev;
36
37	printf("Bus %d:\t%s", dev_seq(bus), bus->name);
38	if (device_active(bus))
39		printf("  (active)");
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 */
57static 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 */
86static 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
101static int do_axi_show_bus(struct cmd_tbl *cmdtp, int flag, int argc,
102			   char *const argv[])
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 */
124		i = dectoul(argv[1], NULL);
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
140static int do_axi_bus_num(struct cmd_tbl *cmdtp, int flag, int argc,
141			  char *const argv[])
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))
151			bus_no = dev_seq(bus);
152		else
153			bus_no = -1;
154
155		printf("Current bus is %d\n", bus_no);
156	} else {
157		bus_no = dectoul(argv[1], NULL);
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
168static int do_axi_md(struct cmd_tbl *cmdtp, int flag, int argc,
169		     char *const argv[])
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) {
197		size = dectoul(argv[1], NULL);
198
199		/*
200		 * Address is specified since argc >= 3
201		 */
202		addr = hextoul(argv[2], NULL);
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)
209			length = hextoul(argv[3], NULL);
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
267static int do_axi_mw(struct cmd_tbl *cmdtp, int flag, int argc,
268		     char *const argv[])
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
277	size = dectoul(argv[1], NULL);
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 */
295	addr = hextoul(argv[2], NULL);
296
297	/* Get the value to write */
298	writeval = hextoul(argv[3], NULL);
299
300	/* Count ? */
301	if (argc == 5)
302		count = hextoul(argv[4], NULL);
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
319static struct cmd_tbl cmd_axi_sub[] = {
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
326static int do_ihs_axi(struct cmd_tbl *cmdtp, int flag, int argc,
327		      char *const argv[])
328{
329	struct cmd_tbl *c;
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
347U_BOOT_LONGHELP(axi,
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
353U_BOOT_CMD(axi, 7, 1, do_ihs_axi,
354	   "AXI sub-system",
355	   axi_help_text
356);
357