[U-Boot-Users] [PATCH] 85xx: add ability to upload QE firmware

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 ---
Tested on an MPC8568 MDS. This patch applies on top of Kumar's mpc85xx branch.
common/fdt_support.c | 48 ++++++++++ cpu/mpc85xx/fdt.c | 1 + drivers/qe/qe.c | 219 ++++++++++++++++++++++++++++++++++++++++++++ drivers/qe/qe.h | 56 +++++++++++ include/asm-ppc/immap_qe.h | 33 ++++++- include/fdt_support.h | 1 + 6 files changed, 356 insertions(+), 2 deletions(-)
diff --git a/common/fdt_support.c b/common/fdt_support.c index b5ee6e9..17a9da4 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -30,6 +30,9 @@ #include <fdt_support.h> #include <exports.h>
+#ifdef CONFIG_QE +#include "../drivers/qe/qe.h" +#endif /* * Global data (for the gd->bd) */ @@ -607,4 +610,49 @@ void fdt_fixup_ethernet(void *fdt, bd_t *bd) #endif } } + +#ifdef CONFIG_QE +/* + * If a QE firmware has been uploaded, then add the 'firmware' node under + * the 'qe' node. + */ +void fdt_fixup_qe_firmware(void *fdt) +{ + struct qe_firmware_info *qe_fw_info; + int node, ret; + + qe_fw_info = qe_get_firmware_info(); + if (!qe_fw_info) + return; + + node = fdt_path_offset(fdt, "/qe"); + if (node < 0) + return; + + /* We assume the node doesn't exist yet */ + node = fdt_add_subnode(fdt, node, "firmware"); + if (node < 0) + return; + + ret = fdt_setprop(fdt, node, "extended_modes", + &qe_fw_info->extended_modes, sizeof(u64)); + if (ret < 0) + goto error; + + ret = fdt_setprop_string(fdt, node, "id", qe_fw_info->id); + if (ret < 0) + goto error; + + ret = fdt_setprop(fdt, node, "virtual_traps", qe_fw_info->vtraps, + sizeof(qe_fw_info->vtraps)); + if (ret < 0) + goto error; + + return; + +error: + fdt_del_node(fdt, node); +} +#endif + #endif diff --git a/cpu/mpc85xx/fdt.c b/cpu/mpc85xx/fdt.c index 737a6c4..0812c89 100644 --- a/cpu/mpc85xx/fdt.c +++ b/cpu/mpc85xx/fdt.c @@ -45,6 +45,7 @@ void ft_cpu_setup(void *blob, bd_t *bd) #ifdef CONFIG_QE do_fixup_by_prop_u32(blob, "device_type", "soc", 4, "bus-frequency", bd->bi_busfreq, 1); + fdt_fixup_qe_firmware(blob); #endif
#ifdef CFG_NS16550 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..39da377 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 { diff --git a/include/fdt_support.h b/include/fdt_support.h index 3d6c1a8..859eaa8 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -46,6 +46,7 @@ void do_fixup_by_compat_u32(void *fdt, const char *compat, const char *prop, u32 val, int create); int fdt_fixup_memory(void *blob, u64 start, u64 size); void fdt_fixup_ethernet(void *fdt, bd_t *bd); +void fdt_fixup_qe_firmware(void *fdt);
#ifdef CONFIG_OF_HAS_UBOOT_ENV int fdt_env(void *fdt);

In message 11976719892795-git-send-email-timur@freescale.com you wrote:
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
I'm not sure if such firmware shouldbe part of the device tree, or rather a separate image or part of the image. Actually I think the definition of such a blob, if needed at all, should only be a tamporary workaround unti we have the new image format in place. Then this should become part of the new image implementation.
Please follow the ongoing discussions of this topic and make sure to voice your wishes and requirements.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
I'm not sure if such firmware shouldbe part of the device tree, or rather a separate image or part of the image.
The firmware itself is not part of the device tree, just a few pieces of information about the firmware. I need some way to pass information from U-Boot to the kernel, and my choices are the device tree or command line. The command line is clunky, and so I thought the device tree is best place. This design decision has already been reviewed (and approved by Arnd) on the kernel mailing lists.
The firmware blob needs to be a separate image because the code and firmware layout is meant to be cross-platform, so I can't use any U-Boot-specific wrapper. In addition, the firmware itself is not GPL, so I can't merge it with GPL code, or anything else for that matter. It needs to exist as an independent entity. The code which uploads the blob into the QE is GPL, of course.
Actually I think the definition of such a blob, if needed at all, should only be a tamporary workaround unti we have the new image format in place. Then this should become part of the new image implementation.
The design and layout of the firmware blob is specific to the needs of the QE. I don't see how a new image format will help. I'll only use the new image format to wrap the binary.
The goal is to have this firmware blob sitting in flash somewhere, all I need to do to upload it is:
qe fw $fwaddr
The ability to upload firmware to the QE in U-Boot is needed by QE microcode developers.
Please follow the ongoing discussions of this topic and make sure to voice your wishes and requirements.
Are you talking about "RFC: New U-boot image format"? If so, then I just don't see how that applies. I don't want a new binary format, because I already have one defined that works. If you want me to add support for my binary blob wrapped in some other kind of image format, I can add that *after* this patch has been applied, since the ability to accept these binary blobs as-is is necessary.

In message 476696D3.90803@freescale.com you wrote:
Please follow the ongoing discussions of this topic and make sure to voice your wishes and requirements.
Are you talking about "RFC: New U-boot image format"? If so, then I just don't
Yes, I do.
see how that applies. I don't want a new binary format, because I already have one defined that works. If you want me to add support for my binary blob wrapped in some other kind of image format, I can add that *after* this patch has been applied, since the ability to accept these binary blobs as-is is necessary.
Think about the situation that someone whats to use DHCP to boot his board, which will usually result in the TFTP download of exactly *one* image from a TFTP server. That image should then contain the Linux kernel, the dtb, the QE firmware image, eventually a ramdisk, and maybe even more... That's what the new image format is about.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
Think about the situation that someone whats to use DHCP to boot his board, which will usually result in the TFTP download of exactly *one* image from a TFTP server. That image should then contain the Linux kernel, the dtb, the QE firmware image, eventually a ramdisk, and maybe even more... That's what the new image format is about.
Ok, I can appreciate that, but how is my QE firmware blob any different from any other generic binary that this new image format must support? And since this new format is still a long way from being implemented, I don't see why it should delay adding support for QE firmware.
The "qe fw" command requires an address, and it takes care of the rest. As long as this new image format can identify the QE firmware within it and pass the address of the blob to the "qe fw" command, I don't really care about the rest.

In message 4766D29F.7090707@freescale.com you wrote:
Think about the situation that someone whats to use DHCP to boot his board, which will usually result in the TFTP download of exactly *one* image from a TFTP server. That image should then contain the Linux kernel, the dtb, the QE firmware image, eventually a ramdisk, and maybe even more... That's what the new image format is about.
Ok, I can appreciate that, but how is my QE firmware blob any different from any other generic binary that this new image format must support? And since this
It's just another type of payload to wrap and to recognize.
new format is still a long way from being implemented, I don't see why it should delay adding support for QE firmware.
Oh, that's not a long way. Probaly counted in weeks rather than months.
The "qe fw" command requires an address, and it takes care of the rest. As long
Yes, but how do you get this address from a multi-file image format?
as this new image format can identify the QE firmware within it and pass the address of the blob to the "qe fw" command, I don't really care about the rest.
I see.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
The "qe fw" command requires an address, and it takes care of the rest. As long
Yes, but how do you get this address from a multi-file image format?
Hmmmm. I think the easiest way would be to for the multi-file image format processing code to identify the QE firmware blob and set the '$qefwaddr' environment variable accordingly.
I'll add my two cents to the multi-image format thread.

In message 4767E365.3080900@freescale.com you wrote:
The "qe fw" command requires an address, and it takes care of the rest. As long
Yes, but how do you get this address from a multi-file image format?
Hmmmm. I think the easiest way would be to for the multi-file image format processing code to identify the QE firmware blob and set the '$qefwaddr' environment variable accordingly.
It will not do that. Booting a multifile image is a different procedure.
I'll add my two cents to the multi-image format thread.
Yes, please.
Best regards,
Wolfgang Denk

Timur Tabi wrote:
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
Slight typo -- this should say "85xx" not "83xx". Andy, please correct if/when you apply this patch.
participants (2)
-
Timur Tabi
-
Wolfgang Denk