xref: /rk3399_rockchip-uboot/drivers/qe/qe.c (revision 38d67a4e552ac991f21c2d3e442a38fb0098fda6)
17737d5c6SDave Liu /*
24e7b25e4SHaiying Wang  * Copyright (C) 2006-2009 Freescale Semiconductor, Inc.
37737d5c6SDave Liu  *
47737d5c6SDave Liu  * Dave Liu <daveliu@freescale.com>
57737d5c6SDave Liu  * based on source code of Shlomi Gridish
67737d5c6SDave Liu  *
71a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
87737d5c6SDave Liu  */
97737d5c6SDave Liu 
107737d5c6SDave Liu #include "common.h"
11b8ec2385STimur Tabi #include <command.h>
127737d5c6SDave Liu #include "asm/errno.h"
137737d5c6SDave Liu #include "asm/io.h"
14*38d67a4eSZhao Qiang #include "linux/immap_qe.h"
157737d5c6SDave Liu #include "qe.h"
167737d5c6SDave Liu 
17ca721fb2SZhao Qiang #define MPC85xx_DEVDISR_QE_DISABLE	0x1
18ca721fb2SZhao Qiang 
197737d5c6SDave Liu qe_map_t		*qe_immr = NULL;
207737d5c6SDave Liu static qe_snum_t	snums[QE_NUM_OF_SNUM];
217737d5c6SDave Liu 
221218abf1SWolfgang Denk DECLARE_GLOBAL_DATA_PTR;
231218abf1SWolfgang Denk 
247737d5c6SDave Liu void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data)
257737d5c6SDave Liu {
267737d5c6SDave Liu 	u32 cecr;
277737d5c6SDave Liu 
287737d5c6SDave Liu 	if (cmd == QE_RESET) {
297737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecr,(u32) (cmd | QE_CR_FLG));
307737d5c6SDave Liu 	} else {
317737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecdr, cmd_data);
327737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecr, (sbc | QE_CR_FLG |
337737d5c6SDave Liu 			 ((u32) mcn<<QE_CR_PROTOCOL_SHIFT) | cmd));
347737d5c6SDave Liu 	}
357737d5c6SDave Liu 	/* Wait for the QE_CR_FLG to clear */
367737d5c6SDave Liu 	do {
377737d5c6SDave Liu 		cecr = in_be32(&qe_immr->cp.cecr);
387737d5c6SDave Liu 	} while (cecr & QE_CR_FLG);
397737d5c6SDave Liu 
407737d5c6SDave Liu 	return;
417737d5c6SDave Liu }
427737d5c6SDave Liu 
437737d5c6SDave Liu uint qe_muram_alloc(uint size, uint align)
447737d5c6SDave Liu {
457737d5c6SDave Liu 	uint	retloc;
467737d5c6SDave Liu 	uint	align_mask, off;
477737d5c6SDave Liu 	uint	savebase;
487737d5c6SDave Liu 
497737d5c6SDave Liu 	align_mask = align - 1;
5045bae2e3SSimon Glass 	savebase = gd->arch.mp_alloc_base;
517737d5c6SDave Liu 
5245bae2e3SSimon Glass 	off = gd->arch.mp_alloc_base & align_mask;
5345bae2e3SSimon Glass 	if (off != 0)
5445bae2e3SSimon Glass 		gd->arch.mp_alloc_base += (align - off);
557737d5c6SDave Liu 
567737d5c6SDave Liu 	if ((off = size & align_mask) != 0)
577737d5c6SDave Liu 		size += (align - off);
587737d5c6SDave Liu 
5945bae2e3SSimon Glass 	if ((gd->arch.mp_alloc_base + size) >= gd->arch.mp_alloc_top) {
6045bae2e3SSimon Glass 		gd->arch.mp_alloc_base = savebase;
617737d5c6SDave Liu 		printf("%s: ran out of ram.\n",  __FUNCTION__);
627737d5c6SDave Liu 	}
637737d5c6SDave Liu 
6445bae2e3SSimon Glass 	retloc = gd->arch.mp_alloc_base;
6545bae2e3SSimon Glass 	gd->arch.mp_alloc_base += size;
667737d5c6SDave Liu 
677737d5c6SDave Liu 	memset((void *)&qe_immr->muram[retloc], 0, size);
687737d5c6SDave Liu 
697737d5c6SDave Liu 	__asm__ __volatile__("sync");
707737d5c6SDave Liu 
717737d5c6SDave Liu 	return retloc;
727737d5c6SDave Liu }
737737d5c6SDave Liu 
747737d5c6SDave Liu void *qe_muram_addr(uint offset)
757737d5c6SDave Liu {
767737d5c6SDave Liu 	return (void *)&qe_immr->muram[offset];
777737d5c6SDave Liu }
787737d5c6SDave Liu 
797737d5c6SDave Liu static void qe_sdma_init(void)
807737d5c6SDave Liu {
817737d5c6SDave Liu 	volatile sdma_t	*p;
827737d5c6SDave Liu 	uint		sdma_buffer_base;
837737d5c6SDave Liu 
847737d5c6SDave Liu 	p = (volatile sdma_t *)&qe_immr->sdma;
857737d5c6SDave Liu 
867737d5c6SDave Liu 	/* All of DMA transaction in bus 1 */
877737d5c6SDave Liu 	out_be32(&p->sdaqr, 0);
887737d5c6SDave Liu 	out_be32(&p->sdaqmr, 0);
897737d5c6SDave Liu 
907737d5c6SDave Liu 	/* Allocate 2KB temporary buffer for sdma */
91ff9658d7SDave Liu 	sdma_buffer_base = qe_muram_alloc(2048, 4096);
927737d5c6SDave Liu 	out_be32(&p->sdwbcr, sdma_buffer_base & QE_SDEBCR_BA_MASK);
937737d5c6SDave Liu 
947737d5c6SDave Liu 	/* Clear sdma status */
957737d5c6SDave Liu 	out_be32(&p->sdsr, 0x03000000);
967737d5c6SDave Liu 
977737d5c6SDave Liu 	/* Enable global mode on bus 1, and 2KB buffer size */
987737d5c6SDave Liu 	out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT));
997737d5c6SDave Liu }
1007737d5c6SDave Liu 
1014e7b25e4SHaiying Wang /* This table is a list of the serial numbers of the Threads, taken from the
1024e7b25e4SHaiying Wang  * "SNUM Table" chart in the QE Reference Manual. The order is not important,
1034e7b25e4SHaiying Wang  * we just need to know what the SNUMs are for the threads.
1044e7b25e4SHaiying Wang  */
1054e7b25e4SHaiying Wang static u8 thread_snum[] = {
106a88731a6SGerlando Falauto /* Evthreads 16-29 are not supported in MPC8309 */
107a88731a6SGerlando Falauto #if !defined(CONFIG_MPC8309)
1087737d5c6SDave Liu 	0x04, 0x05, 0x0c, 0x0d,
1097737d5c6SDave Liu 	0x14, 0x15, 0x1c, 0x1d,
1107737d5c6SDave Liu 	0x24, 0x25, 0x2c, 0x2d,
111a88731a6SGerlando Falauto 	0x34, 0x35,
112a88731a6SGerlando Falauto #endif
113a88731a6SGerlando Falauto 	0x88, 0x89, 0x98, 0x99,
114a88731a6SGerlando Falauto 	0xa8, 0xa9, 0xb8, 0xb9,
115a88731a6SGerlando Falauto 	0xc8, 0xc9, 0xd8, 0xd9,
116a88731a6SGerlando Falauto 	0xe8, 0xe9, 0x08, 0x09,
117a88731a6SGerlando Falauto 	0x18, 0x19, 0x28, 0x29,
118a88731a6SGerlando Falauto 	0x38, 0x39, 0x48, 0x49,
119a88731a6SGerlando Falauto 	0x58, 0x59, 0x68, 0x69,
120a88731a6SGerlando Falauto 	0x78, 0x79, 0x80, 0x81
1217737d5c6SDave Liu };
1227737d5c6SDave Liu 
1237737d5c6SDave Liu static void qe_snums_init(void)
1247737d5c6SDave Liu {
1257737d5c6SDave Liu 	int	i;
1267737d5c6SDave Liu 
1277737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1287737d5c6SDave Liu 		snums[i].state = QE_SNUM_STATE_FREE;
1297737d5c6SDave Liu 		snums[i].num   = thread_snum[i];
1307737d5c6SDave Liu 	}
1317737d5c6SDave Liu }
1327737d5c6SDave Liu 
1337737d5c6SDave Liu int qe_get_snum(void)
1347737d5c6SDave Liu {
1357737d5c6SDave Liu 	int	snum = -EBUSY;
1367737d5c6SDave Liu 	int	i;
1377737d5c6SDave Liu 
1387737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1397737d5c6SDave Liu 		if (snums[i].state == QE_SNUM_STATE_FREE) {
1407737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_USED;
1417737d5c6SDave Liu 			snum = snums[i].num;
1427737d5c6SDave Liu 			break;
1437737d5c6SDave Liu 		}
1447737d5c6SDave Liu 	}
1457737d5c6SDave Liu 
1467737d5c6SDave Liu 	return snum;
1477737d5c6SDave Liu }
1487737d5c6SDave Liu 
1497737d5c6SDave Liu void qe_put_snum(u8 snum)
1507737d5c6SDave Liu {
1517737d5c6SDave Liu 	int	i;
1527737d5c6SDave Liu 
1537737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1547737d5c6SDave Liu 		if (snums[i].num == snum) {
1557737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_FREE;
1567737d5c6SDave Liu 			break;
1577737d5c6SDave Liu 		}
1587737d5c6SDave Liu 	}
1597737d5c6SDave Liu }
1607737d5c6SDave Liu 
1617737d5c6SDave Liu void qe_init(uint qe_base)
1627737d5c6SDave Liu {
1637737d5c6SDave Liu 	/* Init the QE IMMR base */
1647737d5c6SDave Liu 	qe_immr = (qe_map_t *)qe_base;
1657737d5c6SDave Liu 
166f2717b47STimur Tabi #ifdef CONFIG_SYS_QE_FMAN_FW_IN_NOR
167c0a14aedSWolfgang Denk 	/*
168c0a14aedSWolfgang Denk 	 * Upload microcode to IRAM for those SOCs which do not have ROM in QE.
1692d4de6aeSHaiying Wang 	 */
170dcf1d774SZhao Qiang 	qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR);
1712d4de6aeSHaiying Wang 
1722d4de6aeSHaiying Wang 	/* enable the microcode in IRAM */
1732d4de6aeSHaiying Wang 	out_be32(&qe_immr->iram.iready,QE_IRAM_READY);
1742d4de6aeSHaiying Wang #endif
1752d4de6aeSHaiying Wang 
17645bae2e3SSimon Glass 	gd->arch.mp_alloc_base = QE_DATAONLY_BASE;
17745bae2e3SSimon Glass 	gd->arch.mp_alloc_top = gd->arch.mp_alloc_base + QE_DATAONLY_SIZE;
1787737d5c6SDave Liu 
1797737d5c6SDave Liu 	qe_sdma_init();
1807737d5c6SDave Liu 	qe_snums_init();
1817737d5c6SDave Liu }
1827737d5c6SDave Liu 
1837737d5c6SDave Liu void qe_reset(void)
1847737d5c6SDave Liu {
1857737d5c6SDave Liu 	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
1867737d5c6SDave Liu 			 (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
1877737d5c6SDave Liu }
1887737d5c6SDave Liu 
1897737d5c6SDave Liu void qe_assign_page(uint snum, uint para_ram_base)
1907737d5c6SDave Liu {
1917737d5c6SDave Liu 	u32	cecr;
1927737d5c6SDave Liu 
1937737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecdr, para_ram_base);
1947737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecr, ((u32) snum<<QE_CR_ASSIGN_PAGE_SNUM_SHIFT)
1957737d5c6SDave Liu 					 | QE_CR_FLG | QE_ASSIGN_PAGE);
1967737d5c6SDave Liu 
1977737d5c6SDave Liu 	/* Wait for the QE_CR_FLG to clear */
1987737d5c6SDave Liu 	do {
1997737d5c6SDave Liu 		cecr = in_be32(&qe_immr->cp.cecr);
2007737d5c6SDave Liu 	} while (cecr & QE_CR_FLG );
2017737d5c6SDave Liu 
2027737d5c6SDave Liu 	return;
2037737d5c6SDave Liu }
2047737d5c6SDave Liu 
2057737d5c6SDave Liu /*
2067737d5c6SDave Liu  * brg: 0~15 as BRG1~BRG16
2077737d5c6SDave Liu    rate: baud rate
2087737d5c6SDave Liu  * BRG input clock comes from the BRGCLK (internal clock generated from
2097737d5c6SDave Liu    the QE clock, it is one-half of the QE clock), If need the clock source
2107737d5c6SDave Liu    from CLKn pin, we have te change the function.
2117737d5c6SDave Liu  */
2127737d5c6SDave Liu 
2131206c184SSimon Glass #define BRG_CLK		(gd->arch.brg_clk)
2147737d5c6SDave Liu 
2157737d5c6SDave Liu int qe_set_brg(uint brg, uint rate)
2167737d5c6SDave Liu {
2177737d5c6SDave Liu 	volatile uint	*bp;
2187737d5c6SDave Liu 	u32		divisor;
2197737d5c6SDave Liu 	int		div16 = 0;
2207737d5c6SDave Liu 
2217737d5c6SDave Liu 	if (brg >= QE_NUM_OF_BRGS)
2227737d5c6SDave Liu 		return -EINVAL;
2237737d5c6SDave Liu 	bp = (uint *)&qe_immr->brg.brgc1;
2247737d5c6SDave Liu 	bp += brg;
2257737d5c6SDave Liu 
2267737d5c6SDave Liu 	divisor = (BRG_CLK / rate);
2277737d5c6SDave Liu 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
2287737d5c6SDave Liu 		div16 = 1;
2297737d5c6SDave Liu 		divisor /= 16;
2307737d5c6SDave Liu 	}
2317737d5c6SDave Liu 
2327737d5c6SDave Liu 	*bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
2337737d5c6SDave Liu 	__asm__ __volatile__("sync");
2347737d5c6SDave Liu 
2357737d5c6SDave Liu 	if (div16) {
2367737d5c6SDave Liu 		*bp |= QE_BRGC_DIV16;
2377737d5c6SDave Liu 		__asm__ __volatile__("sync");
2387737d5c6SDave Liu 	}
2397737d5c6SDave Liu 
2407737d5c6SDave Liu 	return 0;
2417737d5c6SDave Liu }
2427737d5c6SDave Liu 
2437737d5c6SDave Liu /* Set ethernet MII clock master
2447737d5c6SDave Liu */
2457737d5c6SDave Liu int qe_set_mii_clk_src(int ucc_num)
2467737d5c6SDave Liu {
2477737d5c6SDave Liu 	u32	cmxgcr;
2487737d5c6SDave Liu 
2497737d5c6SDave Liu 	/* check if the UCC number is in range. */
2507737d5c6SDave Liu 	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) {
2517737d5c6SDave Liu 		printf("%s: ucc num not in ranges\n", __FUNCTION__);
2527737d5c6SDave Liu 		return -EINVAL;
2537737d5c6SDave Liu 	}
2547737d5c6SDave Liu 
2557737d5c6SDave Liu 	cmxgcr = in_be32(&qe_immr->qmx.cmxgcr);
2567737d5c6SDave Liu 	cmxgcr &= ~QE_CMXGCR_MII_ENET_MNG_MASK;
2577737d5c6SDave Liu 	cmxgcr |= (ucc_num <<QE_CMXGCR_MII_ENET_MNG_SHIFT);
2587737d5c6SDave Liu 	out_be32(&qe_immr->qmx.cmxgcr, cmxgcr);
2597737d5c6SDave Liu 
2607737d5c6SDave Liu 	return 0;
2617737d5c6SDave Liu }
2627737d5c6SDave Liu 
263b8ec2385STimur Tabi /* Firmware information stored here for qe_get_firmware_info() */
264b8ec2385STimur Tabi static struct qe_firmware_info qe_firmware_info;
265b8ec2385STimur Tabi 
266b8ec2385STimur Tabi /*
267b8ec2385STimur Tabi  * Set to 1 if QE firmware has been uploaded, and therefore
268b8ec2385STimur Tabi  * qe_firmware_info contains valid data.
269b8ec2385STimur Tabi  */
270b8ec2385STimur Tabi static int qe_firmware_uploaded;
271b8ec2385STimur Tabi 
272b8ec2385STimur Tabi /*
273b8ec2385STimur Tabi  * Upload a QE microcode
274b8ec2385STimur Tabi  *
275b8ec2385STimur Tabi  * This function is a worker function for qe_upload_firmware().  It does
276b8ec2385STimur Tabi  * the actual uploading of the microcode.
277b8ec2385STimur Tabi  */
278b8ec2385STimur Tabi static void qe_upload_microcode(const void *base,
279b8ec2385STimur Tabi 	const struct qe_microcode *ucode)
280b8ec2385STimur Tabi {
281b8ec2385STimur Tabi 	const u32 *code = base + be32_to_cpu(ucode->code_offset);
282b8ec2385STimur Tabi 	unsigned int i;
283b8ec2385STimur Tabi 
284b8ec2385STimur Tabi 	if (ucode->major || ucode->minor || ucode->revision)
285b8ec2385STimur Tabi 		printf("QE: uploading microcode '%s' version %u.%u.%u\n",
286b8ec2385STimur Tabi 			ucode->id, ucode->major, ucode->minor, ucode->revision);
287b8ec2385STimur Tabi 	else
288b8ec2385STimur Tabi 		printf("QE: uploading microcode '%s'\n", ucode->id);
289b8ec2385STimur Tabi 
290b8ec2385STimur Tabi 	/* Use auto-increment */
291b8ec2385STimur Tabi 	out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
292b8ec2385STimur Tabi 		QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
293b8ec2385STimur Tabi 
294b8ec2385STimur Tabi 	for (i = 0; i < be32_to_cpu(ucode->count); i++)
295b8ec2385STimur Tabi 		out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
296b8ec2385STimur Tabi }
297b8ec2385STimur Tabi 
298b8ec2385STimur Tabi /*
299b8ec2385STimur Tabi  * Upload a microcode to the I-RAM at a specific address.
300b8ec2385STimur Tabi  *
301b8ec2385STimur Tabi  * See docs/README.qe_firmware for information on QE microcode uploading.
302b8ec2385STimur Tabi  *
303b8ec2385STimur Tabi  * Currently, only version 1 is supported, so the 'version' field must be
304b8ec2385STimur Tabi  * set to 1.
305b8ec2385STimur Tabi  *
306b8ec2385STimur Tabi  * The SOC model and revision are not validated, they are only displayed for
307b8ec2385STimur Tabi  * informational purposes.
308b8ec2385STimur Tabi  *
309b8ec2385STimur Tabi  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
310b8ec2385STimur Tabi  * all of the microcode structures, minus the CRC.
311b8ec2385STimur Tabi  *
312b8ec2385STimur Tabi  * 'length' is the size that the structure says it is, including the CRC.
313b8ec2385STimur Tabi  */
314b8ec2385STimur Tabi int qe_upload_firmware(const struct qe_firmware *firmware)
315b8ec2385STimur Tabi {
316b8ec2385STimur Tabi 	unsigned int i;
317b8ec2385STimur Tabi 	unsigned int j;
318b8ec2385STimur Tabi 	u32 crc;
319b8ec2385STimur Tabi 	size_t calc_size = sizeof(struct qe_firmware);
320b8ec2385STimur Tabi 	size_t length;
321b8ec2385STimur Tabi 	const struct qe_header *hdr;
322ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
323ca721fb2SZhao Qiang 	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
324ca721fb2SZhao Qiang #endif
325b8ec2385STimur Tabi 	if (!firmware) {
326b8ec2385STimur Tabi 		printf("Invalid address\n");
327b8ec2385STimur Tabi 		return -EINVAL;
328b8ec2385STimur Tabi 	}
329b8ec2385STimur Tabi 
330b8ec2385STimur Tabi 	hdr = &firmware->header;
331b8ec2385STimur Tabi 	length = be32_to_cpu(hdr->length);
332b8ec2385STimur Tabi 
333b8ec2385STimur Tabi 	/* Check the magic */
334b8ec2385STimur Tabi 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
335b8ec2385STimur Tabi 	    (hdr->magic[2] != 'F')) {
336b8ec2385STimur Tabi 		printf("Not a microcode\n");
337ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
338ca721fb2SZhao Qiang 		setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE);
339ca721fb2SZhao Qiang #endif
340b8ec2385STimur Tabi 		return -EPERM;
341b8ec2385STimur Tabi 	}
342b8ec2385STimur Tabi 
343b8ec2385STimur Tabi 	/* Check the version */
344b8ec2385STimur Tabi 	if (hdr->version != 1) {
345b8ec2385STimur Tabi 		printf("Unsupported version\n");
346b8ec2385STimur Tabi 		return -EPERM;
347b8ec2385STimur Tabi 	}
348b8ec2385STimur Tabi 
349b8ec2385STimur Tabi 	/* Validate some of the fields */
350491fb6deSTimur Tabi 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
351b8ec2385STimur Tabi 		printf("Invalid data\n");
352b8ec2385STimur Tabi 		return -EINVAL;
353b8ec2385STimur Tabi 	}
354b8ec2385STimur Tabi 
355b8ec2385STimur Tabi 	/* Validate the length and check if there's a CRC */
356b8ec2385STimur Tabi 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
357b8ec2385STimur Tabi 
358b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++)
359b8ec2385STimur Tabi 		/*
360b8ec2385STimur Tabi 		 * For situations where the second RISC uses the same microcode
361b8ec2385STimur Tabi 		 * as the first, the 'code_offset' and 'count' fields will be
362b8ec2385STimur Tabi 		 * zero, so it's okay to add those.
363b8ec2385STimur Tabi 		 */
364b8ec2385STimur Tabi 		calc_size += sizeof(u32) *
365b8ec2385STimur Tabi 			be32_to_cpu(firmware->microcode[i].count);
366b8ec2385STimur Tabi 
367b8ec2385STimur Tabi 	/* Validate the length */
368b8ec2385STimur Tabi 	if (length != calc_size + sizeof(u32)) {
369b8ec2385STimur Tabi 		printf("Invalid length\n");
370b8ec2385STimur Tabi 		return -EPERM;
371b8ec2385STimur Tabi 	}
372b8ec2385STimur Tabi 
373b8ec2385STimur Tabi 	/*
374b8ec2385STimur Tabi 	 * Validate the CRC.  We would normally call crc32_no_comp(), but that
375b8ec2385STimur Tabi 	 * function isn't available unless you turn on JFFS support.
376b8ec2385STimur Tabi 	 */
377b8ec2385STimur Tabi 	crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
378b8ec2385STimur Tabi 	if (crc != (crc32(-1, (const void *) firmware, calc_size) ^ -1)) {
379b8ec2385STimur Tabi 		printf("Firmware CRC is invalid\n");
380b8ec2385STimur Tabi 		return -EIO;
381b8ec2385STimur Tabi 	}
382b8ec2385STimur Tabi 
383b8ec2385STimur Tabi 	/*
384b8ec2385STimur Tabi 	 * If the microcode calls for it, split the I-RAM.
385b8ec2385STimur Tabi 	 */
386b8ec2385STimur Tabi 	if (!firmware->split) {
387b8ec2385STimur Tabi 		out_be16(&qe_immr->cp.cercr,
388b8ec2385STimur Tabi 			in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR);
389b8ec2385STimur Tabi 	}
390b8ec2385STimur Tabi 
391b8ec2385STimur Tabi 	if (firmware->soc.model)
392b8ec2385STimur Tabi 		printf("Firmware '%s' for %u V%u.%u\n",
393b8ec2385STimur Tabi 			firmware->id, be16_to_cpu(firmware->soc.model),
394b8ec2385STimur Tabi 			firmware->soc.major, firmware->soc.minor);
395b8ec2385STimur Tabi 	else
396b8ec2385STimur Tabi 		printf("Firmware '%s'\n", firmware->id);
397b8ec2385STimur Tabi 
398b8ec2385STimur Tabi 	/*
399b8ec2385STimur Tabi 	 * The QE only supports one microcode per RISC, so clear out all the
400b8ec2385STimur Tabi 	 * saved microcode information and put in the new.
401b8ec2385STimur Tabi 	 */
402b8ec2385STimur Tabi 	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
40306c428bcSDave Liu 	strcpy(qe_firmware_info.id, (char *)firmware->id);
404b8ec2385STimur Tabi 	qe_firmware_info.extended_modes = firmware->extended_modes;
405b8ec2385STimur Tabi 	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
406b8ec2385STimur Tabi 		sizeof(firmware->vtraps));
407b8ec2385STimur Tabi 	qe_firmware_uploaded = 1;
408b8ec2385STimur Tabi 
409b8ec2385STimur Tabi 	/* Loop through each microcode. */
410b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++) {
411b8ec2385STimur Tabi 		const struct qe_microcode *ucode = &firmware->microcode[i];
412b8ec2385STimur Tabi 
413b8ec2385STimur Tabi 		/* Upload a microcode if it's present */
414b8ec2385STimur Tabi 		if (ucode->code_offset)
415b8ec2385STimur Tabi 			qe_upload_microcode(firmware, ucode);
416b8ec2385STimur Tabi 
417b8ec2385STimur Tabi 		/* Program the traps for this processor */
418b8ec2385STimur Tabi 		for (j = 0; j < 16; j++) {
419b8ec2385STimur Tabi 			u32 trap = be32_to_cpu(ucode->traps[j]);
420b8ec2385STimur Tabi 
421b8ec2385STimur Tabi 			if (trap)
422b8ec2385STimur Tabi 				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
423b8ec2385STimur Tabi 		}
424b8ec2385STimur Tabi 
425b8ec2385STimur Tabi 		/* Enable traps */
426b8ec2385STimur Tabi 		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
427b8ec2385STimur Tabi 	}
428b8ec2385STimur Tabi 
429b8ec2385STimur Tabi 	return 0;
430b8ec2385STimur Tabi }
431b8ec2385STimur Tabi 
432b8ec2385STimur Tabi struct qe_firmware_info *qe_get_firmware_info(void)
433b8ec2385STimur Tabi {
434b8ec2385STimur Tabi 	return qe_firmware_uploaded ? &qe_firmware_info : NULL;
435b8ec2385STimur Tabi }
436b8ec2385STimur Tabi 
43754841ab5SWolfgang Denk static int qe_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
438b8ec2385STimur Tabi {
439b8ec2385STimur Tabi 	ulong addr;
440b8ec2385STimur Tabi 
44147e26b1bSWolfgang Denk 	if (argc < 3)
44247e26b1bSWolfgang Denk 		return cmd_usage(cmdtp);
443b8ec2385STimur Tabi 
444b8ec2385STimur Tabi 	if (strcmp(argv[1], "fw") == 0) {
445b8ec2385STimur Tabi 		addr = simple_strtoul(argv[2], NULL, 16);
446b8ec2385STimur Tabi 
447b8ec2385STimur Tabi 		if (!addr) {
448b8ec2385STimur Tabi 			printf("Invalid address\n");
449b8ec2385STimur Tabi 			return -EINVAL;
450b8ec2385STimur Tabi 		}
451b8ec2385STimur Tabi 
452b8ec2385STimur Tabi 		/*
453b8ec2385STimur Tabi 		 * If a length was supplied, compare that with the 'length'
454b8ec2385STimur Tabi 		 * field.
455b8ec2385STimur Tabi 		 */
456b8ec2385STimur Tabi 
457b8ec2385STimur Tabi 		if (argc > 3) {
458b8ec2385STimur Tabi 			ulong length = simple_strtoul(argv[3], NULL, 16);
459b8ec2385STimur Tabi 			struct qe_firmware *firmware = (void *) addr;
460b8ec2385STimur Tabi 
461b8ec2385STimur Tabi 			if (length != be32_to_cpu(firmware->header.length)) {
462b8ec2385STimur Tabi 				printf("Length mismatch\n");
463b8ec2385STimur Tabi 				return -EINVAL;
464b8ec2385STimur Tabi 			}
465b8ec2385STimur Tabi 		}
466b8ec2385STimur Tabi 
467b8ec2385STimur Tabi 		return qe_upload_firmware((const struct qe_firmware *) addr);
468b8ec2385STimur Tabi 	}
469b8ec2385STimur Tabi 
47047e26b1bSWolfgang Denk 	return cmd_usage(cmdtp);
471b8ec2385STimur Tabi }
472b8ec2385STimur Tabi 
473b8ec2385STimur Tabi U_BOOT_CMD(
474b8ec2385STimur Tabi 	qe, 4, 0, qe_cmd,
4752fb2604dSPeter Tyser 	"QUICC Engine commands",
476b8ec2385STimur Tabi 	"fw <addr> [<length>] - Upload firmware binary at address <addr> to "
477a89c33dbSWolfgang Denk 		"the QE,\n"
478a89c33dbSWolfgang Denk 	"\twith optional length <length> verification."
479b8ec2385STimur Tabi );
480