xref: /rk3399_rockchip-uboot/drivers/qe/qe.c (revision 010034028fef0bf601f7e7474b516888e066cac3)
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 
10b5bf5cb3SMasahiro Yamada #include <common.h>
11*5aa03dddSZhao Qiang #include <malloc.h>
12b8ec2385STimur Tabi #include <command.h>
131221ce45SMasahiro Yamada #include <linux/errno.h>
14b5bf5cb3SMasahiro Yamada #include <asm/io.h>
15b5bf5cb3SMasahiro Yamada #include <linux/immap_qe.h>
162459afb1SQianyu Gong #include <fsl_qe.h>
1773fb5838SYork Sun #ifdef CONFIG_ARCH_LS1021A
189c7c86f4SZhao Qiang #include <asm/arch/immap_ls102xa.h>
199c7c86f4SZhao Qiang #endif
207737d5c6SDave Liu 
21*5aa03dddSZhao Qiang #ifdef CONFIG_SYS_QE_FMAN_FW_IN_MMC
22*5aa03dddSZhao Qiang #include <mmc.h>
23*5aa03dddSZhao Qiang #endif
24*5aa03dddSZhao Qiang 
25ca721fb2SZhao Qiang #define MPC85xx_DEVDISR_QE_DISABLE	0x1
26ca721fb2SZhao Qiang 
277737d5c6SDave Liu qe_map_t		*qe_immr = NULL;
283bf46e6aSZhao Qiang #ifdef CONFIG_QE
297737d5c6SDave Liu static qe_snum_t	snums[QE_NUM_OF_SNUM];
303bf46e6aSZhao Qiang #endif
317737d5c6SDave Liu 
321218abf1SWolfgang Denk DECLARE_GLOBAL_DATA_PTR;
331218abf1SWolfgang Denk 
qe_issue_cmd(uint cmd,uint sbc,u8 mcn,u32 cmd_data)347737d5c6SDave Liu void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data)
357737d5c6SDave Liu {
367737d5c6SDave Liu 	u32 cecr;
377737d5c6SDave Liu 
387737d5c6SDave Liu 	if (cmd == QE_RESET) {
397737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecr,(u32) (cmd | QE_CR_FLG));
407737d5c6SDave Liu 	} else {
417737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecdr, cmd_data);
427737d5c6SDave Liu 		out_be32(&qe_immr->cp.cecr, (sbc | QE_CR_FLG |
437737d5c6SDave Liu 			 ((u32) mcn<<QE_CR_PROTOCOL_SHIFT) | cmd));
447737d5c6SDave Liu 	}
457737d5c6SDave Liu 	/* Wait for the QE_CR_FLG to clear */
467737d5c6SDave Liu 	do {
477737d5c6SDave Liu 		cecr = in_be32(&qe_immr->cp.cecr);
487737d5c6SDave Liu 	} while (cecr & QE_CR_FLG);
497737d5c6SDave Liu 
507737d5c6SDave Liu 	return;
517737d5c6SDave Liu }
527737d5c6SDave Liu 
5393d33204SZhao Qiang #ifdef CONFIG_QE
qe_muram_alloc(uint size,uint align)547737d5c6SDave Liu uint qe_muram_alloc(uint size, uint align)
557737d5c6SDave Liu {
567737d5c6SDave Liu 	uint	retloc;
577737d5c6SDave Liu 	uint	align_mask, off;
587737d5c6SDave Liu 	uint	savebase;
597737d5c6SDave Liu 
607737d5c6SDave Liu 	align_mask = align - 1;
6145bae2e3SSimon Glass 	savebase = gd->arch.mp_alloc_base;
627737d5c6SDave Liu 
6345bae2e3SSimon Glass 	off = gd->arch.mp_alloc_base & align_mask;
6445bae2e3SSimon Glass 	if (off != 0)
6545bae2e3SSimon Glass 		gd->arch.mp_alloc_base += (align - off);
667737d5c6SDave Liu 
677737d5c6SDave Liu 	if ((off = size & align_mask) != 0)
687737d5c6SDave Liu 		size += (align - off);
697737d5c6SDave Liu 
7045bae2e3SSimon Glass 	if ((gd->arch.mp_alloc_base + size) >= gd->arch.mp_alloc_top) {
7145bae2e3SSimon Glass 		gd->arch.mp_alloc_base = savebase;
727737d5c6SDave Liu 		printf("%s: ran out of ram.\n",  __FUNCTION__);
737737d5c6SDave Liu 	}
747737d5c6SDave Liu 
7545bae2e3SSimon Glass 	retloc = gd->arch.mp_alloc_base;
7645bae2e3SSimon Glass 	gd->arch.mp_alloc_base += size;
777737d5c6SDave Liu 
787737d5c6SDave Liu 	memset((void *)&qe_immr->muram[retloc], 0, size);
797737d5c6SDave Liu 
807737d5c6SDave Liu 	__asm__ __volatile__("sync");
817737d5c6SDave Liu 
827737d5c6SDave Liu 	return retloc;
837737d5c6SDave Liu }
8493d33204SZhao Qiang #endif
857737d5c6SDave Liu 
qe_muram_addr(uint offset)867737d5c6SDave Liu void *qe_muram_addr(uint offset)
877737d5c6SDave Liu {
887737d5c6SDave Liu 	return (void *)&qe_immr->muram[offset];
897737d5c6SDave Liu }
907737d5c6SDave Liu 
913bf46e6aSZhao Qiang #ifdef CONFIG_QE
qe_sdma_init(void)927737d5c6SDave Liu static void qe_sdma_init(void)
937737d5c6SDave Liu {
947737d5c6SDave Liu 	volatile sdma_t	*p;
957737d5c6SDave Liu 	uint		sdma_buffer_base;
967737d5c6SDave Liu 
977737d5c6SDave Liu 	p = (volatile sdma_t *)&qe_immr->sdma;
987737d5c6SDave Liu 
997737d5c6SDave Liu 	/* All of DMA transaction in bus 1 */
1007737d5c6SDave Liu 	out_be32(&p->sdaqr, 0);
1017737d5c6SDave Liu 	out_be32(&p->sdaqmr, 0);
1027737d5c6SDave Liu 
1037737d5c6SDave Liu 	/* Allocate 2KB temporary buffer for sdma */
104ff9658d7SDave Liu 	sdma_buffer_base = qe_muram_alloc(2048, 4096);
1057737d5c6SDave Liu 	out_be32(&p->sdwbcr, sdma_buffer_base & QE_SDEBCR_BA_MASK);
1067737d5c6SDave Liu 
1077737d5c6SDave Liu 	/* Clear sdma status */
1087737d5c6SDave Liu 	out_be32(&p->sdsr, 0x03000000);
1097737d5c6SDave Liu 
1107737d5c6SDave Liu 	/* Enable global mode on bus 1, and 2KB buffer size */
1117737d5c6SDave Liu 	out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT));
1127737d5c6SDave Liu }
1137737d5c6SDave Liu 
1144e7b25e4SHaiying Wang /* This table is a list of the serial numbers of the Threads, taken from the
1154e7b25e4SHaiying Wang  * "SNUM Table" chart in the QE Reference Manual. The order is not important,
1164e7b25e4SHaiying Wang  * we just need to know what the SNUMs are for the threads.
1174e7b25e4SHaiying Wang  */
1184e7b25e4SHaiying Wang static u8 thread_snum[] = {
119a88731a6SGerlando Falauto /* Evthreads 16-29 are not supported in MPC8309 */
120a88731a6SGerlando Falauto #if !defined(CONFIG_MPC8309)
1217737d5c6SDave Liu 	0x04, 0x05, 0x0c, 0x0d,
1227737d5c6SDave Liu 	0x14, 0x15, 0x1c, 0x1d,
1237737d5c6SDave Liu 	0x24, 0x25, 0x2c, 0x2d,
124a88731a6SGerlando Falauto 	0x34, 0x35,
125a88731a6SGerlando Falauto #endif
126a88731a6SGerlando Falauto 	0x88, 0x89, 0x98, 0x99,
127a88731a6SGerlando Falauto 	0xa8, 0xa9, 0xb8, 0xb9,
128a88731a6SGerlando Falauto 	0xc8, 0xc9, 0xd8, 0xd9,
129a88731a6SGerlando Falauto 	0xe8, 0xe9, 0x08, 0x09,
130a88731a6SGerlando Falauto 	0x18, 0x19, 0x28, 0x29,
131a88731a6SGerlando Falauto 	0x38, 0x39, 0x48, 0x49,
132a88731a6SGerlando Falauto 	0x58, 0x59, 0x68, 0x69,
133a88731a6SGerlando Falauto 	0x78, 0x79, 0x80, 0x81
1347737d5c6SDave Liu };
1357737d5c6SDave Liu 
qe_snums_init(void)1367737d5c6SDave Liu static void qe_snums_init(void)
1377737d5c6SDave Liu {
1387737d5c6SDave Liu 	int	i;
1397737d5c6SDave Liu 
1407737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1417737d5c6SDave Liu 		snums[i].state = QE_SNUM_STATE_FREE;
1427737d5c6SDave Liu 		snums[i].num   = thread_snum[i];
1437737d5c6SDave Liu 	}
1447737d5c6SDave Liu }
1457737d5c6SDave Liu 
qe_get_snum(void)1467737d5c6SDave Liu int qe_get_snum(void)
1477737d5c6SDave Liu {
1487737d5c6SDave Liu 	int	snum = -EBUSY;
1497737d5c6SDave Liu 	int	i;
1507737d5c6SDave Liu 
1517737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1527737d5c6SDave Liu 		if (snums[i].state == QE_SNUM_STATE_FREE) {
1537737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_USED;
1547737d5c6SDave Liu 			snum = snums[i].num;
1557737d5c6SDave Liu 			break;
1567737d5c6SDave Liu 		}
1577737d5c6SDave Liu 	}
1587737d5c6SDave Liu 
1597737d5c6SDave Liu 	return snum;
1607737d5c6SDave Liu }
1617737d5c6SDave Liu 
qe_put_snum(u8 snum)1627737d5c6SDave Liu void qe_put_snum(u8 snum)
1637737d5c6SDave Liu {
1647737d5c6SDave Liu 	int	i;
1657737d5c6SDave Liu 
1667737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1677737d5c6SDave Liu 		if (snums[i].num == snum) {
1687737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_FREE;
1697737d5c6SDave Liu 			break;
1707737d5c6SDave Liu 		}
1717737d5c6SDave Liu 	}
1727737d5c6SDave Liu }
1737737d5c6SDave Liu 
qe_init(uint qe_base)1747737d5c6SDave Liu void qe_init(uint qe_base)
1757737d5c6SDave Liu {
1767737d5c6SDave Liu 	/* Init the QE IMMR base */
1777737d5c6SDave Liu 	qe_immr = (qe_map_t *)qe_base;
1787737d5c6SDave Liu 
179f2717b47STimur Tabi #ifdef CONFIG_SYS_QE_FMAN_FW_IN_NOR
180c0a14aedSWolfgang Denk 	/*
181c0a14aedSWolfgang Denk 	 * Upload microcode to IRAM for those SOCs which do not have ROM in QE.
1822d4de6aeSHaiying Wang 	 */
183dcf1d774SZhao Qiang 	qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR);
1842d4de6aeSHaiying Wang 
1852d4de6aeSHaiying Wang 	/* enable the microcode in IRAM */
1862d4de6aeSHaiying Wang 	out_be32(&qe_immr->iram.iready,QE_IRAM_READY);
1872d4de6aeSHaiying Wang #endif
1882d4de6aeSHaiying Wang 
18945bae2e3SSimon Glass 	gd->arch.mp_alloc_base = QE_DATAONLY_BASE;
19045bae2e3SSimon Glass 	gd->arch.mp_alloc_top = gd->arch.mp_alloc_base + QE_DATAONLY_SIZE;
1917737d5c6SDave Liu 
1927737d5c6SDave Liu 	qe_sdma_init();
1937737d5c6SDave Liu 	qe_snums_init();
1947737d5c6SDave Liu }
1953bf46e6aSZhao Qiang #endif
1967737d5c6SDave Liu 
19793d33204SZhao Qiang #ifdef CONFIG_U_QE
u_qe_init(void)19893d33204SZhao Qiang void u_qe_init(void)
19993d33204SZhao Qiang {
200d3e6d30cSZhao Qiang 	qe_immr = (qe_map_t *)(CONFIG_SYS_IMMR + QE_IMMR_OFFSET);
20193d33204SZhao Qiang 
202*5aa03dddSZhao Qiang 	void *addr = (void *)CONFIG_SYS_QE_FW_ADDR;
203*5aa03dddSZhao Qiang #ifdef CONFIG_SYS_QE_FMAN_FW_IN_MMC
204*5aa03dddSZhao Qiang 	int dev = CONFIG_SYS_MMC_ENV_DEV;
205*5aa03dddSZhao Qiang 	u32 cnt = CONFIG_SYS_QE_FMAN_FW_LENGTH / 512;
206*5aa03dddSZhao Qiang 	u32 blk = CONFIG_SYS_QE_FW_ADDR / 512;
207*5aa03dddSZhao Qiang 
208*5aa03dddSZhao Qiang 	if (mmc_initialize(gd->bd)) {
209*5aa03dddSZhao Qiang 		printf("%s: mmc_initialize() failed\n", __func__);
210*5aa03dddSZhao Qiang 		return;
211*5aa03dddSZhao Qiang 	}
212*5aa03dddSZhao Qiang 	addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH);
213*5aa03dddSZhao Qiang 	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
214*5aa03dddSZhao Qiang 
215*5aa03dddSZhao Qiang 	if (!mmc) {
216*5aa03dddSZhao Qiang 		free(addr);
217*5aa03dddSZhao Qiang 		printf("\nMMC cannot find device for ucode\n");
218*5aa03dddSZhao Qiang 	} else {
219*5aa03dddSZhao Qiang 		printf("\nMMC read: dev # %u, block # %u, count %u ...\n",
220*5aa03dddSZhao Qiang 		       dev, blk, cnt);
221*5aa03dddSZhao Qiang 		mmc_init(mmc);
222*5aa03dddSZhao Qiang 		(void)mmc->block_dev.block_read(&mmc->block_dev, blk, cnt,
223*5aa03dddSZhao Qiang 						addr);
224*5aa03dddSZhao Qiang 	}
225*5aa03dddSZhao Qiang #endif
226*5aa03dddSZhao Qiang 	u_qe_upload_firmware(addr);
22793d33204SZhao Qiang 	out_be32(&qe_immr->iram.iready, QE_IRAM_READY);
228*5aa03dddSZhao Qiang #ifdef CONFIG_SYS_QE_FMAN_FW_IN_MMC
229*5aa03dddSZhao Qiang 	free(addr);
230*5aa03dddSZhao Qiang #endif
23193d33204SZhao Qiang }
23293d33204SZhao Qiang #endif
23393d33204SZhao Qiang 
234ae42eb03SZhao Qiang #ifdef CONFIG_U_QE
u_qe_resume(void)235ae42eb03SZhao Qiang void u_qe_resume(void)
236ae42eb03SZhao Qiang {
237ae42eb03SZhao Qiang 	qe_map_t *qe_immrr;
238ae42eb03SZhao Qiang 
239d3e6d30cSZhao Qiang 	qe_immrr = (qe_map_t *)(CONFIG_SYS_IMMR + QE_IMMR_OFFSET);
240ae42eb03SZhao Qiang 	u_qe_firmware_resume((const void *)CONFIG_SYS_QE_FW_ADDR, qe_immrr);
241ae42eb03SZhao Qiang 	out_be32(&qe_immrr->iram.iready, QE_IRAM_READY);
242ae42eb03SZhao Qiang }
243ae42eb03SZhao Qiang #endif
244ae42eb03SZhao Qiang 
qe_reset(void)2457737d5c6SDave Liu void qe_reset(void)
2467737d5c6SDave Liu {
2477737d5c6SDave Liu 	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
2487737d5c6SDave Liu 			 (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
2497737d5c6SDave Liu }
2507737d5c6SDave Liu 
2513bf46e6aSZhao Qiang #ifdef CONFIG_QE
qe_assign_page(uint snum,uint para_ram_base)2527737d5c6SDave Liu void qe_assign_page(uint snum, uint para_ram_base)
2537737d5c6SDave Liu {
2547737d5c6SDave Liu 	u32	cecr;
2557737d5c6SDave Liu 
2567737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecdr, para_ram_base);
2577737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecr, ((u32) snum<<QE_CR_ASSIGN_PAGE_SNUM_SHIFT)
2587737d5c6SDave Liu 					 | QE_CR_FLG | QE_ASSIGN_PAGE);
2597737d5c6SDave Liu 
2607737d5c6SDave Liu 	/* Wait for the QE_CR_FLG to clear */
2617737d5c6SDave Liu 	do {
2627737d5c6SDave Liu 		cecr = in_be32(&qe_immr->cp.cecr);
2637737d5c6SDave Liu 	} while (cecr & QE_CR_FLG );
2647737d5c6SDave Liu 
2657737d5c6SDave Liu 	return;
2667737d5c6SDave Liu }
2673bf46e6aSZhao Qiang #endif
2687737d5c6SDave Liu 
2697737d5c6SDave Liu /*
2707737d5c6SDave Liu  * brg: 0~15 as BRG1~BRG16
2717737d5c6SDave Liu    rate: baud rate
2727737d5c6SDave Liu  * BRG input clock comes from the BRGCLK (internal clock generated from
2737737d5c6SDave Liu    the QE clock, it is one-half of the QE clock), If need the clock source
2747737d5c6SDave Liu    from CLKn pin, we have te change the function.
2757737d5c6SDave Liu  */
2767737d5c6SDave Liu 
2771206c184SSimon Glass #define BRG_CLK		(gd->arch.brg_clk)
2787737d5c6SDave Liu 
27993d33204SZhao Qiang #ifdef CONFIG_QE
qe_set_brg(uint brg,uint rate)2807737d5c6SDave Liu int qe_set_brg(uint brg, uint rate)
2817737d5c6SDave Liu {
2827737d5c6SDave Liu 	volatile uint	*bp;
2837737d5c6SDave Liu 	u32		divisor;
2847737d5c6SDave Liu 	int		div16 = 0;
2857737d5c6SDave Liu 
2867737d5c6SDave Liu 	if (brg >= QE_NUM_OF_BRGS)
2877737d5c6SDave Liu 		return -EINVAL;
2887737d5c6SDave Liu 	bp = (uint *)&qe_immr->brg.brgc1;
2897737d5c6SDave Liu 	bp += brg;
2907737d5c6SDave Liu 
2917737d5c6SDave Liu 	divisor = (BRG_CLK / rate);
2927737d5c6SDave Liu 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
2937737d5c6SDave Liu 		div16 = 1;
2947737d5c6SDave Liu 		divisor /= 16;
2957737d5c6SDave Liu 	}
2967737d5c6SDave Liu 
2977737d5c6SDave Liu 	*bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
2987737d5c6SDave Liu 	__asm__ __volatile__("sync");
2997737d5c6SDave Liu 
3007737d5c6SDave Liu 	if (div16) {
3017737d5c6SDave Liu 		*bp |= QE_BRGC_DIV16;
3027737d5c6SDave Liu 		__asm__ __volatile__("sync");
3037737d5c6SDave Liu 	}
3047737d5c6SDave Liu 
3057737d5c6SDave Liu 	return 0;
3067737d5c6SDave Liu }
30793d33204SZhao Qiang #endif
3087737d5c6SDave Liu 
3097737d5c6SDave Liu /* Set ethernet MII clock master
3107737d5c6SDave Liu */
qe_set_mii_clk_src(int ucc_num)3117737d5c6SDave Liu int qe_set_mii_clk_src(int ucc_num)
3127737d5c6SDave Liu {
3137737d5c6SDave Liu 	u32	cmxgcr;
3147737d5c6SDave Liu 
3157737d5c6SDave Liu 	/* check if the UCC number is in range. */
3167737d5c6SDave Liu 	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) {
3177737d5c6SDave Liu 		printf("%s: ucc num not in ranges\n", __FUNCTION__);
3187737d5c6SDave Liu 		return -EINVAL;
3197737d5c6SDave Liu 	}
3207737d5c6SDave Liu 
3217737d5c6SDave Liu 	cmxgcr = in_be32(&qe_immr->qmx.cmxgcr);
3227737d5c6SDave Liu 	cmxgcr &= ~QE_CMXGCR_MII_ENET_MNG_MASK;
3237737d5c6SDave Liu 	cmxgcr |= (ucc_num <<QE_CMXGCR_MII_ENET_MNG_SHIFT);
3247737d5c6SDave Liu 	out_be32(&qe_immr->qmx.cmxgcr, cmxgcr);
3257737d5c6SDave Liu 
3267737d5c6SDave Liu 	return 0;
3277737d5c6SDave Liu }
3287737d5c6SDave Liu 
329b8ec2385STimur Tabi /* Firmware information stored here for qe_get_firmware_info() */
330b8ec2385STimur Tabi static struct qe_firmware_info qe_firmware_info;
331b8ec2385STimur Tabi 
332b8ec2385STimur Tabi /*
333b8ec2385STimur Tabi  * Set to 1 if QE firmware has been uploaded, and therefore
334b8ec2385STimur Tabi  * qe_firmware_info contains valid data.
335b8ec2385STimur Tabi  */
336b8ec2385STimur Tabi static int qe_firmware_uploaded;
337b8ec2385STimur Tabi 
338b8ec2385STimur Tabi /*
339b8ec2385STimur Tabi  * Upload a QE microcode
340b8ec2385STimur Tabi  *
341b8ec2385STimur Tabi  * This function is a worker function for qe_upload_firmware().  It does
342b8ec2385STimur Tabi  * the actual uploading of the microcode.
343b8ec2385STimur Tabi  */
qe_upload_microcode(const void * base,const struct qe_microcode * ucode)344b8ec2385STimur Tabi static void qe_upload_microcode(const void *base,
345b8ec2385STimur Tabi 	const struct qe_microcode *ucode)
346b8ec2385STimur Tabi {
347b8ec2385STimur Tabi 	const u32 *code = base + be32_to_cpu(ucode->code_offset);
348b8ec2385STimur Tabi 	unsigned int i;
349b8ec2385STimur Tabi 
350b8ec2385STimur Tabi 	if (ucode->major || ucode->minor || ucode->revision)
351b8ec2385STimur Tabi 		printf("QE: uploading microcode '%s' version %u.%u.%u\n",
352e94a8fd3SZhao Qiang 		       (char *)ucode->id, (u16)ucode->major, (u16)ucode->minor,
353e94a8fd3SZhao Qiang 		       (u16)ucode->revision);
354b8ec2385STimur Tabi 	else
355e94a8fd3SZhao Qiang 		printf("QE: uploading microcode '%s'\n", (char *)ucode->id);
356b8ec2385STimur Tabi 
357b8ec2385STimur Tabi 	/* Use auto-increment */
358b8ec2385STimur Tabi 	out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
359b8ec2385STimur Tabi 		QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
360b8ec2385STimur Tabi 
361b8ec2385STimur Tabi 	for (i = 0; i < be32_to_cpu(ucode->count); i++)
362b8ec2385STimur Tabi 		out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
363b8ec2385STimur Tabi }
364b8ec2385STimur Tabi 
365b8ec2385STimur Tabi /*
366b8ec2385STimur Tabi  * Upload a microcode to the I-RAM at a specific address.
367b8ec2385STimur Tabi  *
368b8ec2385STimur Tabi  * See docs/README.qe_firmware for information on QE microcode uploading.
369b8ec2385STimur Tabi  *
370b8ec2385STimur Tabi  * Currently, only version 1 is supported, so the 'version' field must be
371b8ec2385STimur Tabi  * set to 1.
372b8ec2385STimur Tabi  *
373b8ec2385STimur Tabi  * The SOC model and revision are not validated, they are only displayed for
374b8ec2385STimur Tabi  * informational purposes.
375b8ec2385STimur Tabi  *
376b8ec2385STimur Tabi  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
377b8ec2385STimur Tabi  * all of the microcode structures, minus the CRC.
378b8ec2385STimur Tabi  *
379b8ec2385STimur Tabi  * 'length' is the size that the structure says it is, including the CRC.
380b8ec2385STimur Tabi  */
qe_upload_firmware(const struct qe_firmware * firmware)381b8ec2385STimur Tabi int qe_upload_firmware(const struct qe_firmware *firmware)
382b8ec2385STimur Tabi {
383b8ec2385STimur Tabi 	unsigned int i;
384b8ec2385STimur Tabi 	unsigned int j;
385b8ec2385STimur Tabi 	u32 crc;
386b8ec2385STimur Tabi 	size_t calc_size = sizeof(struct qe_firmware);
387b8ec2385STimur Tabi 	size_t length;
388b8ec2385STimur Tabi 	const struct qe_header *hdr;
389ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
39073fb5838SYork Sun #ifdef CONFIG_ARCH_LS1021A
3919c7c86f4SZhao Qiang 	struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR;
3929c7c86f4SZhao Qiang #else
393ca721fb2SZhao Qiang 	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
394ca721fb2SZhao Qiang #endif
3959c7c86f4SZhao Qiang #endif
396b8ec2385STimur Tabi 	if (!firmware) {
397b8ec2385STimur Tabi 		printf("Invalid address\n");
398b8ec2385STimur Tabi 		return -EINVAL;
399b8ec2385STimur Tabi 	}
400b8ec2385STimur Tabi 
401b8ec2385STimur Tabi 	hdr = &firmware->header;
402b8ec2385STimur Tabi 	length = be32_to_cpu(hdr->length);
403b8ec2385STimur Tabi 
404b8ec2385STimur Tabi 	/* Check the magic */
405b8ec2385STimur Tabi 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
406b8ec2385STimur Tabi 	    (hdr->magic[2] != 'F')) {
40712eeb135SVijay Rai 		printf("QE microcode not found\n");
408ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
409ca721fb2SZhao Qiang 		setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE);
410ca721fb2SZhao Qiang #endif
411b8ec2385STimur Tabi 		return -EPERM;
412b8ec2385STimur Tabi 	}
413b8ec2385STimur Tabi 
414b8ec2385STimur Tabi 	/* Check the version */
415b8ec2385STimur Tabi 	if (hdr->version != 1) {
416b8ec2385STimur Tabi 		printf("Unsupported version\n");
417b8ec2385STimur Tabi 		return -EPERM;
418b8ec2385STimur Tabi 	}
419b8ec2385STimur Tabi 
420b8ec2385STimur Tabi 	/* Validate some of the fields */
421491fb6deSTimur Tabi 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
422b8ec2385STimur Tabi 		printf("Invalid data\n");
423b8ec2385STimur Tabi 		return -EINVAL;
424b8ec2385STimur Tabi 	}
425b8ec2385STimur Tabi 
426b8ec2385STimur Tabi 	/* Validate the length and check if there's a CRC */
427b8ec2385STimur Tabi 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
428b8ec2385STimur Tabi 
429b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++)
430b8ec2385STimur Tabi 		/*
431b8ec2385STimur Tabi 		 * For situations where the second RISC uses the same microcode
432b8ec2385STimur Tabi 		 * as the first, the 'code_offset' and 'count' fields will be
433b8ec2385STimur Tabi 		 * zero, so it's okay to add those.
434b8ec2385STimur Tabi 		 */
435b8ec2385STimur Tabi 		calc_size += sizeof(u32) *
436b8ec2385STimur Tabi 			be32_to_cpu(firmware->microcode[i].count);
437b8ec2385STimur Tabi 
438b8ec2385STimur Tabi 	/* Validate the length */
439b8ec2385STimur Tabi 	if (length != calc_size + sizeof(u32)) {
440b8ec2385STimur Tabi 		printf("Invalid length\n");
441b8ec2385STimur Tabi 		return -EPERM;
442b8ec2385STimur Tabi 	}
443b8ec2385STimur Tabi 
444b8ec2385STimur Tabi 	/*
445b8ec2385STimur Tabi 	 * Validate the CRC.  We would normally call crc32_no_comp(), but that
446b8ec2385STimur Tabi 	 * function isn't available unless you turn on JFFS support.
447b8ec2385STimur Tabi 	 */
448b8ec2385STimur Tabi 	crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
449b8ec2385STimur Tabi 	if (crc != (crc32(-1, (const void *) firmware, calc_size) ^ -1)) {
450b8ec2385STimur Tabi 		printf("Firmware CRC is invalid\n");
451b8ec2385STimur Tabi 		return -EIO;
452b8ec2385STimur Tabi 	}
453b8ec2385STimur Tabi 
454b8ec2385STimur Tabi 	/*
455b8ec2385STimur Tabi 	 * If the microcode calls for it, split the I-RAM.
456b8ec2385STimur Tabi 	 */
457b8ec2385STimur Tabi 	if (!firmware->split) {
458b8ec2385STimur Tabi 		out_be16(&qe_immr->cp.cercr,
459b8ec2385STimur Tabi 			in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR);
460b8ec2385STimur Tabi 	}
461b8ec2385STimur Tabi 
462b8ec2385STimur Tabi 	if (firmware->soc.model)
463b8ec2385STimur Tabi 		printf("Firmware '%s' for %u V%u.%u\n",
464b8ec2385STimur Tabi 			firmware->id, be16_to_cpu(firmware->soc.model),
465b8ec2385STimur Tabi 			firmware->soc.major, firmware->soc.minor);
466b8ec2385STimur Tabi 	else
467b8ec2385STimur Tabi 		printf("Firmware '%s'\n", firmware->id);
468b8ec2385STimur Tabi 
469b8ec2385STimur Tabi 	/*
470b8ec2385STimur Tabi 	 * The QE only supports one microcode per RISC, so clear out all the
471b8ec2385STimur Tabi 	 * saved microcode information and put in the new.
472b8ec2385STimur Tabi 	 */
473b8ec2385STimur Tabi 	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
4740e0224eeSZhao Qiang 	strncpy(qe_firmware_info.id, (char *)firmware->id, 62);
475b8ec2385STimur Tabi 	qe_firmware_info.extended_modes = firmware->extended_modes;
476b8ec2385STimur Tabi 	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
477b8ec2385STimur Tabi 		sizeof(firmware->vtraps));
478b8ec2385STimur Tabi 	qe_firmware_uploaded = 1;
479b8ec2385STimur Tabi 
480b8ec2385STimur Tabi 	/* Loop through each microcode. */
481b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++) {
482b8ec2385STimur Tabi 		const struct qe_microcode *ucode = &firmware->microcode[i];
483b8ec2385STimur Tabi 
484b8ec2385STimur Tabi 		/* Upload a microcode if it's present */
485b8ec2385STimur Tabi 		if (ucode->code_offset)
486b8ec2385STimur Tabi 			qe_upload_microcode(firmware, ucode);
487b8ec2385STimur Tabi 
488b8ec2385STimur Tabi 		/* Program the traps for this processor */
489b8ec2385STimur Tabi 		for (j = 0; j < 16; j++) {
490b8ec2385STimur Tabi 			u32 trap = be32_to_cpu(ucode->traps[j]);
491b8ec2385STimur Tabi 
492b8ec2385STimur Tabi 			if (trap)
493b8ec2385STimur Tabi 				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
494b8ec2385STimur Tabi 		}
495b8ec2385STimur Tabi 
496b8ec2385STimur Tabi 		/* Enable traps */
497b8ec2385STimur Tabi 		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
498b8ec2385STimur Tabi 	}
499b8ec2385STimur Tabi 
500b8ec2385STimur Tabi 	return 0;
501b8ec2385STimur Tabi }
502b8ec2385STimur Tabi 
5035632d15cSZhao Qiang #ifdef CONFIG_U_QE
5045632d15cSZhao Qiang /*
5055632d15cSZhao Qiang  * Upload a microcode to the I-RAM at a specific address.
5065632d15cSZhao Qiang  *
5075632d15cSZhao Qiang  * See docs/README.qe_firmware for information on QE microcode uploading.
5085632d15cSZhao Qiang  *
5095632d15cSZhao Qiang  * Currently, only version 1 is supported, so the 'version' field must be
5105632d15cSZhao Qiang  * set to 1.
5115632d15cSZhao Qiang  *
5125632d15cSZhao Qiang  * The SOC model and revision are not validated, they are only displayed for
5135632d15cSZhao Qiang  * informational purposes.
5145632d15cSZhao Qiang  *
5155632d15cSZhao Qiang  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
5165632d15cSZhao Qiang  * all of the microcode structures, minus the CRC.
5175632d15cSZhao Qiang  *
5185632d15cSZhao Qiang  * 'length' is the size that the structure says it is, including the CRC.
5195632d15cSZhao Qiang  */
u_qe_upload_firmware(const struct qe_firmware * firmware)5205632d15cSZhao Qiang int u_qe_upload_firmware(const struct qe_firmware *firmware)
5215632d15cSZhao Qiang {
5225632d15cSZhao Qiang 	unsigned int i;
5235632d15cSZhao Qiang 	unsigned int j;
5245632d15cSZhao Qiang 	u32 crc;
5255632d15cSZhao Qiang 	size_t calc_size = sizeof(struct qe_firmware);
5265632d15cSZhao Qiang 	size_t length;
5275632d15cSZhao Qiang 	const struct qe_header *hdr;
5285632d15cSZhao Qiang #ifdef CONFIG_DEEP_SLEEP
52973fb5838SYork Sun #ifdef CONFIG_ARCH_LS1021A
5309c7c86f4SZhao Qiang 	struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR;
5319c7c86f4SZhao Qiang #else
5325632d15cSZhao Qiang 	ccsr_gur_t __iomem *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
5335632d15cSZhao Qiang #endif
5349c7c86f4SZhao Qiang #endif
5355632d15cSZhao Qiang 	if (!firmware) {
5365632d15cSZhao Qiang 		printf("Invalid address\n");
5375632d15cSZhao Qiang 		return -EINVAL;
5385632d15cSZhao Qiang 	}
5395632d15cSZhao Qiang 
5405632d15cSZhao Qiang 	hdr = &firmware->header;
5415632d15cSZhao Qiang 	length = be32_to_cpu(hdr->length);
5425632d15cSZhao Qiang 
5435632d15cSZhao Qiang 	/* Check the magic */
5445632d15cSZhao Qiang 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
5455632d15cSZhao Qiang 	    (hdr->magic[2] != 'F')) {
5465632d15cSZhao Qiang 		printf("Not a microcode\n");
5475632d15cSZhao Qiang #ifdef CONFIG_DEEP_SLEEP
5485632d15cSZhao Qiang 		setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE);
5495632d15cSZhao Qiang #endif
5505632d15cSZhao Qiang 		return -EPERM;
5515632d15cSZhao Qiang 	}
5525632d15cSZhao Qiang 
5535632d15cSZhao Qiang 	/* Check the version */
5545632d15cSZhao Qiang 	if (hdr->version != 1) {
5555632d15cSZhao Qiang 		printf("Unsupported version\n");
5565632d15cSZhao Qiang 		return -EPERM;
5575632d15cSZhao Qiang 	}
5585632d15cSZhao Qiang 
5595632d15cSZhao Qiang 	/* Validate some of the fields */
5605632d15cSZhao Qiang 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
5615632d15cSZhao Qiang 		printf("Invalid data\n");
5625632d15cSZhao Qiang 		return -EINVAL;
5635632d15cSZhao Qiang 	}
5645632d15cSZhao Qiang 
5655632d15cSZhao Qiang 	/* Validate the length and check if there's a CRC */
5665632d15cSZhao Qiang 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
5675632d15cSZhao Qiang 
5685632d15cSZhao Qiang 	for (i = 0; i < firmware->count; i++)
5695632d15cSZhao Qiang 		/*
5705632d15cSZhao Qiang 		 * For situations where the second RISC uses the same microcode
5715632d15cSZhao Qiang 		 * as the first, the 'code_offset' and 'count' fields will be
5725632d15cSZhao Qiang 		 * zero, so it's okay to add those.
5735632d15cSZhao Qiang 		 */
5745632d15cSZhao Qiang 		calc_size += sizeof(u32) *
5755632d15cSZhao Qiang 			be32_to_cpu(firmware->microcode[i].count);
5765632d15cSZhao Qiang 
5775632d15cSZhao Qiang 	/* Validate the length */
5785632d15cSZhao Qiang 	if (length != calc_size + sizeof(u32)) {
5795632d15cSZhao Qiang 		printf("Invalid length\n");
5805632d15cSZhao Qiang 		return -EPERM;
5815632d15cSZhao Qiang 	}
5825632d15cSZhao Qiang 
5835632d15cSZhao Qiang 	/*
5845632d15cSZhao Qiang 	 * Validate the CRC.  We would normally call crc32_no_comp(), but that
5855632d15cSZhao Qiang 	 * function isn't available unless you turn on JFFS support.
5865632d15cSZhao Qiang 	 */
5875632d15cSZhao Qiang 	crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
5885632d15cSZhao Qiang 	if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) {
5895632d15cSZhao Qiang 		printf("Firmware CRC is invalid\n");
5905632d15cSZhao Qiang 		return -EIO;
5915632d15cSZhao Qiang 	}
5925632d15cSZhao Qiang 
5935632d15cSZhao Qiang 	/*
5945632d15cSZhao Qiang 	 * If the microcode calls for it, split the I-RAM.
5955632d15cSZhao Qiang 	 */
5965632d15cSZhao Qiang 	if (!firmware->split) {
5975632d15cSZhao Qiang 		out_be16(&qe_immr->cp.cercr,
5985632d15cSZhao Qiang 			 in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR);
5995632d15cSZhao Qiang 	}
6005632d15cSZhao Qiang 
6015632d15cSZhao Qiang 	if (firmware->soc.model)
6025632d15cSZhao Qiang 		printf("Firmware '%s' for %u V%u.%u\n",
6035632d15cSZhao Qiang 		       firmware->id, be16_to_cpu(firmware->soc.model),
6045632d15cSZhao Qiang 		       firmware->soc.major, firmware->soc.minor);
6055632d15cSZhao Qiang 	else
6065632d15cSZhao Qiang 		printf("Firmware '%s'\n", firmware->id);
6075632d15cSZhao Qiang 
6085632d15cSZhao Qiang 	/* Loop through each microcode. */
6095632d15cSZhao Qiang 	for (i = 0; i < firmware->count; i++) {
6105632d15cSZhao Qiang 		const struct qe_microcode *ucode = &firmware->microcode[i];
6115632d15cSZhao Qiang 
6125632d15cSZhao Qiang 		/* Upload a microcode if it's present */
6135632d15cSZhao Qiang 		if (ucode->code_offset)
6145632d15cSZhao Qiang 			qe_upload_microcode(firmware, ucode);
6155632d15cSZhao Qiang 
6165632d15cSZhao Qiang 		/* Program the traps for this processor */
6175632d15cSZhao Qiang 		for (j = 0; j < 16; j++) {
6185632d15cSZhao Qiang 			u32 trap = be32_to_cpu(ucode->traps[j]);
6195632d15cSZhao Qiang 
6205632d15cSZhao Qiang 			if (trap)
6215632d15cSZhao Qiang 				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
6225632d15cSZhao Qiang 		}
6235632d15cSZhao Qiang 
6245632d15cSZhao Qiang 		/* Enable traps */
6255632d15cSZhao Qiang 		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
6265632d15cSZhao Qiang 	}
6275632d15cSZhao Qiang 
6285632d15cSZhao Qiang 	return 0;
6295632d15cSZhao Qiang }
6305632d15cSZhao Qiang #endif
6315632d15cSZhao Qiang 
632ae42eb03SZhao Qiang #ifdef CONFIG_U_QE
u_qe_firmware_resume(const struct qe_firmware * firmware,qe_map_t * qe_immrr)633ae42eb03SZhao Qiang int u_qe_firmware_resume(const struct qe_firmware *firmware, qe_map_t *qe_immrr)
634ae42eb03SZhao Qiang {
635ae42eb03SZhao Qiang 	unsigned int i;
636ae42eb03SZhao Qiang 	unsigned int j;
637ae42eb03SZhao Qiang 	const struct qe_header *hdr;
638ae42eb03SZhao Qiang 	const u32 *code;
639ae42eb03SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
640ae42eb03SZhao Qiang #ifdef CONFIG_PPC
641ae42eb03SZhao Qiang 	ccsr_gur_t __iomem *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
642ae42eb03SZhao Qiang #else
643ae42eb03SZhao Qiang 	struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR;
644ae42eb03SZhao Qiang #endif
645ae42eb03SZhao Qiang #endif
646ae42eb03SZhao Qiang 
647ae42eb03SZhao Qiang 	if (!firmware)
648ae42eb03SZhao Qiang 		return -EINVAL;
649ae42eb03SZhao Qiang 
650ae42eb03SZhao Qiang 	hdr = &firmware->header;
651ae42eb03SZhao Qiang 
652ae42eb03SZhao Qiang 	/* Check the magic */
653ae42eb03SZhao Qiang 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
654ae42eb03SZhao Qiang 	    (hdr->magic[2] != 'F')) {
655ae42eb03SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
656ae42eb03SZhao Qiang 		setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE);
657ae42eb03SZhao Qiang #endif
658ae42eb03SZhao Qiang 		return -EPERM;
659ae42eb03SZhao Qiang 	}
660ae42eb03SZhao Qiang 
661ae42eb03SZhao Qiang 	/*
662ae42eb03SZhao Qiang 	 * If the microcode calls for it, split the I-RAM.
663ae42eb03SZhao Qiang 	 */
664ae42eb03SZhao Qiang 	if (!firmware->split) {
665ae42eb03SZhao Qiang 		out_be16(&qe_immrr->cp.cercr,
666ae42eb03SZhao Qiang 			 in_be16(&qe_immrr->cp.cercr) | QE_CP_CERCR_CIR);
667ae42eb03SZhao Qiang 	}
668ae42eb03SZhao Qiang 
669ae42eb03SZhao Qiang 	/* Loop through each microcode. */
670ae42eb03SZhao Qiang 	for (i = 0; i < firmware->count; i++) {
671ae42eb03SZhao Qiang 		const struct qe_microcode *ucode = &firmware->microcode[i];
672ae42eb03SZhao Qiang 
673ae42eb03SZhao Qiang 		/* Upload a microcode if it's present */
674ae42eb03SZhao Qiang 		if (!ucode->code_offset)
675ae42eb03SZhao Qiang 			return 0;
676ae42eb03SZhao Qiang 
677ae42eb03SZhao Qiang 		code = (const void *)firmware + be32_to_cpu(ucode->code_offset);
678ae42eb03SZhao Qiang 
679ae42eb03SZhao Qiang 		/* Use auto-increment */
680ae42eb03SZhao Qiang 		out_be32(&qe_immrr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
681ae42eb03SZhao Qiang 			QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
682ae42eb03SZhao Qiang 
683ae42eb03SZhao Qiang 		for (i = 0; i < be32_to_cpu(ucode->count); i++)
684ae42eb03SZhao Qiang 			out_be32(&qe_immrr->iram.idata, be32_to_cpu(code[i]));
685ae42eb03SZhao Qiang 
686ae42eb03SZhao Qiang 		/* Program the traps for this processor */
687ae42eb03SZhao Qiang 		for (j = 0; j < 16; j++) {
688ae42eb03SZhao Qiang 			u32 trap = be32_to_cpu(ucode->traps[j]);
689ae42eb03SZhao Qiang 
690ae42eb03SZhao Qiang 			if (trap)
691ae42eb03SZhao Qiang 				out_be32(&qe_immrr->rsp[i].tibcr[j], trap);
692ae42eb03SZhao Qiang 		}
693ae42eb03SZhao Qiang 
694ae42eb03SZhao Qiang 		/* Enable traps */
695ae42eb03SZhao Qiang 		out_be32(&qe_immrr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
696ae42eb03SZhao Qiang 	}
697ae42eb03SZhao Qiang 
698ae42eb03SZhao Qiang 	return 0;
699ae42eb03SZhao Qiang }
700ae42eb03SZhao Qiang #endif
701ae42eb03SZhao Qiang 
qe_get_firmware_info(void)702b8ec2385STimur Tabi struct qe_firmware_info *qe_get_firmware_info(void)
703b8ec2385STimur Tabi {
704b8ec2385STimur Tabi 	return qe_firmware_uploaded ? &qe_firmware_info : NULL;
705b8ec2385STimur Tabi }
706b8ec2385STimur Tabi 
qe_cmd(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])70754841ab5SWolfgang Denk static int qe_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
708b8ec2385STimur Tabi {
709b8ec2385STimur Tabi 	ulong addr;
710b8ec2385STimur Tabi 
71147e26b1bSWolfgang Denk 	if (argc < 3)
71247e26b1bSWolfgang Denk 		return cmd_usage(cmdtp);
713b8ec2385STimur Tabi 
714b8ec2385STimur Tabi 	if (strcmp(argv[1], "fw") == 0) {
715b8ec2385STimur Tabi 		addr = simple_strtoul(argv[2], NULL, 16);
716b8ec2385STimur Tabi 
717b8ec2385STimur Tabi 		if (!addr) {
718b8ec2385STimur Tabi 			printf("Invalid address\n");
719b8ec2385STimur Tabi 			return -EINVAL;
720b8ec2385STimur Tabi 		}
721b8ec2385STimur Tabi 
722b8ec2385STimur Tabi 		/*
723b8ec2385STimur Tabi 		 * If a length was supplied, compare that with the 'length'
724b8ec2385STimur Tabi 		 * field.
725b8ec2385STimur Tabi 		 */
726b8ec2385STimur Tabi 
727b8ec2385STimur Tabi 		if (argc > 3) {
728b8ec2385STimur Tabi 			ulong length = simple_strtoul(argv[3], NULL, 16);
729b8ec2385STimur Tabi 			struct qe_firmware *firmware = (void *) addr;
730b8ec2385STimur Tabi 
731b8ec2385STimur Tabi 			if (length != be32_to_cpu(firmware->header.length)) {
732b8ec2385STimur Tabi 				printf("Length mismatch\n");
733b8ec2385STimur Tabi 				return -EINVAL;
734b8ec2385STimur Tabi 			}
735b8ec2385STimur Tabi 		}
736b8ec2385STimur Tabi 
737b8ec2385STimur Tabi 		return qe_upload_firmware((const struct qe_firmware *) addr);
738b8ec2385STimur Tabi 	}
739b8ec2385STimur Tabi 
74047e26b1bSWolfgang Denk 	return cmd_usage(cmdtp);
741b8ec2385STimur Tabi }
742b8ec2385STimur Tabi 
743b8ec2385STimur Tabi U_BOOT_CMD(
744b8ec2385STimur Tabi 	qe, 4, 0, qe_cmd,
7452fb2604dSPeter Tyser 	"QUICC Engine commands",
746b8ec2385STimur Tabi 	"fw <addr> [<length>] - Upload firmware binary at address <addr> to "
747a89c33dbSWolfgang Denk 		"the QE,\n"
748a89c33dbSWolfgang Denk 	"\twith optional length <length> verification."
749b8ec2385STimur Tabi );
750