]>
Commit | Line | Data |
---|---|---|
b940ca64 GR |
1 | /* |
2 | * Copyright (C) 2014 Freescale Semiconductor | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
7b3bd9a7 | 6 | |
b940ca64 GR |
7 | #include <errno.h> |
8 | #include <asm/io.h> | |
7b3bd9a7 GR |
9 | #include <fsl-mc/fsl_mc.h> |
10 | #include <fsl-mc/fsl_mc_sys.h> | |
11 | #include <fsl-mc/fsl_dpmng.h> | |
b940ca64 GR |
12 | |
13 | DECLARE_GLOBAL_DATA_PTR; | |
14 | static int mc_boot_status; | |
15 | ||
16 | /** | |
17 | * Copying MC firmware or DPL image to DDR | |
18 | */ | |
19 | static int mc_copy_image(const char *title, | |
7b3bd9a7 | 20 | u64 image_addr, u32 image_size, u64 mc_ram_addr) |
b940ca64 GR |
21 | { |
22 | debug("%s copied to address %p\n", title, (void *)mc_ram_addr); | |
23 | memcpy((void *)mc_ram_addr, (void *)image_addr, image_size); | |
24 | return 0; | |
25 | } | |
26 | ||
27 | /** | |
28 | * MC firmware FIT image parser checks if the image is in FIT | |
29 | * format, verifies integrity of the image and calculates | |
30 | * raw image address and size values. | |
7b3bd9a7 | 31 | * Returns 0 on success and a negative errno on error. |
b940ca64 GR |
32 | * task fail. |
33 | **/ | |
b940ca64 GR |
34 | int parse_mc_firmware_fit_image(const void **raw_image_addr, |
35 | size_t *raw_image_size) | |
36 | { | |
37 | int format; | |
38 | void *fit_hdr; | |
39 | int node_offset; | |
40 | const void *data; | |
41 | size_t size; | |
42 | const char *uname = "firmware"; | |
43 | ||
7b3bd9a7 | 44 | /* Check if the image is in NOR flash */ |
b940ca64 GR |
45 | #ifdef CONFIG_SYS_LS_MC_FW_IN_NOR |
46 | fit_hdr = (void *)CONFIG_SYS_LS_MC_FW_ADDR; | |
47 | #else | |
48 | #error "No CONFIG_SYS_LS_MC_FW_IN_xxx defined" | |
49 | #endif | |
50 | ||
51 | /* Check if Image is in FIT format */ | |
52 | format = genimg_get_format(fit_hdr); | |
53 | ||
54 | if (format != IMAGE_FORMAT_FIT) { | |
7b3bd9a7 GR |
55 | printf("fsl-mc: ERROR: Bad firmware image (not a FIT image)\n"); |
56 | return -EINVAL; | |
b940ca64 GR |
57 | } |
58 | ||
59 | if (!fit_check_format(fit_hdr)) { | |
7b3bd9a7 GR |
60 | printf("fsl-mc: ERROR: Bad firmware image (bad FIT header)\n"); |
61 | return -EINVAL; | |
b940ca64 GR |
62 | } |
63 | ||
64 | node_offset = fit_image_get_node(fit_hdr, uname); | |
65 | ||
66 | if (node_offset < 0) { | |
7b3bd9a7 GR |
67 | printf("fsl-mc: ERROR: Bad firmware image (missing subimage)\n"); |
68 | return -ENOENT; | |
b940ca64 GR |
69 | } |
70 | ||
71 | /* Verify MC firmware image */ | |
72 | if (!(fit_image_verify(fit_hdr, node_offset))) { | |
7b3bd9a7 GR |
73 | printf("fsl-mc: ERROR: Bad firmware image (bad CRC)\n"); |
74 | return -EINVAL; | |
b940ca64 GR |
75 | } |
76 | ||
77 | /* Get address and size of raw image */ | |
78 | fit_image_get_data(fit_hdr, node_offset, &data, &size); | |
79 | ||
80 | *raw_image_addr = data; | |
81 | *raw_image_size = size; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | int mc_init(bd_t *bis) | |
87 | { | |
88 | int error = 0; | |
89 | int timeout = 200000; | |
90 | struct mc_ccsr_registers __iomem *mc_ccsr_regs = MC_CCSR_BASE_ADDR; | |
91 | u64 mc_ram_addr; | |
92 | u64 mc_dpl_offset; | |
93 | u32 reg_gsr; | |
94 | u32 mc_fw_boot_status; | |
7b3bd9a7 | 95 | void *dpl_fdt_hdr; |
b940ca64 GR |
96 | int dpl_size; |
97 | const void *raw_image_addr; | |
98 | size_t raw_image_size = 0; | |
7b3bd9a7 GR |
99 | struct fsl_mc_io mc_io; |
100 | int portal_id; | |
101 | struct mc_version mc_ver_info; | |
b940ca64 GR |
102 | |
103 | /* | |
104 | * The MC private DRAM block was already carved at the end of DRAM | |
105 | * by board_init_f() using CONFIG_SYS_MEM_TOP_HIDE: | |
106 | */ | |
107 | if (gd->bd->bi_dram[1].start) { | |
108 | mc_ram_addr = | |
109 | gd->bd->bi_dram[1].start + gd->bd->bi_dram[1].size; | |
110 | } else { | |
111 | mc_ram_addr = | |
112 | gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size; | |
113 | } | |
114 | ||
115 | /* | |
116 | * Management Complex cores should be held at reset out of POR. | |
117 | * U-boot should be the first software to touch MC. To be safe, | |
118 | * we reset all cores again by setting GCR1 to 0. It doesn't do | |
119 | * anything if they are held at reset. After we setup the firmware | |
120 | * we kick off MC by deasserting the reset bit for core 0, and | |
121 | * deasserting the reset bits for Command Portal Managers. | |
122 | * The stop bits are not touched here. They are used to stop the | |
123 | * cores when they are active. Setting stop bits doesn't stop the | |
124 | * cores from fetching instructions when they are released from | |
125 | * reset. | |
126 | */ | |
127 | out_le32(&mc_ccsr_regs->reg_gcr1, 0); | |
128 | dmb(); | |
129 | ||
130 | error = parse_mc_firmware_fit_image(&raw_image_addr, &raw_image_size); | |
131 | if (error != 0) | |
132 | goto out; | |
133 | /* | |
134 | * Load the MC FW at the beginning of the MC private DRAM block: | |
135 | */ | |
7b3bd9a7 GR |
136 | mc_copy_image("MC Firmware", |
137 | (u64)raw_image_addr, raw_image_size, mc_ram_addr); | |
138 | ||
139 | /* | |
140 | * Get address and size of the DPL blob stored in flash: | |
141 | */ | |
142 | #ifdef CONFIG_SYS_LS_MC_DPL_IN_NOR | |
143 | dpl_fdt_hdr = (void *)CONFIG_SYS_LS_MC_DPL_ADDR; | |
144 | #else | |
145 | #error "No CONFIG_SYS_LS_MC_DPL_IN_xxx defined" | |
146 | #endif | |
147 | ||
148 | error = fdt_check_header(dpl_fdt_hdr); | |
149 | if (error != 0) { | |
150 | printf("fsl-mc: ERROR: Bad DPL image (bad header)\n"); | |
151 | goto out; | |
152 | } | |
153 | ||
154 | dpl_size = fdt_totalsize(dpl_fdt_hdr); | |
155 | if (dpl_size > CONFIG_SYS_LS_MC_DPL_MAX_LENGTH) { | |
156 | printf("fsl-mc: ERROR: Bad DPL image (too large: %d)\n", | |
157 | dpl_size); | |
158 | error = -EINVAL; | |
159 | goto out; | |
160 | } | |
b940ca64 GR |
161 | |
162 | /* | |
163 | * Calculate offset in the MC private DRAM block at which the MC DPL | |
164 | * blob is to be placed: | |
165 | */ | |
166 | #ifdef CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET | |
7b3bd9a7 GR |
167 | BUILD_BUG_ON((CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET & 0x3) != 0 || |
168 | CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET > 0xffffffff); | |
b940ca64 GR |
169 | |
170 | mc_dpl_offset = CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET; | |
171 | #else | |
172 | mc_dpl_offset = mc_get_dram_block_size() - | |
7b3bd9a7 | 173 | roundup(CONFIG_SYS_LS_MC_DPL_MAX_LENGTH, 4096); |
b940ca64 GR |
174 | |
175 | if ((mc_dpl_offset & 0x3) != 0 || mc_dpl_offset > 0xffffffff) { | |
176 | printf("%s: Invalid MC DPL offset: %llu\n", | |
177 | __func__, mc_dpl_offset); | |
178 | error = -EINVAL; | |
179 | goto out; | |
180 | } | |
181 | #endif | |
182 | ||
b940ca64 GR |
183 | /* |
184 | * Load the MC DPL blob at the far end of the MC private DRAM block: | |
7b3bd9a7 GR |
185 | * |
186 | * TODO: Should we place the DPL at a different location to match | |
187 | * assumptions of MC firmware about its memory layout? | |
b940ca64 | 188 | */ |
7b3bd9a7 GR |
189 | mc_copy_image("MC DPL blob", |
190 | (u64)dpl_fdt_hdr, dpl_size, mc_ram_addr + mc_dpl_offset); | |
b940ca64 GR |
191 | |
192 | debug("mc_ccsr_regs %p\n", mc_ccsr_regs); | |
193 | ||
194 | /* | |
195 | * Tell MC where the MC Firmware image was loaded in DDR: | |
196 | */ | |
197 | out_le32(&mc_ccsr_regs->reg_mcfbalr, (u32)mc_ram_addr); | |
198 | out_le32(&mc_ccsr_regs->reg_mcfbahr, (u32)((u64)mc_ram_addr >> 32)); | |
199 | out_le32(&mc_ccsr_regs->reg_mcfapr, MCFAPR_BYPASS_ICID_MASK); | |
200 | ||
201 | /* | |
202 | * Tell MC where the DPL blob was loaded in DDR, by indicating | |
203 | * its offset relative to the beginning of the DDR block | |
204 | * allocated to the MC firmware. The MC firmware is responsible | |
205 | * for checking that there is no overlap between the DPL blob | |
206 | * and the runtime heap and stack of the MC firmware itself. | |
207 | * | |
208 | * NOTE: bits [31:2] of this offset need to be stored in bits [29:0] of | |
209 | * the GSR MC CCSR register. So, this offset is assumed to be 4-byte | |
210 | * aligned. | |
211 | * Care must be taken not to write 1s into bits 31 and 30 of the GSR in | |
212 | * this case as the SoC COP or PIC will be signaled. | |
213 | */ | |
214 | out_le32(&mc_ccsr_regs->reg_gsr, (u32)(mc_dpl_offset >> 2)); | |
215 | ||
7b3bd9a7 GR |
216 | printf("\nfsl-mc: Booting Management Complex ...\n"); |
217 | ||
b940ca64 GR |
218 | /* |
219 | * Deassert reset and release MC core 0 to run | |
220 | */ | |
221 | out_le32(&mc_ccsr_regs->reg_gcr1, GCR1_P1_DE_RST | GCR1_M_ALL_DE_RST); | |
222 | dmb(); | |
223 | debug("Polling mc_ccsr_regs->reg_gsr ...\n"); | |
224 | ||
225 | for (;;) { | |
226 | reg_gsr = in_le32(&mc_ccsr_regs->reg_gsr); | |
227 | mc_fw_boot_status = (reg_gsr & GSR_FS_MASK); | |
228 | if (mc_fw_boot_status & 0x1) | |
229 | break; | |
230 | ||
231 | udelay(1000); /* throttle polling */ | |
232 | if (timeout-- <= 0) | |
233 | break; | |
234 | } | |
235 | ||
236 | if (timeout <= 0) { | |
7b3bd9a7 | 237 | printf("fsl-mc: timeout booting management complex firmware\n"); |
b940ca64 GR |
238 | |
239 | /* TODO: Get an error status from an MC CCSR register */ | |
240 | error = -ETIMEDOUT; | |
241 | goto out; | |
242 | } | |
243 | ||
b940ca64 GR |
244 | if (mc_fw_boot_status != 0x1) { |
245 | /* | |
246 | * TODO: Identify critical errors from the GSR register's FS | |
247 | * field and for those errors, set error to -ENODEV or other | |
248 | * appropriate errno, so that the status property is set to | |
249 | * failure in the fsl,dprc device tree node. | |
250 | */ | |
7b3bd9a7 GR |
251 | printf("fsl-mc: WARNING: Firmware booted with error (GSR: %#x)\n", |
252 | reg_gsr); | |
b940ca64 GR |
253 | } |
254 | ||
7b3bd9a7 GR |
255 | /* |
256 | * TODO: need to obtain the portal_id for the root container from the | |
257 | * DPL | |
258 | */ | |
259 | portal_id = 0; | |
260 | ||
261 | /* | |
262 | * Check that the MC firmware is responding portal commands: | |
263 | */ | |
264 | mc_io.mmio_regs = SOC_MC_PORTAL_ADDR(portal_id); | |
265 | debug("Checking access to MC portal of root DPRC container (portal_id %d, portal physical addr %p)\n", | |
266 | portal_id, mc_io.mmio_regs); | |
267 | ||
268 | error = mc_get_version(&mc_io, &mc_ver_info); | |
269 | if (error != 0) { | |
270 | printf("fsl-mc: ERROR: Firmware version check failed (error: %d)\n", | |
271 | error); | |
272 | goto out; | |
273 | } | |
274 | ||
275 | if (MC_VER_MAJOR != mc_ver_info.major) | |
276 | printf("fsl-mc: ERROR: Firmware major version mismatch (found: %d, expected: %d)\n", | |
277 | mc_ver_info.major, MC_VER_MAJOR); | |
278 | ||
279 | if (MC_VER_MINOR != mc_ver_info.minor) | |
280 | printf("fsl-mc: WARNING: Firmware minor version mismatch (found: %d, expected: %d)\n", | |
281 | mc_ver_info.minor, MC_VER_MINOR); | |
282 | ||
283 | printf("fsl-mc: Management Complex booted (version: %d.%d.%d, boot status: %#x)\n", | |
284 | mc_ver_info.major, mc_ver_info.minor, mc_ver_info.revision, | |
285 | mc_fw_boot_status); | |
b940ca64 GR |
286 | out: |
287 | if (error != 0) | |
288 | mc_boot_status = -error; | |
289 | else | |
290 | mc_boot_status = 0; | |
291 | ||
292 | return error; | |
293 | } | |
294 | ||
295 | int get_mc_boot_status(void) | |
296 | { | |
297 | return mc_boot_status; | |
298 | } | |
299 | ||
300 | /** | |
301 | * Return the actual size of the MC private DRAM block. | |
302 | * | |
303 | * NOTE: For now this function always returns the minimum required size, | |
304 | * However, in the future, the actual size may be obtained from an environment | |
305 | * variable. | |
306 | */ | |
307 | unsigned long mc_get_dram_block_size(void) | |
308 | { | |
309 | return CONFIG_SYS_LS_MC_DRAM_BLOCK_MIN_SIZE; | |
310 | } |