xref: /rk3399_rockchip-uboot/drivers/qe/qe.c (revision 93d3320417b16f331d5edb87409cd7a347c4eeaa)
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"
1438d67a4eSZhao 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 
43*93d33204SZhao Qiang #ifdef CONFIG_QE
447737d5c6SDave Liu uint qe_muram_alloc(uint size, uint align)
457737d5c6SDave Liu {
467737d5c6SDave Liu 	uint	retloc;
477737d5c6SDave Liu 	uint	align_mask, off;
487737d5c6SDave Liu 	uint	savebase;
497737d5c6SDave Liu 
507737d5c6SDave Liu 	align_mask = align - 1;
5145bae2e3SSimon Glass 	savebase = gd->arch.mp_alloc_base;
527737d5c6SDave Liu 
5345bae2e3SSimon Glass 	off = gd->arch.mp_alloc_base & align_mask;
5445bae2e3SSimon Glass 	if (off != 0)
5545bae2e3SSimon Glass 		gd->arch.mp_alloc_base += (align - off);
567737d5c6SDave Liu 
577737d5c6SDave Liu 	if ((off = size & align_mask) != 0)
587737d5c6SDave Liu 		size += (align - off);
597737d5c6SDave Liu 
6045bae2e3SSimon Glass 	if ((gd->arch.mp_alloc_base + size) >= gd->arch.mp_alloc_top) {
6145bae2e3SSimon Glass 		gd->arch.mp_alloc_base = savebase;
627737d5c6SDave Liu 		printf("%s: ran out of ram.\n",  __FUNCTION__);
637737d5c6SDave Liu 	}
647737d5c6SDave Liu 
6545bae2e3SSimon Glass 	retloc = gd->arch.mp_alloc_base;
6645bae2e3SSimon Glass 	gd->arch.mp_alloc_base += size;
677737d5c6SDave Liu 
687737d5c6SDave Liu 	memset((void *)&qe_immr->muram[retloc], 0, size);
697737d5c6SDave Liu 
707737d5c6SDave Liu 	__asm__ __volatile__("sync");
717737d5c6SDave Liu 
727737d5c6SDave Liu 	return retloc;
737737d5c6SDave Liu }
74*93d33204SZhao Qiang #endif
757737d5c6SDave Liu 
767737d5c6SDave Liu void *qe_muram_addr(uint offset)
777737d5c6SDave Liu {
787737d5c6SDave Liu 	return (void *)&qe_immr->muram[offset];
797737d5c6SDave Liu }
807737d5c6SDave Liu 
817737d5c6SDave Liu static void qe_sdma_init(void)
827737d5c6SDave Liu {
837737d5c6SDave Liu 	volatile sdma_t	*p;
847737d5c6SDave Liu 	uint		sdma_buffer_base;
857737d5c6SDave Liu 
867737d5c6SDave Liu 	p = (volatile sdma_t *)&qe_immr->sdma;
877737d5c6SDave Liu 
887737d5c6SDave Liu 	/* All of DMA transaction in bus 1 */
897737d5c6SDave Liu 	out_be32(&p->sdaqr, 0);
907737d5c6SDave Liu 	out_be32(&p->sdaqmr, 0);
917737d5c6SDave Liu 
927737d5c6SDave Liu 	/* Allocate 2KB temporary buffer for sdma */
93ff9658d7SDave Liu 	sdma_buffer_base = qe_muram_alloc(2048, 4096);
947737d5c6SDave Liu 	out_be32(&p->sdwbcr, sdma_buffer_base & QE_SDEBCR_BA_MASK);
957737d5c6SDave Liu 
967737d5c6SDave Liu 	/* Clear sdma status */
977737d5c6SDave Liu 	out_be32(&p->sdsr, 0x03000000);
987737d5c6SDave Liu 
997737d5c6SDave Liu 	/* Enable global mode on bus 1, and 2KB buffer size */
1007737d5c6SDave Liu 	out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT));
1017737d5c6SDave Liu }
1027737d5c6SDave Liu 
1034e7b25e4SHaiying Wang /* This table is a list of the serial numbers of the Threads, taken from the
1044e7b25e4SHaiying Wang  * "SNUM Table" chart in the QE Reference Manual. The order is not important,
1054e7b25e4SHaiying Wang  * we just need to know what the SNUMs are for the threads.
1064e7b25e4SHaiying Wang  */
1074e7b25e4SHaiying Wang static u8 thread_snum[] = {
108a88731a6SGerlando Falauto /* Evthreads 16-29 are not supported in MPC8309 */
109a88731a6SGerlando Falauto #if !defined(CONFIG_MPC8309)
1107737d5c6SDave Liu 	0x04, 0x05, 0x0c, 0x0d,
1117737d5c6SDave Liu 	0x14, 0x15, 0x1c, 0x1d,
1127737d5c6SDave Liu 	0x24, 0x25, 0x2c, 0x2d,
113a88731a6SGerlando Falauto 	0x34, 0x35,
114a88731a6SGerlando Falauto #endif
115a88731a6SGerlando Falauto 	0x88, 0x89, 0x98, 0x99,
116a88731a6SGerlando Falauto 	0xa8, 0xa9, 0xb8, 0xb9,
117a88731a6SGerlando Falauto 	0xc8, 0xc9, 0xd8, 0xd9,
118a88731a6SGerlando Falauto 	0xe8, 0xe9, 0x08, 0x09,
119a88731a6SGerlando Falauto 	0x18, 0x19, 0x28, 0x29,
120a88731a6SGerlando Falauto 	0x38, 0x39, 0x48, 0x49,
121a88731a6SGerlando Falauto 	0x58, 0x59, 0x68, 0x69,
122a88731a6SGerlando Falauto 	0x78, 0x79, 0x80, 0x81
1237737d5c6SDave Liu };
1247737d5c6SDave Liu 
1257737d5c6SDave Liu static void qe_snums_init(void)
1267737d5c6SDave Liu {
1277737d5c6SDave Liu 	int	i;
1287737d5c6SDave Liu 
1297737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1307737d5c6SDave Liu 		snums[i].state = QE_SNUM_STATE_FREE;
1317737d5c6SDave Liu 		snums[i].num   = thread_snum[i];
1327737d5c6SDave Liu 	}
1337737d5c6SDave Liu }
1347737d5c6SDave Liu 
1357737d5c6SDave Liu int qe_get_snum(void)
1367737d5c6SDave Liu {
1377737d5c6SDave Liu 	int	snum = -EBUSY;
1387737d5c6SDave Liu 	int	i;
1397737d5c6SDave Liu 
1407737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1417737d5c6SDave Liu 		if (snums[i].state == QE_SNUM_STATE_FREE) {
1427737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_USED;
1437737d5c6SDave Liu 			snum = snums[i].num;
1447737d5c6SDave Liu 			break;
1457737d5c6SDave Liu 		}
1467737d5c6SDave Liu 	}
1477737d5c6SDave Liu 
1487737d5c6SDave Liu 	return snum;
1497737d5c6SDave Liu }
1507737d5c6SDave Liu 
1517737d5c6SDave Liu void qe_put_snum(u8 snum)
1527737d5c6SDave Liu {
1537737d5c6SDave Liu 	int	i;
1547737d5c6SDave Liu 
1557737d5c6SDave Liu 	for (i = 0; i < QE_NUM_OF_SNUM; i++) {
1567737d5c6SDave Liu 		if (snums[i].num == snum) {
1577737d5c6SDave Liu 			snums[i].state = QE_SNUM_STATE_FREE;
1587737d5c6SDave Liu 			break;
1597737d5c6SDave Liu 		}
1607737d5c6SDave Liu 	}
1617737d5c6SDave Liu }
1627737d5c6SDave Liu 
1637737d5c6SDave Liu void qe_init(uint qe_base)
1647737d5c6SDave Liu {
1657737d5c6SDave Liu 	/* Init the QE IMMR base */
1667737d5c6SDave Liu 	qe_immr = (qe_map_t *)qe_base;
1677737d5c6SDave Liu 
168f2717b47STimur Tabi #ifdef CONFIG_SYS_QE_FMAN_FW_IN_NOR
169c0a14aedSWolfgang Denk 	/*
170c0a14aedSWolfgang Denk 	 * Upload microcode to IRAM for those SOCs which do not have ROM in QE.
1712d4de6aeSHaiying Wang 	 */
172dcf1d774SZhao Qiang 	qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR);
1732d4de6aeSHaiying Wang 
1742d4de6aeSHaiying Wang 	/* enable the microcode in IRAM */
1752d4de6aeSHaiying Wang 	out_be32(&qe_immr->iram.iready,QE_IRAM_READY);
1762d4de6aeSHaiying Wang #endif
1772d4de6aeSHaiying Wang 
17845bae2e3SSimon Glass 	gd->arch.mp_alloc_base = QE_DATAONLY_BASE;
17945bae2e3SSimon Glass 	gd->arch.mp_alloc_top = gd->arch.mp_alloc_base + QE_DATAONLY_SIZE;
1807737d5c6SDave Liu 
1817737d5c6SDave Liu 	qe_sdma_init();
1827737d5c6SDave Liu 	qe_snums_init();
1837737d5c6SDave Liu }
1847737d5c6SDave Liu 
185*93d33204SZhao Qiang #ifdef CONFIG_U_QE
186*93d33204SZhao Qiang void u_qe_init(void)
187*93d33204SZhao Qiang {
188*93d33204SZhao Qiang 	uint qe_base = CONFIG_SYS_IMMR + 0x01400000; /* QE immr base */
189*93d33204SZhao Qiang 	qe_immr = (qe_map_t *)qe_base;
190*93d33204SZhao Qiang 
191*93d33204SZhao Qiang 	qe_upload_firmware((const void *)CONFIG_SYS_QE_FW_ADDR);
192*93d33204SZhao Qiang 	out_be32(&qe_immr->iram.iready, QE_IRAM_READY);
193*93d33204SZhao Qiang }
194*93d33204SZhao Qiang #endif
195*93d33204SZhao Qiang 
1967737d5c6SDave Liu void qe_reset(void)
1977737d5c6SDave Liu {
1987737d5c6SDave Liu 	qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
1997737d5c6SDave Liu 			 (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
2007737d5c6SDave Liu }
2017737d5c6SDave Liu 
2027737d5c6SDave Liu void qe_assign_page(uint snum, uint para_ram_base)
2037737d5c6SDave Liu {
2047737d5c6SDave Liu 	u32	cecr;
2057737d5c6SDave Liu 
2067737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecdr, para_ram_base);
2077737d5c6SDave Liu 	out_be32(&qe_immr->cp.cecr, ((u32) snum<<QE_CR_ASSIGN_PAGE_SNUM_SHIFT)
2087737d5c6SDave Liu 					 | QE_CR_FLG | QE_ASSIGN_PAGE);
2097737d5c6SDave Liu 
2107737d5c6SDave Liu 	/* Wait for the QE_CR_FLG to clear */
2117737d5c6SDave Liu 	do {
2127737d5c6SDave Liu 		cecr = in_be32(&qe_immr->cp.cecr);
2137737d5c6SDave Liu 	} while (cecr & QE_CR_FLG );
2147737d5c6SDave Liu 
2157737d5c6SDave Liu 	return;
2167737d5c6SDave Liu }
2177737d5c6SDave Liu 
2187737d5c6SDave Liu /*
2197737d5c6SDave Liu  * brg: 0~15 as BRG1~BRG16
2207737d5c6SDave Liu    rate: baud rate
2217737d5c6SDave Liu  * BRG input clock comes from the BRGCLK (internal clock generated from
2227737d5c6SDave Liu    the QE clock, it is one-half of the QE clock), If need the clock source
2237737d5c6SDave Liu    from CLKn pin, we have te change the function.
2247737d5c6SDave Liu  */
2257737d5c6SDave Liu 
2261206c184SSimon Glass #define BRG_CLK		(gd->arch.brg_clk)
2277737d5c6SDave Liu 
228*93d33204SZhao Qiang #ifdef CONFIG_QE
2297737d5c6SDave Liu int qe_set_brg(uint brg, uint rate)
2307737d5c6SDave Liu {
2317737d5c6SDave Liu 	volatile uint	*bp;
2327737d5c6SDave Liu 	u32		divisor;
2337737d5c6SDave Liu 	int		div16 = 0;
2347737d5c6SDave Liu 
2357737d5c6SDave Liu 	if (brg >= QE_NUM_OF_BRGS)
2367737d5c6SDave Liu 		return -EINVAL;
2377737d5c6SDave Liu 	bp = (uint *)&qe_immr->brg.brgc1;
2387737d5c6SDave Liu 	bp += brg;
2397737d5c6SDave Liu 
2407737d5c6SDave Liu 	divisor = (BRG_CLK / rate);
2417737d5c6SDave Liu 	if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
2427737d5c6SDave Liu 		div16 = 1;
2437737d5c6SDave Liu 		divisor /= 16;
2447737d5c6SDave Liu 	}
2457737d5c6SDave Liu 
2467737d5c6SDave Liu 	*bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE;
2477737d5c6SDave Liu 	__asm__ __volatile__("sync");
2487737d5c6SDave Liu 
2497737d5c6SDave Liu 	if (div16) {
2507737d5c6SDave Liu 		*bp |= QE_BRGC_DIV16;
2517737d5c6SDave Liu 		__asm__ __volatile__("sync");
2527737d5c6SDave Liu 	}
2537737d5c6SDave Liu 
2547737d5c6SDave Liu 	return 0;
2557737d5c6SDave Liu }
256*93d33204SZhao Qiang #endif
2577737d5c6SDave Liu 
2587737d5c6SDave Liu /* Set ethernet MII clock master
2597737d5c6SDave Liu */
2607737d5c6SDave Liu int qe_set_mii_clk_src(int ucc_num)
2617737d5c6SDave Liu {
2627737d5c6SDave Liu 	u32	cmxgcr;
2637737d5c6SDave Liu 
2647737d5c6SDave Liu 	/* check if the UCC number is in range. */
2657737d5c6SDave Liu 	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) {
2667737d5c6SDave Liu 		printf("%s: ucc num not in ranges\n", __FUNCTION__);
2677737d5c6SDave Liu 		return -EINVAL;
2687737d5c6SDave Liu 	}
2697737d5c6SDave Liu 
2707737d5c6SDave Liu 	cmxgcr = in_be32(&qe_immr->qmx.cmxgcr);
2717737d5c6SDave Liu 	cmxgcr &= ~QE_CMXGCR_MII_ENET_MNG_MASK;
2727737d5c6SDave Liu 	cmxgcr |= (ucc_num <<QE_CMXGCR_MII_ENET_MNG_SHIFT);
2737737d5c6SDave Liu 	out_be32(&qe_immr->qmx.cmxgcr, cmxgcr);
2747737d5c6SDave Liu 
2757737d5c6SDave Liu 	return 0;
2767737d5c6SDave Liu }
2777737d5c6SDave Liu 
278b8ec2385STimur Tabi /* Firmware information stored here for qe_get_firmware_info() */
279b8ec2385STimur Tabi static struct qe_firmware_info qe_firmware_info;
280b8ec2385STimur Tabi 
281b8ec2385STimur Tabi /*
282b8ec2385STimur Tabi  * Set to 1 if QE firmware has been uploaded, and therefore
283b8ec2385STimur Tabi  * qe_firmware_info contains valid data.
284b8ec2385STimur Tabi  */
285b8ec2385STimur Tabi static int qe_firmware_uploaded;
286b8ec2385STimur Tabi 
287b8ec2385STimur Tabi /*
288b8ec2385STimur Tabi  * Upload a QE microcode
289b8ec2385STimur Tabi  *
290b8ec2385STimur Tabi  * This function is a worker function for qe_upload_firmware().  It does
291b8ec2385STimur Tabi  * the actual uploading of the microcode.
292b8ec2385STimur Tabi  */
293b8ec2385STimur Tabi static void qe_upload_microcode(const void *base,
294b8ec2385STimur Tabi 	const struct qe_microcode *ucode)
295b8ec2385STimur Tabi {
296b8ec2385STimur Tabi 	const u32 *code = base + be32_to_cpu(ucode->code_offset);
297b8ec2385STimur Tabi 	unsigned int i;
298b8ec2385STimur Tabi 
299b8ec2385STimur Tabi 	if (ucode->major || ucode->minor || ucode->revision)
300b8ec2385STimur Tabi 		printf("QE: uploading microcode '%s' version %u.%u.%u\n",
301b8ec2385STimur Tabi 			ucode->id, ucode->major, ucode->minor, ucode->revision);
302b8ec2385STimur Tabi 	else
303b8ec2385STimur Tabi 		printf("QE: uploading microcode '%s'\n", ucode->id);
304b8ec2385STimur Tabi 
305b8ec2385STimur Tabi 	/* Use auto-increment */
306b8ec2385STimur Tabi 	out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
307b8ec2385STimur Tabi 		QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
308b8ec2385STimur Tabi 
309b8ec2385STimur Tabi 	for (i = 0; i < be32_to_cpu(ucode->count); i++)
310b8ec2385STimur Tabi 		out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
311b8ec2385STimur Tabi }
312b8ec2385STimur Tabi 
313b8ec2385STimur Tabi /*
314b8ec2385STimur Tabi  * Upload a microcode to the I-RAM at a specific address.
315b8ec2385STimur Tabi  *
316b8ec2385STimur Tabi  * See docs/README.qe_firmware for information on QE microcode uploading.
317b8ec2385STimur Tabi  *
318b8ec2385STimur Tabi  * Currently, only version 1 is supported, so the 'version' field must be
319b8ec2385STimur Tabi  * set to 1.
320b8ec2385STimur Tabi  *
321b8ec2385STimur Tabi  * The SOC model and revision are not validated, they are only displayed for
322b8ec2385STimur Tabi  * informational purposes.
323b8ec2385STimur Tabi  *
324b8ec2385STimur Tabi  * 'calc_size' is the calculated size, in bytes, of the firmware structure and
325b8ec2385STimur Tabi  * all of the microcode structures, minus the CRC.
326b8ec2385STimur Tabi  *
327b8ec2385STimur Tabi  * 'length' is the size that the structure says it is, including the CRC.
328b8ec2385STimur Tabi  */
329b8ec2385STimur Tabi int qe_upload_firmware(const struct qe_firmware *firmware)
330b8ec2385STimur Tabi {
331b8ec2385STimur Tabi 	unsigned int i;
332b8ec2385STimur Tabi 	unsigned int j;
333b8ec2385STimur Tabi 	u32 crc;
334b8ec2385STimur Tabi 	size_t calc_size = sizeof(struct qe_firmware);
335b8ec2385STimur Tabi 	size_t length;
336b8ec2385STimur Tabi 	const struct qe_header *hdr;
337ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
338ca721fb2SZhao Qiang 	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
339ca721fb2SZhao Qiang #endif
340b8ec2385STimur Tabi 	if (!firmware) {
341b8ec2385STimur Tabi 		printf("Invalid address\n");
342b8ec2385STimur Tabi 		return -EINVAL;
343b8ec2385STimur Tabi 	}
344b8ec2385STimur Tabi 
345b8ec2385STimur Tabi 	hdr = &firmware->header;
346b8ec2385STimur Tabi 	length = be32_to_cpu(hdr->length);
347b8ec2385STimur Tabi 
348b8ec2385STimur Tabi 	/* Check the magic */
349b8ec2385STimur Tabi 	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
350b8ec2385STimur Tabi 	    (hdr->magic[2] != 'F')) {
35112eeb135SVijay Rai 		printf("QE microcode not found\n");
352ca721fb2SZhao Qiang #ifdef CONFIG_DEEP_SLEEP
353ca721fb2SZhao Qiang 		setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE);
354ca721fb2SZhao Qiang #endif
355b8ec2385STimur Tabi 		return -EPERM;
356b8ec2385STimur Tabi 	}
357b8ec2385STimur Tabi 
358b8ec2385STimur Tabi 	/* Check the version */
359b8ec2385STimur Tabi 	if (hdr->version != 1) {
360b8ec2385STimur Tabi 		printf("Unsupported version\n");
361b8ec2385STimur Tabi 		return -EPERM;
362b8ec2385STimur Tabi 	}
363b8ec2385STimur Tabi 
364b8ec2385STimur Tabi 	/* Validate some of the fields */
365491fb6deSTimur Tabi 	if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) {
366b8ec2385STimur Tabi 		printf("Invalid data\n");
367b8ec2385STimur Tabi 		return -EINVAL;
368b8ec2385STimur Tabi 	}
369b8ec2385STimur Tabi 
370b8ec2385STimur Tabi 	/* Validate the length and check if there's a CRC */
371b8ec2385STimur Tabi 	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
372b8ec2385STimur Tabi 
373b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++)
374b8ec2385STimur Tabi 		/*
375b8ec2385STimur Tabi 		 * For situations where the second RISC uses the same microcode
376b8ec2385STimur Tabi 		 * as the first, the 'code_offset' and 'count' fields will be
377b8ec2385STimur Tabi 		 * zero, so it's okay to add those.
378b8ec2385STimur Tabi 		 */
379b8ec2385STimur Tabi 		calc_size += sizeof(u32) *
380b8ec2385STimur Tabi 			be32_to_cpu(firmware->microcode[i].count);
381b8ec2385STimur Tabi 
382b8ec2385STimur Tabi 	/* Validate the length */
383b8ec2385STimur Tabi 	if (length != calc_size + sizeof(u32)) {
384b8ec2385STimur Tabi 		printf("Invalid length\n");
385b8ec2385STimur Tabi 		return -EPERM;
386b8ec2385STimur Tabi 	}
387b8ec2385STimur Tabi 
388b8ec2385STimur Tabi 	/*
389b8ec2385STimur Tabi 	 * Validate the CRC.  We would normally call crc32_no_comp(), but that
390b8ec2385STimur Tabi 	 * function isn't available unless you turn on JFFS support.
391b8ec2385STimur Tabi 	 */
392b8ec2385STimur Tabi 	crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
393b8ec2385STimur Tabi 	if (crc != (crc32(-1, (const void *) firmware, calc_size) ^ -1)) {
394b8ec2385STimur Tabi 		printf("Firmware CRC is invalid\n");
395b8ec2385STimur Tabi 		return -EIO;
396b8ec2385STimur Tabi 	}
397b8ec2385STimur Tabi 
398b8ec2385STimur Tabi 	/*
399b8ec2385STimur Tabi 	 * If the microcode calls for it, split the I-RAM.
400b8ec2385STimur Tabi 	 */
401b8ec2385STimur Tabi 	if (!firmware->split) {
402b8ec2385STimur Tabi 		out_be16(&qe_immr->cp.cercr,
403b8ec2385STimur Tabi 			in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR);
404b8ec2385STimur Tabi 	}
405b8ec2385STimur Tabi 
406b8ec2385STimur Tabi 	if (firmware->soc.model)
407b8ec2385STimur Tabi 		printf("Firmware '%s' for %u V%u.%u\n",
408b8ec2385STimur Tabi 			firmware->id, be16_to_cpu(firmware->soc.model),
409b8ec2385STimur Tabi 			firmware->soc.major, firmware->soc.minor);
410b8ec2385STimur Tabi 	else
411b8ec2385STimur Tabi 		printf("Firmware '%s'\n", firmware->id);
412b8ec2385STimur Tabi 
413b8ec2385STimur Tabi 	/*
414b8ec2385STimur Tabi 	 * The QE only supports one microcode per RISC, so clear out all the
415b8ec2385STimur Tabi 	 * saved microcode information and put in the new.
416b8ec2385STimur Tabi 	 */
417b8ec2385STimur Tabi 	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
41806c428bcSDave Liu 	strcpy(qe_firmware_info.id, (char *)firmware->id);
419b8ec2385STimur Tabi 	qe_firmware_info.extended_modes = firmware->extended_modes;
420b8ec2385STimur Tabi 	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
421b8ec2385STimur Tabi 		sizeof(firmware->vtraps));
422b8ec2385STimur Tabi 	qe_firmware_uploaded = 1;
423b8ec2385STimur Tabi 
424b8ec2385STimur Tabi 	/* Loop through each microcode. */
425b8ec2385STimur Tabi 	for (i = 0; i < firmware->count; i++) {
426b8ec2385STimur Tabi 		const struct qe_microcode *ucode = &firmware->microcode[i];
427b8ec2385STimur Tabi 
428b8ec2385STimur Tabi 		/* Upload a microcode if it's present */
429b8ec2385STimur Tabi 		if (ucode->code_offset)
430b8ec2385STimur Tabi 			qe_upload_microcode(firmware, ucode);
431b8ec2385STimur Tabi 
432b8ec2385STimur Tabi 		/* Program the traps for this processor */
433b8ec2385STimur Tabi 		for (j = 0; j < 16; j++) {
434b8ec2385STimur Tabi 			u32 trap = be32_to_cpu(ucode->traps[j]);
435b8ec2385STimur Tabi 
436b8ec2385STimur Tabi 			if (trap)
437b8ec2385STimur Tabi 				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
438b8ec2385STimur Tabi 		}
439b8ec2385STimur Tabi 
440b8ec2385STimur Tabi 		/* Enable traps */
441b8ec2385STimur Tabi 		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
442b8ec2385STimur Tabi 	}
443b8ec2385STimur Tabi 
444b8ec2385STimur Tabi 	return 0;
445b8ec2385STimur Tabi }
446b8ec2385STimur Tabi 
447b8ec2385STimur Tabi struct qe_firmware_info *qe_get_firmware_info(void)
448b8ec2385STimur Tabi {
449b8ec2385STimur Tabi 	return qe_firmware_uploaded ? &qe_firmware_info : NULL;
450b8ec2385STimur Tabi }
451b8ec2385STimur Tabi 
45254841ab5SWolfgang Denk static int qe_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
453b8ec2385STimur Tabi {
454b8ec2385STimur Tabi 	ulong addr;
455b8ec2385STimur Tabi 
45647e26b1bSWolfgang Denk 	if (argc < 3)
45747e26b1bSWolfgang Denk 		return cmd_usage(cmdtp);
458b8ec2385STimur Tabi 
459b8ec2385STimur Tabi 	if (strcmp(argv[1], "fw") == 0) {
460b8ec2385STimur Tabi 		addr = simple_strtoul(argv[2], NULL, 16);
461b8ec2385STimur Tabi 
462b8ec2385STimur Tabi 		if (!addr) {
463b8ec2385STimur Tabi 			printf("Invalid address\n");
464b8ec2385STimur Tabi 			return -EINVAL;
465b8ec2385STimur Tabi 		}
466b8ec2385STimur Tabi 
467b8ec2385STimur Tabi 		/*
468b8ec2385STimur Tabi 		 * If a length was supplied, compare that with the 'length'
469b8ec2385STimur Tabi 		 * field.
470b8ec2385STimur Tabi 		 */
471b8ec2385STimur Tabi 
472b8ec2385STimur Tabi 		if (argc > 3) {
473b8ec2385STimur Tabi 			ulong length = simple_strtoul(argv[3], NULL, 16);
474b8ec2385STimur Tabi 			struct qe_firmware *firmware = (void *) addr;
475b8ec2385STimur Tabi 
476b8ec2385STimur Tabi 			if (length != be32_to_cpu(firmware->header.length)) {
477b8ec2385STimur Tabi 				printf("Length mismatch\n");
478b8ec2385STimur Tabi 				return -EINVAL;
479b8ec2385STimur Tabi 			}
480b8ec2385STimur Tabi 		}
481b8ec2385STimur Tabi 
482b8ec2385STimur Tabi 		return qe_upload_firmware((const struct qe_firmware *) addr);
483b8ec2385STimur Tabi 	}
484b8ec2385STimur Tabi 
48547e26b1bSWolfgang Denk 	return cmd_usage(cmdtp);
486b8ec2385STimur Tabi }
487b8ec2385STimur Tabi 
488b8ec2385STimur Tabi U_BOOT_CMD(
489b8ec2385STimur Tabi 	qe, 4, 0, qe_cmd,
4902fb2604dSPeter Tyser 	"QUICC Engine commands",
491b8ec2385STimur Tabi 	"fw <addr> [<length>] - Upload firmware binary at address <addr> to "
492a89c33dbSWolfgang Denk 		"the QE,\n"
493a89c33dbSWolfgang Denk 	"\twith optional length <length> verification."
494b8ec2385STimur Tabi );
495