qe: add ability to upload QE firmware

Timur Tabi timur at freescale.com
Fri Oct 19 01:08:25 EST 2007


Define the layout of a binary blob that contains a QE firmware and instructions
on how to upload it.  Add function qe_upload_microcode() to parse the blob
and perform the actual upload.  Fully define 'struct rsp' in immap_qe.h to
include the actual RISC Special Registers.

Signed-off-by: Timur Tabi <timur at freescale.com>
---

This patch applies on top of my previous patches
"qe: add function qe_clock_source" and
"ucc_geth: use rx-clock-name and tx-clock-name device tree properties".

This patch defines a new specification for a QE binary file.  Please review
the specification as well as the code.

 arch/powerpc/sysdev/qe_lib/qe.c |   96 +++++++++++++++++++++++++++++
 include/asm-powerpc/immap_qe.h  |   29 ++++++++-
 include/asm-powerpc/qe.h        |  128 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 251 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 8551e74..7ca398c 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
+#include <linux/crc32.h>
 #include <asm/irq.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -393,3 +394,98 @@ void *qe_muram_addr(unsigned long offset)
 	return (void *)&qe_immr->muram[offset];
 }
 EXPORT_SYMBOL(qe_muram_addr);
+
+/*
+ * Download a microcode to the I-RAM at a specific address.
+ *
+ * @firmware: pointer to qe_firmware structure
+ */
+int qe_upload_microcode(const struct qe_firmware *firmware)
+{
+	unsigned int i;
+	unsigned int j;
+	__be32 *code;
+	u32 crc;
+	size_t length = sizeof(struct qe_firmware);
+
+	/* Check the magic and version number */
+	if ((firmware->magic[0] != 'Q') || (firmware->magic[1] != 'E') ||
+	    (firmware->magic[2] != 'F') || (firmware->version != 1)) {
+		printk(KERN_ERR "QE: invalid or unsupported microcode\n");
+		return -EPERM;
+	}
+
+	/* Validate the length and check if there's a CRC */
+	for (i = 0; i < firmware->count; i++)
+		length += firmware->microcode[i].count * 4;
+
+	if (firmware->length == (length + sizeof(u32))) {
+		/* Length is valid, and there's a CRC */
+		crc = be32_to_cpu(*((__be32 *) ((void *) firmware + length)));
+		if (crc != crc32(0, firmware, length)) {
+			printk(KERN_ERR "QE: firmware CRC is invalid\n");
+			return -EIO;
+		}
+	} else if (firmware->length != length) {
+		printk(KERN_ERR "QE: invalid length(s) in firware structure\n");
+		return -EPERM;
+	}
+
+	/* If there's only one microcode, then we assume it's common for all
+	   RISCs, so we set the CERCR.CIR bit to share the IRAM with all RISCs.
+	   This should be safe even on SOCs with only one RISC.
+
+	   If there are multiple 'microcode' structures, but each one points
+	   to the same microcode binary (ignoring offsets), then we also assume
+	   that we want share RAM.
+	 */
+	if (firmware->count == 1)
+	      setbits16(&qe_immr->cp.cercr, cpu_to_be16(0x800));
+	else {
+		for (i = 1; i < firmware->count; i++)
+			if (firmware->microcode[i].code_offset !=
+			    firmware->microcode[0].code_offset)
+				break;
+
+		if (i == firmware->count)
+			setbits16(&qe_immr->cp.cercr, cpu_to_be16(0x800));
+	}
+
+	if (firmware->soc.model)
+		printk(KERN_INFO "QE: uploading microcode '%s' for %u V%u.%u\n",
+			firmware->id, be16_to_cpu(firmware->soc.model),
+			firmware->soc.rev_h, firmware->soc.rev_l);
+	else
+		printk(KERN_INFO "QE: uploading microcode '%s'\n",
+			firmware->id);
+
+	for (i = 0; i < firmware->count; i++) {
+		const struct qe_microcode *ucode = &firmware->microcode[i];
+
+		code = (void *) firmware + be32_to_cpu(ucode->code_offset);
+
+		/* Use auto-increment */
+		out_be32(&qe_immr->iram.iadd, cpu_to_be32(0x80080000 |
+			be32_to_cpu(ucode->iram_offset)));
+
+		for (j = 0; j < ucode->count; j++)
+			out_be32(&qe_immr->iram.idata, be32_to_cpu(code[j]));
+
+		/* Program the traps.  We program both RISCs, even on platforms
+		   that only have one.  This *should* be safe. */
+		for (j = 0; j < 16; j++)
+			if (ucode->traps[j]) {
+				u32 trap = be32_to_cpu(ucode->traps[j]);
+				out_be32(&qe_immr->rsp[0].tibcr[j], trap);
+				out_be32(&qe_immr->rsp[1].tibcr[j], trap);
+			}
+	}
+
+	/* Enable the traps for both RISCs.  Again, on a single-RISC system,
+	   this should be safe. */
+	out_be32(&qe_immr->rsp[0].eccr, cpu_to_be32(0x20800000));
+	out_be32(&qe_immr->rsp[1].eccr, cpu_to_be32(0x20800000));
+
+	return 0;
+}
+EXPORT_SYMBOL(qe_upload_microcode);
diff --git a/include/asm-powerpc/immap_qe.h b/include/asm-powerpc/immap_qe.h
index aba9806..d0c60c4 100644
--- a/include/asm-powerpc/immap_qe.h
+++ b/include/asm-powerpc/immap_qe.h
@@ -395,8 +395,33 @@ struct dbg {
 
 /* RISC Special Registers (Trap and Breakpoint) */
 struct rsp {
-	u32	reg[0x40];	/* 64 32-bit registers */
-} __attribute__ ((packed));
+	__be32 tibcr[16];
+	u8 res0[64];
+	__be32 ibcr0;
+	__be32 ibs0;
+	__be32 ibcnr0;
+	u8 res1[4];
+	__be32 ibcr1;
+	__be32 ibs1;
+	__be32 ibcnr1;
+	__be32 npcr;
+	__be32 dbcr;
+	__be32 dbar;
+	__be32 dbamr;
+	__be32 dbsr;
+	__be32 dbcnr;
+	u8 res2[12];
+	__be32 dbdr_h;
+	__be32 dbdr_l;
+	__be32 dbdmr_h;
+	__be32 dbdmr_l;
+	__be32 bsr;
+	__be32 bor;
+	u8 res3[24];
+	__be32 eccr;
+	__be32 eicr;
+	u8 res4[0x100-0xf8];
+};
 
 struct qe_immap {
 	struct qe_iram		iram;		/* I-RAM */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index 81403ee..f773280 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -94,6 +94,134 @@ unsigned long qe_muram_alloc_fixed(unsigned long offset, int size);
 void qe_muram_dump(void);
 void *qe_muram_addr(unsigned long offset);
 
+/* Structure that defines QE firmware binary files.
+ *
+ * All integers are big-endian.  All lengths are in bytes.
+ *
+ * The 'length' field is the size, in bytes, of the entire structure,
+ * including all the microcode embedded in it, as well as the CRC (if
+ * present).
+ *
+ * The 'magic' field is an array of three bytes that contains the letters
+ * 'Q', 'E', and 'F'.  This is an identifier that indicates that this
+ * structure is a QE Firmware structure.
+ *
+ * The 'version' field is a single byte that indicates the version of this
+ * structure.  If the layout of the structure should ever need to be changed
+ * to add support for additional types of microcode, then the version number
+ * should also be changed.  Currently, only version 1 (this version) is
+ * supported, so this field must be set to 1.
+ *
+ * The 'id' field is a null-terminated string suitable for printing.
+ *
+ * The 'count' field indicates the number of 'microcode' structures.  If
+ * there are multiple RISC processors, then it's possible to have a
+ * different microcode for each one.  This allows for three possible
+ * scenarios:
+ *
+ *      1) Single microcode common to all RISCs
+ *      2) Single microcode copied to multiple offsets, one per RISC
+ *      3) Different microcode and traps for each RISC
+ *
+ * In scenarious (1) and (2), the CERCR.CIR bit is set to 1, which allows
+ * all of IRAM to be shared among all RISC processors.
+ *
+ * The 'soc' structure contains the SOC numbers and revisions used to match
+ * the microcode to the SOC itself.  Normally, the microcode loader should
+ * check the data in this structure with the SOC number and revisions, and
+ * only upload the microcode if there's a match.  However, because there is
+ * no generic way to obtain the SOC model and revision, this check is not
+ * currently made.
+ *
+ * Although it is not recommended, you can specify '0' in the soc.model
+ * field to skip matching SOCs altogether.
+ *
+ * The 'model' field is a 16-bit number that matches the actual SOC. The
+ * 'rev_h' and 'rev_l' fields match the high and low bytes of the SOC
+ * revision ID.
+ *
+ * For example, to match the 8323, revision 1.0:
+ *      soc.model = 8323
+ *      soc.rev_h = 1
+ *      soc.rev_h = 0
+ *
+ * 'reserved' is neccessary for structure alignment.  This field ensures
+ * that the first element of the 'microcode' array is aligned on a 64-bit
+ * boundary.
+ *
+ * 'microcode' (type: struct qe_microcode):
+ *      'traps' is an array of 16 words that contain hardware trap values
+ *      for each of the 16 traps.  If trap[i] is 0, then this particular
+ *      trap is to be ignored (i.e. not written to TIBCR[i]).
+ *
+ *      'vtraps' is an array of 8 words that contain virtual trap values for
+ *      each virtual traps.  Currently, this field is ignored.
+ *
+ *      'extended_modes' is a bitfield that defines special functionality
+ *      which has an impact on the software drivers.  Each bit has its own
+ *      impact and has special instructions for the driver associated with
+ *      it.  Currently, this field is ignored.
+ *
+ *      'iram_offset' is the offset into IRAM to start writing the
+ *      microcode.
+ *
+ *      'count' is the number of 32-bit words in the microcode.
+ *
+ *      'code_offset' is the offset, in bytes, from the beginning of this
+ *      structure where the microcode itself can be found.  The first
+ *      microcode binary should be located immediately after the 'microcode'
+ *      array.
+ *
+ *      'reserved' is necessary for structure alignment.  Since 'microcode'
+ *      is an array, the 64-bit 'extended_modes' field needs to be aligned
+ *      on a 64-bit boundary, and this can only happen if the size of
+ *      'microcode' is a multiple of 8 bytes.  To ensure that, we add
+ *      'reserved'.
+ *
+ * In addition, an optional 32-bit CRC can be added after the last
+ * microcode binary.  The CRC is checked via the crc32() Linux kernel macro.
+ * It can be calculated using this algorithm:
+ *
+ * u32 crc32(const u8 *p, unsigned int len)
+ * {
+ *         unsigned int i;
+ *         u32 crc = 0;
+ *
+ *         while (len--) {
+ *      	   crc ^= *p++;
+ *      	   for (i = 0; i < 8; i++)
+ *      		   crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
+ *         }
+ *         return crc;
+ * }
+ */
+struct qe_firmware {
+	__be32 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' */
+	u8 id[63];      /* Null-terminated identifier string */
+	u8 count;       /* Number of microcode[] structures */
+	struct {
+		__be16 model;   	/* The SOC model  */
+		u8 rev_h;       	/* The SOC revision */
+		u8 rev_l;       	/* The SOC revision */
+	} soc;
+	__be32 reserved;		/* Reserved, for alignement */
+	struct qe_microcode {
+		__be32 traps[16];       /* Trap addresses, 0 == ignore */
+		__be32 vtraps[8];       /* Virtual trap addresses */
+		__be64 extended_modes;  /* Extended modes */
+		__be32 iram_offset;     /* Offset into I-RAM for the code */
+		__be32 count;   	/* Number of 32-bit words of the code */
+		__be32 code_offset;     /* Offset of the actual microcode */
+		__be32 reserved;	/* Reserved, for alignement */
+	} microcode[1]; 		/* At least one microcode */
+	/* All microcode binaries should be located here */
+	/* An optional CRC32 can be added after the microcode binaries here */
+} __attribute__ ((packed));
+
+int qe_upload_microcode(const struct qe_firmware *firmware);
+
 /* Buffer descriptors */
 struct qe_bd {
 	__be16 status;
-- 
1.5.2.4




More information about the Linuxppc-dev mailing list