]>
Commit | Line | Data |
---|---|---|
c916d7c9 KG |
1 | /* |
2 | * Copyright 2009-2011 Freescale Semiconductor, Inc. | |
3 | * Dave Liu <[email protected]> | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
c916d7c9 KG |
6 | */ |
7 | #include <common.h> | |
8 | #include <malloc.h> | |
9 | #include <asm/io.h> | |
10 | #include <asm/errno.h> | |
11 | ||
12 | #include "fm.h" | |
13 | #include "../../qe/qe.h" /* For struct qe_firmware */ | |
14 | ||
f2717b47 | 15 | #ifdef CONFIG_SYS_QE_FMAN_FW_IN_NAND |
c916d7c9 KG |
16 | #include <nand.h> |
17 | #elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) | |
18 | #include <spi_flash.h> | |
f2717b47 | 19 | #elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC) |
c916d7c9 KG |
20 | #include <mmc.h> |
21 | #endif | |
22 | ||
23 | struct fm_muram muram[CONFIG_SYS_NUM_FMAN]; | |
24 | ||
9fc29db1 | 25 | void *fm_muram_base(int fm_idx) |
c916d7c9 KG |
26 | { |
27 | return muram[fm_idx].base; | |
28 | } | |
29 | ||
9fc29db1 | 30 | void *fm_muram_alloc(int fm_idx, size_t size, ulong align) |
c916d7c9 | 31 | { |
9fc29db1 HZ |
32 | void *ret; |
33 | ulong align_mask; | |
34 | size_t off; | |
35 | void *save; | |
c916d7c9 KG |
36 | |
37 | align_mask = align - 1; | |
38 | save = muram[fm_idx].alloc; | |
39 | ||
9fc29db1 | 40 | off = (ulong)save & align_mask; |
c916d7c9 KG |
41 | if (off != 0) |
42 | muram[fm_idx].alloc += (align - off); | |
43 | off = size & align_mask; | |
44 | if (off != 0) | |
45 | size += (align - off); | |
46 | if ((muram[fm_idx].alloc + size) >= muram[fm_idx].top) { | |
47 | muram[fm_idx].alloc = save; | |
48 | printf("%s: run out of ram.\n", __func__); | |
9fc29db1 | 49 | return NULL; |
c916d7c9 KG |
50 | } |
51 | ||
52 | ret = muram[fm_idx].alloc; | |
53 | muram[fm_idx].alloc += size; | |
54 | memset((void *)ret, 0, size); | |
55 | ||
56 | return ret; | |
57 | } | |
58 | ||
59 | static void fm_init_muram(int fm_idx, void *reg) | |
60 | { | |
9fc29db1 | 61 | void *base = reg; |
c916d7c9 KG |
62 | |
63 | muram[fm_idx].base = base; | |
64 | muram[fm_idx].size = CONFIG_SYS_FM_MURAM_SIZE; | |
65 | muram[fm_idx].alloc = base + FM_MURAM_RES_SIZE; | |
66 | muram[fm_idx].top = base + CONFIG_SYS_FM_MURAM_SIZE; | |
67 | } | |
68 | ||
69 | /* | |
70 | * fm_upload_ucode - Fman microcode upload worker function | |
71 | * | |
72 | * This function does the actual uploading of an Fman microcode | |
73 | * to an Fman. | |
74 | */ | |
75 | static void fm_upload_ucode(int fm_idx, struct fm_imem *imem, | |
76 | u32 *ucode, unsigned int size) | |
77 | { | |
78 | unsigned int i; | |
79 | unsigned int timeout = 1000000; | |
80 | ||
81 | /* enable address auto increase */ | |
82 | out_be32(&imem->iadd, IRAM_IADD_AIE); | |
83 | /* write microcode to IRAM */ | |
84 | for (i = 0; i < size / 4; i++) | |
648bde6d | 85 | out_be32(&imem->idata, (be32_to_cpu(ucode[i]))); |
c916d7c9 KG |
86 | |
87 | /* verify if the writing is over */ | |
88 | out_be32(&imem->iadd, 0); | |
648bde6d | 89 | while ((in_be32(&imem->idata) != be32_to_cpu(ucode[0])) && --timeout) |
c916d7c9 KG |
90 | ; |
91 | if (!timeout) | |
92 | printf("Fman%u: microcode upload timeout\n", fm_idx + 1); | |
93 | ||
94 | /* enable microcode from IRAM */ | |
95 | out_be32(&imem->iready, IRAM_READY); | |
96 | } | |
97 | ||
98 | /* | |
99 | * Upload an Fman firmware | |
100 | * | |
101 | * This function is similar to qe_upload_firmware(), exception that it uploads | |
102 | * a microcode to the Fman instead of the QE. | |
103 | * | |
104 | * Because the process for uploading a microcode to the Fman is similar for | |
105 | * that of the QE, the QE firmware binary format is used for Fman microcode. | |
106 | * It should be possible to unify these two functions, but for now we keep them | |
107 | * separate. | |
108 | */ | |
109 | static int fman_upload_firmware(int fm_idx, | |
110 | struct fm_imem *fm_imem, | |
111 | const struct qe_firmware *firmware) | |
112 | { | |
113 | unsigned int i; | |
114 | u32 crc; | |
115 | size_t calc_size = sizeof(struct qe_firmware); | |
116 | size_t length; | |
117 | const struct qe_header *hdr; | |
118 | ||
119 | if (!firmware) { | |
120 | printf("Fman%u: Invalid address for firmware\n", fm_idx + 1); | |
121 | return -EINVAL; | |
122 | } | |
123 | ||
124 | hdr = &firmware->header; | |
125 | length = be32_to_cpu(hdr->length); | |
126 | ||
127 | /* Check the magic */ | |
128 | if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || | |
129 | (hdr->magic[2] != 'F')) { | |
130 | printf("Fman%u: Data at %p is not a firmware\n", fm_idx + 1, | |
131 | firmware); | |
132 | return -EPERM; | |
133 | } | |
134 | ||
135 | /* Check the version */ | |
136 | if (hdr->version != 1) { | |
137 | printf("Fman%u: Unsupported firmware version %u\n", fm_idx + 1, | |
138 | hdr->version); | |
139 | return -EPERM; | |
140 | } | |
141 | ||
142 | /* Validate some of the fields */ | |
143 | if ((firmware->count != 1)) { | |
144 | printf("Fman%u: Invalid data in firmware header\n", fm_idx + 1); | |
145 | return -EINVAL; | |
146 | } | |
147 | ||
148 | /* Validate the length and check if there's a CRC */ | |
149 | calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); | |
150 | ||
151 | for (i = 0; i < firmware->count; i++) | |
152 | /* | |
153 | * For situations where the second RISC uses the same microcode | |
154 | * as the first, the 'code_offset' and 'count' fields will be | |
155 | * zero, so it's okay to add those. | |
156 | */ | |
157 | calc_size += sizeof(u32) * | |
158 | be32_to_cpu(firmware->microcode[i].count); | |
159 | ||
160 | /* Validate the length */ | |
161 | if (length != calc_size + sizeof(u32)) { | |
162 | printf("Fman%u: Invalid length in firmware header\n", | |
163 | fm_idx + 1); | |
164 | return -EPERM; | |
165 | } | |
166 | ||
167 | /* | |
168 | * Validate the CRC. We would normally call crc32_no_comp(), but that | |
169 | * function isn't available unless you turn on JFFS support. | |
170 | */ | |
171 | crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size)); | |
172 | if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) { | |
173 | printf("Fman%u: Firmware CRC is invalid\n", fm_idx + 1); | |
174 | return -EIO; | |
175 | } | |
176 | ||
177 | /* Loop through each microcode. */ | |
178 | for (i = 0; i < firmware->count; i++) { | |
179 | const struct qe_microcode *ucode = &firmware->microcode[i]; | |
180 | ||
181 | /* Upload a microcode if it's present */ | |
648bde6d | 182 | if (be32_to_cpu(ucode->code_offset)) { |
c916d7c9 KG |
183 | u32 ucode_size; |
184 | u32 *code; | |
185 | printf("Fman%u: Uploading microcode version %u.%u.%u\n", | |
186 | fm_idx + 1, ucode->major, ucode->minor, | |
187 | ucode->revision); | |
648bde6d HZ |
188 | code = (void *)firmware + |
189 | be32_to_cpu(ucode->code_offset); | |
190 | ucode_size = sizeof(u32) * be32_to_cpu(ucode->count); | |
c916d7c9 KG |
191 | fm_upload_ucode(fm_idx, fm_imem, code, ucode_size); |
192 | } | |
193 | } | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static u32 fm_assign_risc(int port_id) | |
199 | { | |
200 | u32 risc_sel, val; | |
201 | risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1; | |
202 | val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK; | |
203 | val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel); | |
204 | ||
205 | return val; | |
206 | } | |
207 | ||
208 | static void fm_init_fpm(struct fm_fpm *fpm) | |
209 | { | |
210 | int i, port_id; | |
211 | u32 val; | |
212 | ||
213 | setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC | | |
214 | FMFPEE_CER | FMFPEE_DER); | |
215 | ||
216 | /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */ | |
217 | ||
218 | /* offline/parser port */ | |
219 | for (i = 0; i < MAX_NUM_OH_PORT; i++) { | |
220 | port_id = OH_PORT_ID_BASE + i; | |
221 | val = fm_assign_risc(port_id); | |
222 | out_be32(&fpm->fpmprc, val); | |
223 | } | |
224 | /* Rx 1G port */ | |
225 | for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { | |
226 | port_id = RX_PORT_1G_BASE + i; | |
227 | val = fm_assign_risc(port_id); | |
228 | out_be32(&fpm->fpmprc, val); | |
229 | } | |
230 | /* Tx 1G port */ | |
231 | for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { | |
232 | port_id = TX_PORT_1G_BASE + i; | |
233 | val = fm_assign_risc(port_id); | |
234 | out_be32(&fpm->fpmprc, val); | |
235 | } | |
236 | /* Rx 10G port */ | |
237 | port_id = RX_PORT_10G_BASE; | |
238 | val = fm_assign_risc(port_id); | |
239 | out_be32(&fpm->fpmprc, val); | |
240 | /* Tx 10G port */ | |
241 | port_id = TX_PORT_10G_BASE; | |
242 | val = fm_assign_risc(port_id); | |
243 | out_be32(&fpm->fpmprc, val); | |
244 | ||
245 | /* disable the dispatch limit in IM case */ | |
246 | out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE); | |
247 | /* clear events */ | |
248 | out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT); | |
249 | ||
250 | /* clear risc events */ | |
251 | for (i = 0; i < 4; i++) | |
252 | out_be32(&fpm->fpmcev[i], 0xffffffff); | |
253 | ||
254 | /* clear error */ | |
255 | out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC); | |
256 | } | |
257 | ||
258 | static int fm_init_bmi(int fm_idx, struct fm_bmi_common *bmi) | |
259 | { | |
260 | int blk, i, port_id; | |
9fc29db1 HZ |
261 | u32 val; |
262 | size_t offset; | |
263 | void *base; | |
c916d7c9 KG |
264 | |
265 | /* alloc free buffer pool in MURAM */ | |
266 | base = fm_muram_alloc(fm_idx, FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN); | |
267 | if (!base) { | |
268 | printf("%s: no muram for free buffer pool\n", __func__); | |
269 | return -ENOMEM; | |
270 | } | |
271 | offset = base - fm_muram_base(fm_idx); | |
272 | ||
273 | /* Need 128KB total free buffer pool size */ | |
274 | val = offset / 256; | |
275 | blk = FM_FREE_POOL_SIZE / 256; | |
276 | /* in IM, we must not begin from offset 0 in MURAM */ | |
277 | val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT); | |
278 | out_be32(&bmi->fmbm_cfg1, val); | |
279 | ||
280 | /* disable all BMI interrupt */ | |
281 | out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL); | |
282 | ||
283 | /* clear all events */ | |
284 | out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL); | |
285 | ||
286 | /* | |
287 | * set port parameters - FMBM_PP_x | |
288 | * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1 | |
289 | * max dma 10G Rx/Tx=3, others is 1 | |
290 | * set port FIFO size - FMBM_PFS_x | |
291 | * 4KB for all Rx and Tx ports | |
292 | */ | |
293 | /* offline/parser port */ | |
294 | for (i = 0; i < MAX_NUM_OH_PORT; i++) { | |
295 | port_id = OH_PORT_ID_BASE + i - 1; | |
296 | /* max tasks=1, max dma=1, no extra */ | |
297 | out_be32(&bmi->fmbm_pp[port_id], 0); | |
298 | /* port FIFO size - 256 bytes, no extra */ | |
299 | out_be32(&bmi->fmbm_pfs[port_id], 0); | |
300 | } | |
301 | /* Rx 1G port */ | |
302 | for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { | |
303 | port_id = RX_PORT_1G_BASE + i - 1; | |
304 | /* max tasks=4, max dma=1, no extra */ | |
305 | out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); | |
306 | /* FIFO size - 4KB, no extra */ | |
307 | out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); | |
308 | } | |
309 | /* Tx 1G port FIFO size - 4KB, no extra */ | |
310 | for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { | |
311 | port_id = TX_PORT_1G_BASE + i - 1; | |
312 | /* max tasks=4, max dma=1, no extra */ | |
313 | out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); | |
314 | /* FIFO size - 4KB, no extra */ | |
315 | out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); | |
316 | } | |
317 | /* Rx 10G port */ | |
318 | port_id = RX_PORT_10G_BASE - 1; | |
319 | /* max tasks=12, max dma=3, no extra */ | |
320 | out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); | |
321 | /* FIFO size - 4KB, no extra */ | |
322 | out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); | |
323 | ||
324 | /* Tx 10G port */ | |
325 | port_id = TX_PORT_10G_BASE - 1; | |
326 | /* max tasks=12, max dma=3, no extra */ | |
327 | out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); | |
328 | /* FIFO size - 4KB, no extra */ | |
329 | out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); | |
330 | ||
331 | /* initialize internal buffers data base (linked list) */ | |
332 | out_be32(&bmi->fmbm_init, FMBM_INIT_START); | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | static void fm_init_qmi(struct fm_qmi_common *qmi) | |
338 | { | |
339 | /* disable enqueue and dequeue of QMI */ | |
340 | clrbits_be32(&qmi->fmqm_gc, FMQM_GC_ENQ_EN | FMQM_GC_DEQ_EN); | |
341 | ||
342 | /* disable all error interrupts */ | |
343 | out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL); | |
344 | /* clear all error events */ | |
345 | out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL); | |
346 | ||
347 | /* disable all interrupts */ | |
348 | out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL); | |
349 | /* clear all interrupts */ | |
350 | out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL); | |
351 | } | |
352 | ||
353 | /* Init common part of FM, index is fm num# like fm as above */ | |
354 | int fm_init_common(int index, struct ccsr_fman *reg) | |
355 | { | |
356 | int rc; | |
f2717b47 | 357 | #if defined(CONFIG_SYS_QE_FMAN_FW_IN_NOR) |
dcf1d774 | 358 | void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR; |
f2717b47 TT |
359 | #elif defined(CONFIG_SYS_QE_FMAN_FW_IN_NAND) |
360 | size_t fw_length = CONFIG_SYS_QE_FMAN_FW_LENGTH; | |
361 | void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); | |
c916d7c9 | 362 | |
dcf1d774 | 363 | rc = nand_read(&nand_info[0], (loff_t)CONFIG_SYS_FMAN_FW_ADDR, |
c916d7c9 KG |
364 | &fw_length, (u_char *)addr); |
365 | if (rc == -EUCLEAN) { | |
366 | printf("NAND read of FMAN firmware at offset 0x%x failed %d\n", | |
dcf1d774 | 367 | CONFIG_SYS_FMAN_FW_ADDR, rc); |
c916d7c9 KG |
368 | } |
369 | #elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) | |
370 | struct spi_flash *ucode_flash; | |
f2717b47 | 371 | void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); |
c916d7c9 KG |
372 | int ret = 0; |
373 | ||
374 | ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, | |
375 | CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); | |
376 | if (!ucode_flash) | |
377 | printf("SF: probe for ucode failed\n"); | |
378 | else { | |
dcf1d774 | 379 | ret = spi_flash_read(ucode_flash, CONFIG_SYS_FMAN_FW_ADDR, |
f2717b47 | 380 | CONFIG_SYS_QE_FMAN_FW_LENGTH, addr); |
c916d7c9 KG |
381 | if (ret) |
382 | printf("SF: read for ucode failed\n"); | |
383 | spi_flash_free(ucode_flash); | |
384 | } | |
f2717b47 | 385 | #elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC) |
c916d7c9 | 386 | int dev = CONFIG_SYS_MMC_ENV_DEV; |
f2717b47 TT |
387 | void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH); |
388 | u32 cnt = CONFIG_SYS_QE_FMAN_FW_LENGTH / 512; | |
dcf1d774 | 389 | u32 blk = CONFIG_SYS_FMAN_FW_ADDR / 512; |
c916d7c9 KG |
390 | struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); |
391 | ||
392 | if (!mmc) | |
393 | printf("\nMMC cannot find device for ucode\n"); | |
394 | else { | |
395 | printf("\nMMC read: dev # %u, block # %u, count %u ...\n", | |
396 | dev, blk, cnt); | |
397 | mmc_init(mmc); | |
9ccfed20 | 398 | (void)mmc->block_dev.block_read(dev, blk, cnt, addr); |
c916d7c9 KG |
399 | /* flush cache after read */ |
400 | flush_cache((ulong)addr, cnt * 512); | |
401 | } | |
292dc6c5 | 402 | #elif defined(CONFIG_SYS_QE_FMAN_FW_IN_REMOTE) |
dcf1d774 | 403 | void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR; |
7adefb55 YS |
404 | #else |
405 | void *addr = NULL; | |
c916d7c9 KG |
406 | #endif |
407 | ||
408 | /* Upload the Fman microcode if it's present */ | |
409 | rc = fman_upload_firmware(index, ®->fm_imem, addr); | |
410 | if (rc) | |
411 | return rc; | |
978226da | 412 | setenv_addr("fman_ucode", addr); |
c916d7c9 KG |
413 | |
414 | fm_init_muram(index, ®->muram); | |
415 | fm_init_qmi(®->fm_qmi_common); | |
416 | fm_init_fpm(®->fm_fpm); | |
417 | ||
418 | /* clear DMA status */ | |
419 | setbits_be32(®->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL); | |
420 | ||
421 | /* set DMA mode */ | |
422 | setbits_be32(®->fm_dma.fmdmmr, FMDMMR_SBER); | |
423 | ||
424 | return fm_init_bmi(index, ®->fm_bmi_common); | |
425 | } |