]>
Commit | Line | Data |
---|---|---|
6df8706d AD |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * K3: System Firmware Loader | |
4 | * | |
5 | * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ | |
6 | * Andreas Dannenberg <dannenberg@ti.com> | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <spl.h> | |
11 | #include <malloc.h> | |
12 | #include <remoteproc.h> | |
90526e9f | 13 | #include <asm/cache.h> |
6df8706d | 14 | #include <linux/soc/ti/ti_sci_protocol.h> |
e15b6e35 VR |
15 | #include <g_dnl.h> |
16 | #include <usb.h> | |
17 | #include <dfu.h> | |
7d0866b9 LV |
18 | #include <dm/uclass-internal.h> |
19 | #include <spi_flash.h> | |
e15b6e35 | 20 | |
6df8706d | 21 | #include <asm/arch/sys_proto.h> |
921b3258 AD |
22 | #include "common.h" |
23 | ||
24 | DECLARE_GLOBAL_DATA_PTR; | |
6df8706d AD |
25 | |
26 | /* Name of the FIT image nodes for SYSFW and its config data */ | |
27 | #define SYSFW_FIRMWARE "sysfw.bin" | |
28 | #define SYSFW_CFG_BOARD "board-cfg.bin" | |
29 | #define SYSFW_CFG_PM "pm-cfg.bin" | |
30 | #define SYSFW_CFG_RM "rm-cfg.bin" | |
31 | #define SYSFW_CFG_SEC "sec-cfg.bin" | |
32 | ||
33 | static bool sysfw_loaded; | |
34 | static void *sysfw_load_address; | |
35 | ||
36 | /* | |
37 | * Populate SPL hook to override the default load address used by the SPL | |
38 | * loader function with a custom address for SYSFW loading. | |
39 | */ | |
40 | struct image_header *spl_get_load_buffer(ssize_t offset, size_t size) | |
41 | { | |
42 | if (sysfw_loaded) | |
43 | return (struct image_header *)(CONFIG_SYS_TEXT_BASE + offset); | |
44 | else if (sysfw_load_address) | |
45 | return sysfw_load_address; | |
46 | else | |
47 | panic("SYSFW load address not defined!"); | |
48 | } | |
49 | ||
50 | /* | |
51 | * Populate SPL hook to skip the default SPL loader FIT post-processing steps | |
52 | * during SYSFW loading and return to the calling function so we can perform | |
53 | * our own custom processing. | |
54 | */ | |
55 | bool spl_load_simple_fit_skip_processing(void) | |
56 | { | |
57 | return !sysfw_loaded; | |
58 | } | |
59 | ||
60 | static int fit_get_data_by_name(const void *fit, int images, const char *name, | |
61 | const void **addr, size_t *size) | |
62 | { | |
63 | int node_offset; | |
64 | ||
65 | node_offset = fdt_subnode_offset(fit, images, name); | |
66 | if (node_offset < 0) | |
67 | return -ENOENT; | |
68 | ||
69 | return fit_image_get_data(fit, node_offset, addr, size); | |
70 | } | |
71 | ||
72 | static void k3_sysfw_load_using_fit(void *fit) | |
73 | { | |
74 | int images; | |
75 | const void *sysfw_addr; | |
76 | size_t sysfw_size; | |
77 | int ret; | |
78 | ||
79 | /* Find the node holding the images information */ | |
80 | images = fdt_path_offset(fit, FIT_IMAGES_PATH); | |
81 | if (images < 0) | |
82 | panic("Cannot find /images node (%d)\n", images); | |
83 | ||
84 | /* Extract System Firmware (SYSFW) image from FIT */ | |
85 | ret = fit_get_data_by_name(fit, images, SYSFW_FIRMWARE, | |
86 | &sysfw_addr, &sysfw_size); | |
87 | if (ret < 0) | |
88 | panic("Error accessing %s node in FIT (%d)\n", SYSFW_FIRMWARE, | |
89 | ret); | |
90 | ||
91 | /* | |
92 | * Start up system controller firmware | |
93 | * | |
94 | * It is assumed that remoteproc device 0 is the corresponding | |
95 | * system-controller that runs SYSFW. Make sure DT reflects the same. | |
96 | */ | |
97 | ret = rproc_dev_init(0); | |
98 | if (ret) | |
99 | panic("rproc failed to be initialized (%d)\n", ret); | |
100 | ||
101 | ret = rproc_load(0, (ulong)sysfw_addr, (ulong)sysfw_size); | |
102 | if (ret) | |
103 | panic("Firmware failed to start on rproc (%d)\n", ret); | |
104 | ||
105 | ret = rproc_start(0); | |
106 | if (ret) | |
107 | panic("Firmware init failed on rproc (%d)\n", ret); | |
108 | } | |
109 | ||
110 | static void k3_sysfw_configure_using_fit(void *fit, | |
111 | struct ti_sci_handle *ti_sci) | |
112 | { | |
113 | struct ti_sci_board_ops *board_ops = &ti_sci->ops.board_ops; | |
114 | int images; | |
115 | const void *cfg_fragment_addr; | |
116 | size_t cfg_fragment_size; | |
117 | int ret; | |
118 | ||
119 | /* Find the node holding the images information */ | |
120 | images = fdt_path_offset(fit, FIT_IMAGES_PATH); | |
121 | if (images < 0) | |
122 | panic("Cannot find /images node (%d)\n", images); | |
123 | ||
124 | /* Extract board configuration from FIT */ | |
125 | ret = fit_get_data_by_name(fit, images, SYSFW_CFG_BOARD, | |
126 | &cfg_fragment_addr, &cfg_fragment_size); | |
127 | if (ret < 0) | |
128 | panic("Error accessing %s node in FIT (%d)\n", SYSFW_CFG_BOARD, | |
129 | ret); | |
130 | ||
131 | /* Apply board configuration to SYSFW */ | |
132 | ret = board_ops->board_config(ti_sci, | |
133 | (u64)(u32)cfg_fragment_addr, | |
134 | (u32)cfg_fragment_size); | |
135 | if (ret) | |
136 | panic("Failed to set board configuration (%d)\n", ret); | |
137 | ||
138 | /* Extract power/clock (PM) specific configuration from FIT */ | |
139 | ret = fit_get_data_by_name(fit, images, SYSFW_CFG_PM, | |
140 | &cfg_fragment_addr, &cfg_fragment_size); | |
141 | if (ret < 0) | |
142 | panic("Error accessing %s node in FIT (%d)\n", SYSFW_CFG_PM, | |
143 | ret); | |
144 | ||
145 | /* Apply power/clock (PM) specific configuration to SYSFW */ | |
146 | ret = board_ops->board_config_pm(ti_sci, | |
147 | (u64)(u32)cfg_fragment_addr, | |
148 | (u32)cfg_fragment_size); | |
149 | if (ret) | |
150 | panic("Failed to set board PM configuration (%d)\n", ret); | |
151 | ||
152 | /* Extract resource management (RM) specific configuration from FIT */ | |
153 | ret = fit_get_data_by_name(fit, images, SYSFW_CFG_RM, | |
154 | &cfg_fragment_addr, &cfg_fragment_size); | |
155 | if (ret < 0) | |
156 | panic("Error accessing %s node in FIT (%d)\n", SYSFW_CFG_RM, | |
157 | ret); | |
158 | ||
159 | /* Apply resource management (RM) configuration to SYSFW */ | |
160 | ret = board_ops->board_config_rm(ti_sci, | |
161 | (u64)(u32)cfg_fragment_addr, | |
162 | (u32)cfg_fragment_size); | |
163 | if (ret) | |
164 | panic("Failed to set board RM configuration (%d)\n", ret); | |
165 | ||
166 | /* Extract security specific configuration from FIT */ | |
167 | ret = fit_get_data_by_name(fit, images, SYSFW_CFG_SEC, | |
168 | &cfg_fragment_addr, &cfg_fragment_size); | |
169 | if (ret < 0) | |
170 | panic("Error accessing %s node in FIT (%d)\n", SYSFW_CFG_SEC, | |
171 | ret); | |
172 | ||
173 | /* Apply security configuration to SYSFW */ | |
174 | ret = board_ops->board_config_security(ti_sci, | |
175 | (u64)(u32)cfg_fragment_addr, | |
176 | (u32)cfg_fragment_size); | |
177 | if (ret) | |
178 | panic("Failed to set board security configuration (%d)\n", | |
179 | ret); | |
180 | } | |
181 | ||
e15b6e35 VR |
182 | #if CONFIG_IS_ENABLED(DFU) |
183 | static int k3_sysfw_dfu_download(void *addr) | |
184 | { | |
185 | char dfu_str[50]; | |
186 | int ret; | |
187 | ||
188 | sprintf(dfu_str, "sysfw.itb ram 0x%p 0x%x", addr, | |
189 | CONFIG_K3_SYSFW_IMAGE_SIZE_MAX); | |
190 | ret = dfu_config_entities(dfu_str, "ram", "0"); | |
191 | if (ret) { | |
192 | dfu_free_entities(); | |
193 | goto exit; | |
194 | } | |
195 | ||
196 | run_usb_dnl_gadget(0, "usb_dnl_dfu"); | |
197 | exit: | |
198 | dfu_free_entities(); | |
199 | return ret; | |
200 | } | |
201 | #endif | |
202 | ||
7d0866b9 LV |
203 | #if CONFIG_IS_ENABLED(SPI_LOAD) |
204 | static void *k3_sysfw_get_spi_addr(void) | |
205 | { | |
206 | struct udevice *dev; | |
207 | fdt_addr_t addr; | |
208 | int ret; | |
209 | ||
210 | ret = uclass_find_device_by_seq(UCLASS_SPI, CONFIG_SF_DEFAULT_BUS, | |
211 | true, &dev); | |
212 | if (ret) | |
213 | return NULL; | |
214 | ||
215 | addr = dev_read_addr_index(dev, 1); | |
216 | if (addr == FDT_ADDR_T_NONE) | |
217 | return NULL; | |
218 | ||
219 | return (void *)(addr + CONFIG_K3_SYSFW_IMAGE_SPI_OFFS); | |
220 | } | |
221 | #endif | |
222 | ||
d45ffb79 FA |
223 | void k3_sysfw_loader(void (*config_pm_pre_callback) (void), |
224 | void (*config_pm_done_callback)(void)) | |
6df8706d AD |
225 | { |
226 | struct spl_image_info spl_image = { 0 }; | |
227 | struct spl_boot_device bootdev = { 0 }; | |
228 | struct ti_sci_handle *ti_sci; | |
7d0866b9 | 229 | int ret = 0; |
6df8706d AD |
230 | |
231 | /* Reserve a block of aligned memory for loading the SYSFW image */ | |
232 | sysfw_load_address = memalign(ARCH_DMA_MINALIGN, | |
233 | CONFIG_K3_SYSFW_IMAGE_SIZE_MAX); | |
234 | if (!sysfw_load_address) | |
235 | panic("Error allocating %u bytes of memory for SYSFW image\n", | |
236 | CONFIG_K3_SYSFW_IMAGE_SIZE_MAX); | |
237 | ||
238 | debug("%s: allocated %u bytes at 0x%p\n", __func__, | |
239 | CONFIG_K3_SYSFW_IMAGE_SIZE_MAX, sysfw_load_address); | |
240 | ||
241 | /* Set load address for legacy modes that bypass spl_get_load_buffer */ | |
242 | spl_image.load_addr = (uintptr_t)sysfw_load_address; | |
243 | ||
244 | bootdev.boot_device = spl_boot_device(); | |
245 | ||
246 | /* Load combined System Controller firmware and config data image */ | |
247 | switch (bootdev.boot_device) { | |
248 | #if CONFIG_IS_ENABLED(MMC_SUPPORT) | |
249 | case BOOT_DEVICE_MMC1: | |
250 | case BOOT_DEVICE_MMC2: | |
251 | case BOOT_DEVICE_MMC2_2: | |
252 | ret = spl_mmc_load(&spl_image, &bootdev, | |
253 | #ifdef CONFIG_K3_SYSFW_IMAGE_NAME | |
254 | CONFIG_K3_SYSFW_IMAGE_NAME, | |
255 | #else | |
256 | NULL, | |
257 | #endif | |
258 | #ifdef CONFIG_K3_SYSFW_IMAGE_MMCSD_RAW_MODE_PART | |
259 | CONFIG_K3_SYSFW_IMAGE_MMCSD_RAW_MODE_PART, | |
260 | #else | |
261 | 0, | |
262 | #endif | |
263 | #ifdef CONFIG_K3_SYSFW_IMAGE_MMCSD_RAW_MODE_SECT | |
264 | CONFIG_K3_SYSFW_IMAGE_MMCSD_RAW_MODE_SECT); | |
265 | #else | |
266 | 0); | |
267 | #endif | |
268 | break; | |
921b3258 | 269 | #endif |
7d0866b9 LV |
270 | #if CONFIG_IS_ENABLED(SPI_LOAD) |
271 | case BOOT_DEVICE_SPI: | |
272 | sysfw_load_address = k3_sysfw_get_spi_addr(); | |
273 | if (!sysfw_load_address) | |
274 | ret = -ENODEV; | |
275 | break; | |
276 | #endif | |
921b3258 AD |
277 | #if CONFIG_IS_ENABLED(YMODEM_SUPPORT) |
278 | case BOOT_DEVICE_UART: | |
279 | #ifdef CONFIG_K3_EARLY_CONS | |
280 | /* | |
281 | * Establish a serial console if not yet available as required | |
282 | * for UART-based boot. For this use the early console feature | |
283 | * that allows setting up a UART for use before SYSFW has been | |
284 | * brought up. Note that the associated UART module's clocks | |
285 | * must have gotten enabled by the ROM bootcode which will be | |
286 | * the case when continuing to boot serially from the same | |
287 | * UART that the ROM loaded the initial bootloader from. | |
288 | */ | |
289 | if (!gd->have_console) | |
290 | early_console_init(); | |
291 | #endif | |
292 | ret = spl_ymodem_load_image(&spl_image, &bootdev); | |
293 | break; | |
e15b6e35 VR |
294 | #endif |
295 | #if CONFIG_IS_ENABLED(DFU) | |
296 | case BOOT_DEVICE_DFU: | |
297 | ret = k3_sysfw_dfu_download(sysfw_load_address); | |
298 | break; | |
6df8706d AD |
299 | #endif |
300 | default: | |
301 | panic("Loading SYSFW image from device %u not supported!\n", | |
302 | bootdev.boot_device); | |
303 | } | |
304 | ||
305 | if (ret) | |
306 | panic("Error %d occurred during loading SYSFW image!\n", ret); | |
307 | ||
308 | /* | |
309 | * Now that SYSFW got loaded set helper flag to restore regular SPL | |
310 | * loader behavior so we can later boot into the next stage as expected. | |
311 | */ | |
312 | sysfw_loaded = true; | |
313 | ||
314 | /* Ensure the SYSFW image is in FIT format */ | |
315 | if (image_get_magic((const image_header_t *)sysfw_load_address) != | |
316 | FDT_MAGIC) | |
317 | panic("SYSFW image not in FIT format!\n"); | |
318 | ||
319 | /* Extract and start SYSFW */ | |
320 | k3_sysfw_load_using_fit(sysfw_load_address); | |
321 | ||
322 | /* Get handle for accessing SYSFW services */ | |
323 | ti_sci = get_ti_sci_handle(); | |
324 | ||
d45ffb79 FA |
325 | if (config_pm_pre_callback) |
326 | config_pm_pre_callback(); | |
327 | ||
6df8706d AD |
328 | /* Parse and apply the different SYSFW configuration fragments */ |
329 | k3_sysfw_configure_using_fit(sysfw_load_address, ti_sci); | |
330 | ||
331 | /* | |
332 | * Now that all clocks and PM aspects are setup, invoke a user- | |
333 | * provided callback function. Usually this callback would be used | |
334 | * to setup or re-configure the U-Boot console UART. | |
335 | */ | |
336 | if (config_pm_done_callback) | |
337 | config_pm_done_callback(); | |
6df8706d | 338 | } |