1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2015 Google, Inc
4  */
5 
6 #include <dm.h>
7 #include <malloc.h>
8 #include <mapmem.h>
9 #include <sort.h>
10 #include <dm/root.h>
11 #include <dm/util.h>
12 #include <dm/uclass-internal.h>
13 
14 /**
15  * struct sort_info - information used for sorting
16  *
17  * @dev: List of devices
18  * @alloced: Maximum number of devices in @dev
19  */
20 struct sort_info {
21 	struct udevice **dev;
22 	int size;
23 };
24 
h_cmp_uclass_id(const void * d1,const void * d2)25 static int h_cmp_uclass_id(const void *d1, const void *d2)
26 {
27 	const struct udevice *const *dev1 = d1;
28 	const struct udevice *const *dev2 = d2;
29 
30 	return device_get_uclass_id(*dev1) - device_get_uclass_id(*dev2);
31 }
32 
show_devices(struct udevice * dev,int depth,int last_flag,struct udevice ** devs)33 static void show_devices(struct udevice *dev, int depth, int last_flag,
34 			 struct udevice **devs)
35 {
36 	int i, is_last;
37 	struct udevice *child;
38 	u32 flags = dev_get_flags(dev);
39 
40 	/* print the first 20 characters to not break the tree-format. */
41 	printf(CONFIG_IS_ENABLED(USE_TINY_PRINTF) ? " %s  %d  [ %c ]   %s  " :
42 	       " %-10.10s  %3d  [ %c ]   %-20.20s  ", dev->uclass->uc_drv->name,
43 	       dev->seq_,
44 	       flags & DM_FLAG_ACTIVATED ? '+' : ' ', dev->driver->name);
45 
46 	for (i = depth; i >= 0; i--) {
47 		is_last = (last_flag >> i) & 1;
48 		if (i) {
49 			if (is_last)
50 				printf("    ");
51 			else
52 				printf("|   ");
53 		} else {
54 			if (is_last)
55 				printf("`-- ");
56 			else
57 				printf("|-- ");
58 		}
59 	}
60 
61 	printf("%s\n", dev->name);
62 
63 	if (devs) {
64 		int count;
65 		int i;
66 
67 		count = 0;
68 		device_foreach_child(child, dev)
69 			devs[count++] = child;
70 		qsort(devs, count, sizeof(struct udevice *), h_cmp_uclass_id);
71 
72 		for (i = 0; i < count; i++) {
73 			show_devices(devs[i], depth + 1,
74 				     (last_flag << 1) | (i == count - 1),
75 				     devs + count);
76 		}
77 	} else {
78 		device_foreach_child(child, dev) {
79 			is_last = list_is_last(&child->sibling_node,
80 					       &dev->child_head);
81 			show_devices(child, depth + 1,
82 				     (last_flag << 1) | is_last, NULL);
83 		}
84 	}
85 }
86 
dm_dump_tree_single(struct udevice * dev,bool sort)87 static void dm_dump_tree_single(struct udevice *dev, bool sort)
88 {
89 	int dev_count, uclasses;
90 	struct udevice **devs = NULL;
91 
92 	if (sort) {
93 		dm_get_stats(&dev_count, &uclasses);
94 		devs = calloc(dev_count, sizeof(struct udevice *));
95 		if (!devs) {
96 			printf("(out of memory)\n");
97 			return;
98 		}
99 	}
100 	show_devices(dev, -1, 0, devs);
101 	free(devs);
102 }
103 
dm_dump_tree_recursive(struct udevice * dev,char * dev_name,bool extended,bool sort)104 static void dm_dump_tree_recursive(struct udevice *dev, char *dev_name,
105 				   bool extended, bool sort)
106 {
107 	struct udevice *child;
108 	size_t len;
109 
110 	len = strlen(dev_name);
111 
112 	device_foreach_child(child, dev) {
113 		if (extended) {
114 			if (!strncmp(child->name, dev_name, len)) {
115 				dm_dump_tree_single(child, sort);
116 				continue;
117 			}
118 		} else {
119 			if (!strcmp(child->name, dev_name)) {
120 				dm_dump_tree_single(child, sort);
121 				continue;
122 			}
123 		}
124 		dm_dump_tree_recursive(child, dev_name, extended, sort);
125 	}
126 }
127 
dm_dump_tree(char * dev_name,bool extended,bool sort)128 void dm_dump_tree(char *dev_name, bool extended, bool sort)
129 {
130 	struct udevice *root;
131 
132 	printf(" Class     Seq    Probed  Driver                Name\n");
133 	printf("-----------------------------------------------------------\n");
134 
135 	root = dm_root();
136 	if (!root)
137 		return;
138 
139 	if (!dev_name || !strcmp(dev_name, "root")) {
140 		dm_dump_tree_single(root, sort);
141 		return;
142 	}
143 
144 	dm_dump_tree_recursive(root, dev_name, extended, sort);
145 }
146 
147 /**
148  * dm_display_line() - Display information about a single device
149  *
150  * Displays a single line of information with an option prefix
151  *
152  * @dev:	Device to display
153  */
dm_display_line(struct udevice * dev,int index)154 static void dm_display_line(struct udevice *dev, int index)
155 {
156 	printf("%-3i %c %s @ %08lx", index,
157 	       dev_get_flags(dev) & DM_FLAG_ACTIVATED ? '*' : ' ',
158 	       dev->name, (ulong)map_to_sysmem(dev));
159 	if (dev->seq_ != -1)
160 		printf(", seq %d", dev_seq(dev));
161 	puts("\n");
162 }
163 
dm_dump_uclass_single(enum uclass_id id)164 static void dm_dump_uclass_single(enum uclass_id id)
165 {
166 	struct uclass *uc;
167 	struct udevice *dev;
168 	int i = 0, ret;
169 
170 	ret = uclass_get(id, &uc);
171 	if (ret)
172 		return;
173 
174 	printf("uclass %d: %s\n", id, uc->uc_drv->name);
175 	uclass_foreach_dev(dev, uc) {
176 		dm_display_line(dev, i);
177 		i++;
178 	}
179 	puts("\n");
180 }
181 
dm_dump_uclass(char * uclass,bool extended)182 void dm_dump_uclass(char *uclass, bool extended)
183 {
184 	struct uclass *uc;
185 	enum uclass_id id;
186 	bool matching;
187 	int ret;
188 
189 	matching = !!(uclass && strcmp(uclass, "root"));
190 
191 	for (id = 0; id < UCLASS_COUNT; id++) {
192 		ret = uclass_get(id, &uc);
193 		if (ret)
194 			continue;
195 
196 		if (matching) {
197 			if (extended) {
198 				if (!strncmp(uc->uc_drv->name, uclass,
199 					     strlen(uclass)))
200 					dm_dump_uclass_single(id);
201 			} else {
202 				if (!strcmp(uc->uc_drv->name, uclass))
203 					dm_dump_uclass_single(id);
204 			}
205 		} else {
206 			dm_dump_uclass_single(id);
207 		}
208 	}
209 }
210 
dm_dump_driver_compat(void)211 void dm_dump_driver_compat(void)
212 {
213 	struct driver *d = ll_entry_start(struct driver, driver);
214 	const int n_ents = ll_entry_count(struct driver, driver);
215 	struct driver *entry;
216 	const struct udevice_id *match;
217 
218 	puts("Driver                Compatible\n");
219 	puts("--------------------------------\n");
220 	for (entry = d; entry < d + n_ents; entry++) {
221 		match = entry->of_match;
222 
223 		printf("%-20.20s", entry->name);
224 		if (match) {
225 			printf("  %s", match->compatible);
226 			match++;
227 		}
228 		printf("\n");
229 
230 		for (; match && match->compatible; match++)
231 			printf("%-20.20s  %s\n", "", match->compatible);
232 	}
233 }
234 
dm_dump_drivers(void)235 void dm_dump_drivers(void)
236 {
237 	struct driver *d = ll_entry_start(struct driver, driver);
238 	const int n_ents = ll_entry_count(struct driver, driver);
239 	struct driver *entry;
240 	struct udevice *udev;
241 	struct uclass *uc;
242 	int ret;
243 	int i;
244 
245 	puts("Driver                    uid uclass               Devices\n");
246 	puts("----------------------------------------------------------\n");
247 
248 	for (entry = d; entry < d + n_ents; entry++) {
249 		ret = uclass_get(entry->id, &uc);
250 
251 		printf("%-25.25s %-3.3d %-20.20s ", entry->name, entry->id,
252 		       !ret ? uc->uc_drv->name : "<no uclass>");
253 
254 		if (ret) {
255 			puts("\n");
256 			continue;
257 		}
258 
259 		i = 0;
260 		uclass_foreach_dev(udev, uc) {
261 			if (udev->driver != entry)
262 				continue;
263 			if (i)
264 				printf("%-51.51s", "");
265 
266 			printf("%-25.25s\n", udev->name);
267 			i++;
268 		}
269 		if (!i)
270 			puts("<none>\n");
271 	}
272 }
273 
dm_dump_static_driver_info(void)274 void dm_dump_static_driver_info(void)
275 {
276 	struct driver_info *drv = ll_entry_start(struct driver_info,
277 						 driver_info);
278 	const int n_ents = ll_entry_count(struct driver_info, driver_info);
279 	struct driver_info *entry;
280 
281 	puts("Driver                    Address\n");
282 	puts("---------------------------------\n");
283 	for (entry = drv; entry != drv + n_ents; entry++)
284 		printf("%-25.25s %p\n", entry->name, entry->plat);
285 }
286 
dm_dump_mem(struct dm_stats * stats)287 void dm_dump_mem(struct dm_stats *stats)
288 {
289 	int total, total_delta;
290 	int i;
291 
292 	/* Support SPL printf() */
293 	printf("Struct sizes: udevice %x, driver %x, uclass %x, uc_driver %x\n",
294 	       (int)sizeof(struct udevice), (int)sizeof(struct driver),
295 	       (int)sizeof(struct uclass), (int)sizeof(struct uclass_driver));
296 	printf("Memory: device %x:%x, device names %x, uclass %x:%x\n",
297 	       stats->dev_count, stats->dev_size, stats->dev_name_size,
298 	       stats->uc_count, stats->uc_size);
299 	printf("\n");
300 	printf("%-15s  %5s  %5s  %5s  %5s  %5s\n", "Attached type", "Count",
301 	       "Size", "Cur", "Tags", "Save");
302 	printf("%-15s  %5s  %5s  %5s  %5s  %5s\n", "---------------", "-----",
303 	       "-----", "-----", "-----", "-----");
304 	total_delta = 0;
305 	for (i = 0; i < DM_TAG_ATTACH_COUNT; i++) {
306 		int cur_size, new_size, delta;
307 
308 		cur_size = stats->dev_count * sizeof(struct udevice);
309 		new_size = stats->dev_count * (sizeof(struct udevice) -
310 			sizeof(void *));
311 		/*
312 		 * Let's assume we can fit each dmtag_node into 32 bits. We can
313 		 * limit the 'tiny tags' feature to SPL with
314 		 * CONFIG_SPL_SYS_MALLOC_F_LEN <= 64KB, so needing 14 bits to
315 		 * point to anything in that region (with 4-byte alignment).
316 		 * So:
317 		 *    4 bits for tag
318 		 *    14 bits for offset of dev
319 		 *    14 bits for offset of data
320 		 */
321 		new_size += stats->attach_count[i] * sizeof(u32);
322 		delta = cur_size - new_size;
323 		total_delta += delta;
324 		printf("%-16s %5x %6x %6x %6x %6x (%d)\n", tag_get_name(i),
325 		       stats->attach_count[i], stats->attach_size[i],
326 		       cur_size, new_size, delta > 0 ? delta : 0, delta);
327 	}
328 	printf("%-16s %5x %6x\n", "uclass", stats->uc_attach_count,
329 	       stats->uc_attach_size);
330 	printf("%-16s %5x %6x  %5s  %5s  %6x (%d)\n", "Attached total",
331 	       stats->attach_count_total + stats->uc_attach_count,
332 	       stats->attach_size_total + stats->uc_attach_size, "", "",
333 	       total_delta > 0 ? total_delta : 0, total_delta);
334 	printf("%-16s %5x %6x\n", "tags", stats->tag_count, stats->tag_size);
335 	printf("\n");
336 	printf("Total size: %x (%d)\n", stats->total_size, stats->total_size);
337 	printf("\n");
338 
339 	total = stats->total_size;
340 	total -= total_delta;
341 	printf("With tags:       %x (%d)\n", total, total);
342 
343 	/* Use singly linked lists in struct udevice (3 nodes in each) */
344 	total -= sizeof(void *) * 3 * stats->dev_count;
345 	printf("- singly-linked: %x (%d)\n", total, total);
346 
347 	/* Use an index into the struct_driver list instead of a pointer */
348 	total = total + stats->dev_count * (1 - sizeof(void *));
349 	printf("- driver index:  %x (%d)\n", total, total);
350 
351 	/* Same with the uclass */
352 	total = total + stats->dev_count * (1 - sizeof(void *));
353 	printf("- uclass index:  %x (%d)\n", total, total);
354 
355 	/* Drop the device name */
356 	printf("Drop device name (not SRAM): %x (%d)\n", stats->dev_name_size,
357 	       stats->dev_name_size);
358 }
359