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