]>
Commit | Line | Data |
---|---|---|
2f84e9cf KM |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2021 | |
4 | * Köry Maincent, Bootlin, <[email protected]> | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
18552d2a | 8 | #include <bootdev.h> |
2f84e9cf | 9 | #include <command.h> |
18552d2a | 10 | #include <dm.h> |
2f84e9cf KM |
11 | #include <malloc.h> |
12 | #include <extension_board.h> | |
13 | #include <mapmem.h> | |
14 | #include <linux/libfdt.h> | |
15 | #include <fdt_support.h> | |
16 | ||
17 | static LIST_HEAD(extension_list); | |
18 | ||
19 | static int extension_apply(struct extension *extension) | |
20 | { | |
21 | char *overlay_cmd; | |
22 | ulong extrasize, overlay_addr; | |
23 | struct fdt_header *blob; | |
24 | ||
25 | if (!working_fdt) { | |
26 | printf("No FDT memory address configured. Please configure\n" | |
27 | "the FDT address via \"fdt addr <address>\" command.\n"); | |
28 | return CMD_RET_FAILURE; | |
29 | } | |
30 | ||
31 | overlay_cmd = env_get("extension_overlay_cmd"); | |
32 | if (!overlay_cmd) { | |
33 | printf("Environment extension_overlay_cmd is missing\n"); | |
34 | return CMD_RET_FAILURE; | |
35 | } | |
36 | ||
37 | overlay_addr = env_get_hex("extension_overlay_addr", 0); | |
38 | if (!overlay_addr) { | |
39 | printf("Environment extension_overlay_addr is missing\n"); | |
40 | return CMD_RET_FAILURE; | |
41 | } | |
42 | ||
43 | env_set("extension_overlay_name", extension->overlay); | |
44 | if (run_command(overlay_cmd, 0) != 0) | |
45 | return CMD_RET_FAILURE; | |
46 | ||
47 | extrasize = env_get_hex("filesize", 0); | |
48 | if (!extrasize) | |
49 | return CMD_RET_FAILURE; | |
50 | ||
51 | fdt_shrink_to_minimum(working_fdt, extrasize); | |
52 | ||
53 | blob = map_sysmem(overlay_addr, 0); | |
54 | if (!fdt_valid(&blob)) | |
55 | return CMD_RET_FAILURE; | |
56 | ||
57 | /* apply method prints messages on error */ | |
58 | if (fdt_overlay_apply_verbose(working_fdt, blob)) | |
59 | return CMD_RET_FAILURE; | |
60 | ||
61 | return CMD_RET_SUCCESS; | |
62 | } | |
63 | ||
64 | static int do_extension_list(struct cmd_tbl *cmdtp, int flag, | |
65 | int argc, char *const argv[]) | |
66 | { | |
67 | int i = 0; | |
68 | struct extension *extension; | |
69 | ||
70 | if (list_empty(&extension_list)) { | |
71 | printf("No extension registered - Please run \"extension scan\"\n"); | |
72 | return CMD_RET_SUCCESS; | |
73 | } | |
74 | ||
75 | list_for_each_entry(extension, &extension_list, list) { | |
76 | printf("Extension %d: %s\n", i++, extension->name); | |
77 | printf("\tManufacturer: \t\t%s\n", extension->owner); | |
78 | printf("\tVersion: \t\t%s\n", extension->version); | |
79 | printf("\tDevicetree overlay: \t%s\n", extension->overlay); | |
80 | printf("\tOther information: \t%s\n", extension->other); | |
81 | } | |
82 | return CMD_RET_SUCCESS; | |
83 | } | |
84 | ||
35ce1461 | 85 | static int extension_scan(bool show) |
2f84e9cf KM |
86 | { |
87 | struct extension *extension, *next; | |
88 | int extension_num; | |
89 | ||
90 | list_for_each_entry_safe(extension, next, &extension_list, list) { | |
91 | list_del(&extension->list); | |
92 | free(extension); | |
93 | } | |
94 | extension_num = extension_board_scan(&extension_list); | |
35ce1461 SG |
95 | if (show && extension_num >= 0) |
96 | printf("Found %d extension board(s).\n", extension_num); | |
97 | ||
98 | /* either the number of extensions, or -ve for error */ | |
99 | return extension_num; | |
100 | } | |
101 | ||
2f84e9cf | 102 | |
35ce1461 SG |
103 | static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, |
104 | int argc, char *const argv[]) | |
105 | { | |
106 | int extension_num; | |
107 | ||
108 | extension_num = extension_scan(true); | |
2f84e9cf KM |
109 | if (extension_num < 0) |
110 | return CMD_RET_FAILURE; | |
111 | ||
2f84e9cf KM |
112 | return CMD_RET_SUCCESS; |
113 | } | |
114 | ||
115 | static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, | |
116 | int argc, char *const argv[]) | |
117 | { | |
118 | struct extension *extension = NULL; | |
119 | struct list_head *entry; | |
120 | int i = 0, extension_id, ret; | |
121 | ||
122 | if (argc < 2) | |
123 | return CMD_RET_USAGE; | |
124 | ||
125 | if (strcmp(argv[1], "all") == 0) { | |
689525b1 | 126 | ret = CMD_RET_FAILURE; |
2f84e9cf KM |
127 | list_for_each_entry(extension, &extension_list, list) { |
128 | ret = extension_apply(extension); | |
129 | if (ret != CMD_RET_SUCCESS) | |
130 | break; | |
131 | } | |
132 | } else { | |
133 | extension_id = simple_strtol(argv[1], NULL, 10); | |
134 | list_for_each(entry, &extension_list) { | |
135 | if (i == extension_id) { | |
136 | extension = list_entry(entry, struct extension, list); | |
137 | break; | |
138 | } | |
139 | i++; | |
140 | } | |
141 | ||
142 | if (!extension) { | |
143 | printf("Wrong extension number\n"); | |
144 | return CMD_RET_FAILURE; | |
145 | } | |
146 | ||
147 | ret = extension_apply(extension); | |
148 | } | |
149 | ||
150 | return ret; | |
151 | } | |
152 | ||
153 | static struct cmd_tbl cmd_extension[] = { | |
154 | U_BOOT_CMD_MKENT(scan, 1, 1, do_extension_scan, "", ""), | |
155 | U_BOOT_CMD_MKENT(list, 1, 0, do_extension_list, "", ""), | |
156 | U_BOOT_CMD_MKENT(apply, 2, 0, do_extension_apply, "", ""), | |
157 | }; | |
158 | ||
159 | static int do_extensionops(struct cmd_tbl *cmdtp, int flag, int argc, | |
160 | char *const argv[]) | |
161 | { | |
162 | struct cmd_tbl *cp; | |
163 | ||
164 | /* Drop the extension command */ | |
165 | argc--; | |
166 | argv++; | |
167 | ||
168 | cp = find_cmd_tbl(argv[0], cmd_extension, ARRAY_SIZE(cmd_extension)); | |
169 | if (cp) | |
170 | return cp->cmd(cmdtp, flag, argc, argv); | |
171 | ||
172 | return CMD_RET_USAGE; | |
173 | } | |
174 | ||
175 | U_BOOT_CMD(extension, 3, 1, do_extensionops, | |
176 | "Extension board management sub system", | |
177 | "scan - scan plugged extension(s) board(s)\n" | |
178 | "extension list - lists available extension(s) board(s)\n" | |
179 | "extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n" | |
180 | ); | |
18552d2a SG |
181 | |
182 | static int extension_bootdev_hunt(struct bootdev_hunter *info, bool show) | |
183 | { | |
184 | int ret; | |
185 | ||
186 | ret = env_set_hex("extension_overlay_addr", | |
187 | env_get_hex("fdtoverlay_addr_r", 0)); | |
188 | if (ret) | |
189 | return log_msg_ret("env", ret); | |
190 | ||
191 | ret = extension_scan(show); | |
192 | if (ret < 0) | |
193 | return log_msg_ret("ext", ret); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | /* extensions should have a uclass - for now we use UCLASS_SIMPLE_BUS uclass */ | |
199 | BOOTDEV_HUNTER(extension_bootdev_hunter) = { | |
200 | .prio = BOOTDEVP_1_PRE_SCAN, | |
201 | .uclass = UCLASS_SIMPLE_BUS, | |
202 | .hunt = extension_bootdev_hunt, | |
203 | }; |