All of lore.kernel.org
 help / color / mirror / Atom feed
From: Timur Tabi <timur@freescale.com>
To: u-boot@lists.denx.de
Subject: [U-Boot-Users] [PATCH v2] qe: add ability to upload QE firmware
Date: Mon, 10 Dec 2007 14:39:24 -0600	[thread overview]
Message-ID: <119731916487-git-send-email-timur@freescale.com> (raw)

Define the layout of a binary blob that contains a QE firmware and instructions
on how to upload it.  Add function qe_upload_firmware() to parse the blob and
perform the actual upload.  Add command-line command "qe fw" to take a firmware
blob in memory and upload it.  Update ft_cpu_setup() on 83xx to create the
'firmware' device tree node if U-Boot has uploaded a firmware.  Fully define
'struct rsp' in immap_qe.h to include the actual RISC Special Registers.

Signed-off-by: Timur Tabi <timur@freescale.com>
---
 cpu/mpc83xx/cpu.c          |   53 +++++++++++
 drivers/qe/qe.c            |  219 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/qe/qe.h            |   56 +++++++++++
 include/asm-ppc/immap_qe.h |   35 +++++++-
 4 files changed, 360 insertions(+), 3 deletions(-)

diff --git a/cpu/mpc83xx/cpu.c b/cpu/mpc83xx/cpu.c
index b2c35d3..b098982 100644
--- a/cpu/mpc83xx/cpu.c
+++ b/cpu/mpc83xx/cpu.c
@@ -36,6 +36,9 @@
 #elif defined(CONFIG_OF_LIBFDT)
 #include <libfdt.h>
 #endif
+#ifdef CONFIG_QE
+#include "../../drivers/qe/qe.h"
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -412,6 +415,38 @@ static int fdt_set_qe_brgfreq(void *blob, int nodeoffset, const char *name, bd_t
 	tmp = cpu_to_be32(gd->brg_clk);
 	return fdt_setprop(blob, nodeoffset, name, &tmp, sizeof(tmp));
 }
+
+/*
+ * If a QE firmware has been uploaded, then add the 'firmware' node under
+ * the 'qe' node.
+ */
+static int fdt_set_qe_firmware(void *blob, int nodeoffset, const char *name, bd_t *bd)
+{
+	struct qe_firmware_info *qe_fw_info;
+	int ret;
+
+	qe_fw_info = qe_get_firmware_info();
+
+	if (qe_fw_info) {
+                /* We assume the node doesn't exist yet */
+		nodeoffset = fdt_add_subnode(blob, nodeoffset, name);
+		if (nodeoffset < 0)
+			return nodeoffset;
+		ret = fdt_setprop_typed(blob, nodeoffset, "extended_modes",
+			qe_fw_info->extended_modes);
+		if (ret < 0)
+			return ret;
+
+		ret = fdt_setprop_string(blob, nodeoffset, "id", qe_fw_info->id);
+		if (ret < 0)
+			return ret;
+
+		return fdt_setprop(blob, nodeoffset, "virtual_traps",
+			qe_fw_info->vtraps, sizeof(qe_fw_info->vtraps));
+	}
+
+	return 0;
+}
 #endif
 
 /*
@@ -558,6 +593,24 @@ ft_cpu_setup(void *blob, bd_t *bd)
 		tmp[1] = cpu_to_be32(bd->bi_memsize);
 		fdt_setprop(blob, nodeoffset, "reg", tmp, sizeof(tmp));
 	}
+
+        /* We can't call fdt_set_qe_firmware() from the fixup_props[] table
+         * because the relocation bug causes fdt_setprop() to return
+         * FDT_ERR_NOSPACE if called with the return value of fdt_add_subnode().
+         * So we need to call it directly here.  This problem should go away
+         * when the 83xx relocation code is changed to match 85xx.
+         */
+#ifdef CONFIG_QE
+	nodeoffset = fdt_find_node_by_path(blob, "/" OF_QE);
+	if (nodeoffset >= 0) {
+		err = fdt_set_qe_firmware(blob, nodeoffset, "firmware", NULL);
+		if (err < 0) {
+			debug("Could not add qe/firmware node: %s\n",
+				fdt_strerror(err));
+			fdt_del_node(blob, nodeoffset);
+		}
+	}
+#endif
 }
 #elif defined(CONFIG_OF_FLAT_TREE)
 void
diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c
index 7559e92..276788c 100644
--- a/drivers/qe/qe.c
+++ b/drivers/qe/qe.c
@@ -21,6 +21,7 @@
  */
 
 #include "common.h"
+#include <command.h>
 #include "asm/errno.h"
 #include "asm/io.h"
 #include "asm/immap_qe.h"
@@ -248,4 +249,222 @@ int qe_set_mii_clk_src(int ucc_num)
 	return 0;
 }
 
+/* The maximum number of RISCs we support */
+#define MAX_QE_RISC     2
+
+/* Firmware information stored here for qe_get_firmware_info() */
+static struct qe_firmware_info qe_firmware_info;
+
+/*
+ * Set to 1 if QE firmware has been uploaded, and therefore
+ * qe_firmware_info contains valid data.
+ */
+static int qe_firmware_uploaded;
+
+/*
+ * Upload a QE microcode
+ *
+ * This function is a worker function for qe_upload_firmware().  It does
+ * the actual uploading of the microcode.
+ */
+static void qe_upload_microcode(const void *base,
+	const struct qe_microcode *ucode)
+{
+	const u32 *code = base + be32_to_cpu(ucode->code_offset);
+	unsigned int i;
+
+	if (ucode->major || ucode->minor || ucode->revision)
+		printf("QE: uploading microcode '%s' version %u.%u.%u\n",
+			ucode->id, ucode->major, ucode->minor, ucode->revision);
+	else
+		printf("QE: uploading microcode '%s'\n", ucode->id);
+
+	/* Use auto-increment */
+	out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) |
+		QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR);
+
+	for (i = 0; i < be32_to_cpu(ucode->count); i++)
+		out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i]));
+}
+
+/*
+ * Upload a microcode to the I-RAM at a specific address.
+ *
+ * See docs/README.qe_firmware for information on QE microcode uploading.
+ *
+ * Currently, only version 1 is supported, so the 'version' field must be
+ * set to 1.
+ *
+ * The SOC model and revision are not validated, they are only displayed for
+ * informational purposes.
+ *
+ * 'calc_size' is the calculated size, in bytes, of the firmware structure and
+ * all of the microcode structures, minus the CRC.
+ *
+ * 'length' is the size that the structure says it is, including the CRC.
+ */
+int qe_upload_firmware(const struct qe_firmware *firmware)
+{
+	unsigned int i;
+	unsigned int j;
+	u32 crc;
+	size_t calc_size = sizeof(struct qe_firmware);
+	size_t length;
+	const struct qe_header *hdr;
+
+	if (!firmware) {
+		printf("Invalid address\n");
+		return -EINVAL;
+	}
+
+	hdr = &firmware->header;
+	length = be32_to_cpu(hdr->length);
+
+	/* Check the magic */
+	if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
+	    (hdr->magic[2] != 'F')) {
+		printf("Not a microcode\n");
+		return -EPERM;
+	}
+
+	/* Check the version */
+	if (hdr->version != 1) {
+		printf("Unsupported version\n");
+		return -EPERM;
+	}
+
+	/* Validate some of the fields */
+	if ((firmware->count < 1) || (firmware->count >= MAX_QE_RISC)) {
+		printf("Invalid data\n");
+		return -EINVAL;
+	}
+
+	/* Validate the length and check if there's a CRC */
+	calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
+
+	for (i = 0; i < firmware->count; i++)
+		/*
+		 * For situations where the second RISC uses the same microcode
+		 * as the first, the 'code_offset' and 'count' fields will be
+		 * zero, so it's okay to add those.
+		 */
+		calc_size += sizeof(u32) *
+			be32_to_cpu(firmware->microcode[i].count);
+
+	/* Validate the length */
+	if (length != calc_size + sizeof(u32)) {
+		printf("Invalid length\n");
+		return -EPERM;
+	}
+
+        /*
+         * Validate the CRC.  We would normally call crc32_no_comp(), but that
+         * function isn't available unless you turn on JFFS support.
+         */
+	crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
+	if (crc != (crc32(-1, (const void *) firmware, calc_size) ^ -1)) {
+		printf("Firmware CRC is invalid\n");
+		return -EIO;
+	}
+
+	/*
+	 * If the microcode calls for it, split the I-RAM.
+	 */
+	if (!firmware->split) {
+		out_be16(&qe_immr->cp.cercr,
+			in_be16(&qe_immr->cp.cercr) | QE_CP_CERCR_CIR);
+	}
+
+	if (firmware->soc.model)
+		printf("Firmware '%s' for %u V%u.%u\n",
+			firmware->id, be16_to_cpu(firmware->soc.model),
+			firmware->soc.major, firmware->soc.minor);
+	else
+		printf("Firmware '%s'\n", firmware->id);
+
+	/*
+	 * The QE only supports one microcode per RISC, so clear out all the
+	 * saved microcode information and put in the new.
+	 */
+	memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
+	strcpy(qe_firmware_info.id, firmware->id);
+	qe_firmware_info.extended_modes = firmware->extended_modes;
+	memcpy(qe_firmware_info.vtraps, firmware->vtraps,
+		sizeof(firmware->vtraps));
+	qe_firmware_uploaded = 1;
+
+	/* Loop through each microcode. */
+	for (i = 0; i < firmware->count; i++) {
+		const struct qe_microcode *ucode = &firmware->microcode[i];
+
+		/* Upload a microcode if it's present */
+		if (ucode->code_offset)
+			qe_upload_microcode(firmware, ucode);
+
+		/* Program the traps for this processor */
+		for (j = 0; j < 16; j++) {
+			u32 trap = be32_to_cpu(ucode->traps[j]);
+
+			if (trap)
+				out_be32(&qe_immr->rsp[i].tibcr[j], trap);
+		}
+
+		/* Enable traps */
+		out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr));
+	}
+
+	return 0;
+}
+
+struct qe_firmware_info *qe_get_firmware_info(void)
+{
+	return qe_firmware_uploaded ? &qe_firmware_info : NULL;
+}
+
+static int qe_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	ulong addr;
+
+	if (argc < 3) {
+		printf ("Usage:\n%s\n", cmdtp->usage);
+		return 1;
+	}
+
+	if (strcmp(argv[1], "fw") == 0) {
+		addr = simple_strtoul(argv[2], NULL, 16);
+
+		if (!addr) {
+			printf("Invalid address\n");
+			return -EINVAL;
+		}
+
+                /*
+                 * If a length was supplied, compare that with the 'length'
+                 * field.
+                 */
+
+		if (argc > 3) {
+			ulong length = simple_strtoul(argv[3], NULL, 16);
+			struct qe_firmware *firmware = (void *) addr;
+
+			if (length != be32_to_cpu(firmware->header.length)) {
+				printf("Length mismatch\n");
+				return -EINVAL;
+			}
+		}
+
+		return qe_upload_firmware((const struct qe_firmware *) addr);
+	}
+
+	printf ("Usage:\n%s\n", cmdtp->usage);
+	return 1;
+}
+
+U_BOOT_CMD(
+	qe, 4, 0, qe_cmd,
+	"qe      - QUICC Engine commands\n",
+	"fw <addr> [<length>] - Upload firmware binary at address <addr> to "
+		"the QE,\n\twith optional length <length> verification.\n"
+	);
+
 #endif /* CONFIG_QE */
diff --git a/drivers/qe/qe.h b/drivers/qe/qe.h
index 400b1a6..4c96c67 100644
--- a/drivers/qe/qe.h
+++ b/drivers/qe/qe.h
@@ -222,6 +222,60 @@ typedef enum qe_clock {
 
 #define QE_SDEBCR_BA_MASK		0x01FFFFFF
 
+/* Communication Processor */
+#define QE_CP_CERCR_MEE		0x8000	/* Multi-user RAM ECC enable */
+#define QE_CP_CERCR_IEE		0x4000	/* Instruction RAM ECC enable */
+#define QE_CP_CERCR_CIR		0x0800	/* Common instruction RAM */
+
+/* I-RAM */
+#define QE_IRAM_IADD_AIE	0x80000000	/* Auto Increment Enable */
+#define QE_IRAM_IADD_BADDR	0x00080000	/* Base Address */
+
+/* Structure that defines QE firmware binary files.
+ *
+ * See doc/README.qe_firmware for a description of these fields.
+ */
+struct qe_firmware {
+	struct qe_header {
+		u32 length;  /* Length of the entire structure, in bytes */
+		u8 magic[3];    /* Set to { 'Q', 'E', 'F' } */
+		u8 version;     /* Version of this layout. First ver is '1' */
+	} header;
+	u8 id[62];      /* Null-terminated identifier string */
+	u8 split;	/* 0 = shared I-RAM, 1 = split I-RAM */
+	u8 count;       /* Number of microcode[] structures */
+	struct {
+		u16 model;   	/* The SOC model  */
+		u8 major;       	/* The SOC revision major */
+		u8 minor;       	/* The SOC revision minor */
+	} __attribute__ ((packed)) soc;
+	u8 padding[4];			/* Reserved, for alignment */
+	u64 extended_modes;		/* Extended modes */
+	u32 vtraps[8];		/* Virtual trap addresses */
+	u8 reserved[4];			/* Reserved, for future expansion */
+	struct qe_microcode {
+		u8 id[32];      	/* Null-terminated identifier */
+		u32 traps[16];       /* Trap addresses, 0 == ignore */
+		u32 eccr;    	/* The value for the ECCR register */
+		u32 iram_offset;     /* Offset into I-RAM for the code */
+		u32 count;   	/* Number of 32-bit words of the code */
+		u32 code_offset;     /* Offset of the actual microcode */
+		u8 major;       	/* The microcode version major */
+		u8 minor;       	/* The microcode version minor */
+		u8 revision;		/* The microcode version revision */
+		u8 padding;		/* Reserved, for alignment */
+		u8 reserved[4];		/* Reserved, for future expansion */
+	} __attribute__ ((packed)) microcode[1];
+	/* All microcode binaries should be located here */
+	/* CRC32 should be located here, after the microcode binaries */
+} __attribute__ ((packed));
+
+struct qe_firmware_info {
+	char id[64];		/* Firmware name */
+	u32 vtraps[8];		/* Virtual trap addresses */
+	u64 extended_modes;	/* Extended modes */
+};
+
 void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign);
 void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data);
 uint qe_muram_alloc(uint size, uint align);
@@ -233,5 +287,7 @@ void qe_reset(void);
 void qe_assign_page(uint snum, uint para_ram_base);
 int qe_set_brg(uint brg, uint rate);
 int qe_set_mii_clk_src(int ucc_num);
+int qe_upload_firmware(const struct qe_firmware *firmware);
+struct qe_firmware_info *qe_get_firmware_info(void);
 
 #endif /* __QE_H__ */
diff --git a/include/asm-ppc/immap_qe.h b/include/asm-ppc/immap_qe.h
index a16a6d3..bec54aa 100644
--- a/include/asm-ppc/immap_qe.h
+++ b/include/asm-ppc/immap_qe.h
@@ -513,10 +513,39 @@ typedef struct dbg {
 	u8 res2[0x48];
 } __attribute__ ((packed)) dbg_t;
 
-/* RISC Special Registers (Trap and Breakpoint)
-*/
+/*
+ * RISC Special Registers (Trap and Breakpoint).  These are described in
+ * the QE Developer's Handbook.
+ */
 typedef struct rsp {
-	u8 fixme[0x100];
+	u32 tibcr[16];	/* Trap/instruction breakpoint control regs */
+	u8 res0[64];
+	u32 ibcr0;
+	u32 ibs0;
+	u32 ibcnr0;
+	u8 res1[4];
+	u32 ibcr1;
+	u32 ibs1;
+	u32 ibcnr1;
+	u32 npcr;
+	u32 dbcr;
+	u32 dbar;
+	u32 dbamr;
+	u32 dbsr;
+	u32 dbcnr;
+	u8 res2[12];
+	u32 dbdr_h;
+	u32 dbdr_l;
+	u32 dbdmr_h;
+	u32 dbdmr_l;
+	u32 bsr;
+	u32 bor;
+	u32 bior;
+	u8 res3[4];
+	u32 iatr[4];
+	u32 eccr;		/* Exception control configuration register */
+	u32 eicr;
+	u8 res4[0x100-0xf8];
 } __attribute__ ((packed)) rsp_t;
 
 typedef struct qe_immap {
-- 
1.5.2.4

             reply	other threads:[~2007-12-10 20:39 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-12-10 20:39 Timur Tabi [this message]
2007-12-12 15:16 ` [U-Boot-Users] [PATCH v2] qe: add ability to upload QE firmware Timur Tabi
2007-12-12 21:42   ` Kim Phillips
  -- strict thread matches above, loose matches on Subject: below --
2007-11-30 23:01 Timur Tabi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=119731916487-git-send-email-timur@freescale.com \
    --to=timur@freescale.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.