armv8/fsl-lsch3: Add support to load and start MC Firmware
authorJ. German Rivera <German.Rivera@freescale.com>
Mon, 23 Jun 2014 22:15:55 +0000 (15:15 -0700)
committerAlbert ARIBAUD <albert.u.boot@aribaud.net>
Thu, 3 Jul 2014 06:40:58 +0000 (08:40 +0200)
Adding support to load and start the Layerscape Management Complex (MC)
firmware. First, the MC GCR register is set to 0 to reset all cores. MC
firmware and DPL images are copied from their location in NOR flash to
DDR. MC registers are updated with the location of these images.
Deasserting the reset bit of MC GCR register releases core 0 to run.
Core 1 will be released by MC firmware. Stop bits are not touched for
this step. U-boot waits for MC until it boots up. In case of a failure,
device tree is updated accordingly. The MC firmware image uses FIT format.

Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
Signed-off-by: York Sun <yorksun@freescale.com>
Signed-off-by: Lijun Pan <Lijun.Pan@freescale.com>
Signed-off-by: Shruti Kanetkar <Shruti@Freescale.com>
README
arch/arm/cpu/armv8/fsl-lsch3/cpu.c
drivers/net/Makefile
drivers/net/fsl_mc/Makefile [new file with mode: 0644]
drivers/net/fsl_mc/mc.c [new file with mode: 0644]
include/fdt_support.h
include/fsl_mc.h [new file with mode: 0644]

diff --git a/README b/README
index fe5cacbaa55a86450838b0cc129b538732a8daea..55c3fcdca4820794dd21e19394b91bd56a9f6131 100644 (file)
--- a/README
+++ b/README
@@ -4676,6 +4676,33 @@ within that device.
        window->master inbound window->master LAW->the ucode address in
        master's memory space.
 
+Freescale Layerscape Management Complex Firmware Support:
+---------------------------------------------------------
+The Freescale Layerscape Management Complex (MC) supports the loading of
+"firmware".
+This firmware often needs to be loaded during U-Boot booting, so macros
+are used to identify the storage device (NOR flash, SPI, etc) and the address
+within that device.
+
+- CONFIG_FSL_MC_ENET
+       Enable the MC driver for Layerscape SoCs.
+
+- CONFIG_SYS_LS_MC_FW_ADDR
+       The address in the storage device where the firmware is located.  The
+       meaning of this address depends on which CONFIG_SYS_LS_MC_FW_IN_xxx macro
+       is also specified.
+
+- CONFIG_SYS_LS_MC_FW_LENGTH
+       The maximum possible size of the firmware.  The firmware binary format
+       has a field that specifies the actual size of the firmware, but it
+       might not be possible to read any part of the firmware unless some
+       local storage is allocated to hold the entire firmware first.
+
+- CONFIG_SYS_LS_MC_FW_IN_NOR
+       Specifies that MC firmware is located in NOR flash, mapped as
+       normal addressable memory via the LBC. CONFIG_SYS_LS_MC_FW_ADDR is the
+       virtual address in NOR flash.
+
 Building the Software:
 ======================
 
index 46965f08ad3e7aeb8287c29bf4b82c7088d88e8a..c129d032f4094bc7abfb749945035e9b6009552b 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/arch-fsl-lsch3/immap_lsch3.h>
 #include "cpu.h"
 #include "speed.h"
+#include <fsl_mc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -423,3 +424,13 @@ int print_cpuinfo(void)
        return 0;
 }
 #endif
+
+int cpu_eth_init(bd_t *bis)
+{
+       int error = 0;
+
+#ifdef CONFIG_FSL_MC_ENET
+       error = mc_init(bis);
+#endif
+       return error;
+}
index 6005f7e4137bc0c5092422a1a5dbfe682c950c8a..6226cb259f8d7a92a73830b1c50889768fb2455b 100644 (file)
@@ -64,3 +64,4 @@ obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
 obj-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \
                xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
 obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o
+obj-$(CONFIG_FSL_MC_ENET) += fsl_mc/
diff --git a/drivers/net/fsl_mc/Makefile b/drivers/net/fsl_mc/Makefile
new file mode 100644 (file)
index 0000000..4834086
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Copyright 2014 Freescale Semiconductor, Inc.
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+# Layerscape MC driver
+obj-y += mc.o
diff --git a/drivers/net/fsl_mc/mc.c b/drivers/net/fsl_mc/mc.c
new file mode 100644 (file)
index 0000000..df84568
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <errno.h>
+#include <asm/io.h>
+#include <fsl_mc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+static int mc_boot_status;
+
+/**
+ * Copying MC firmware or DPL image to DDR
+ */
+static int mc_copy_image(const char *title,
+                   u64 image_addr, u32 image_size, u64 mc_ram_addr)
+{
+       debug("%s copied to address %p\n", title, (void *)mc_ram_addr);
+       memcpy((void *)mc_ram_addr, (void *)image_addr, image_size);
+       return 0;
+}
+
+/**
+ * MC firmware FIT image parser checks if the image is in FIT
+ * format, verifies integrity of the image and calculates
+ * raw image address and size values.
+ * Returns 0 if success and 1 if any of the above mentioned
+ * task fail.
+ **/
+
+int parse_mc_firmware_fit_image(const void **raw_image_addr,
+                               size_t *raw_image_size)
+{
+       int format;
+       void *fit_hdr;
+       int node_offset;
+       const void *data;
+       size_t size;
+       const char *uname = "firmware";
+
+       /* Check if the image is in NOR flash*/
+#ifdef CONFIG_SYS_LS_MC_FW_IN_NOR
+       fit_hdr = (void *)CONFIG_SYS_LS_MC_FW_ADDR;
+#else
+#error "No CONFIG_SYS_LS_MC_FW_IN_xxx defined"
+#endif
+
+       /* Check if Image is in FIT format */
+       format = genimg_get_format(fit_hdr);
+
+       if (format != IMAGE_FORMAT_FIT) {
+               debug("Not a FIT image\n");
+               return 1;
+       }
+
+       if (!fit_check_format(fit_hdr)) {
+               debug("Bad FIT image format\n");
+               return 1;
+       }
+
+       node_offset = fit_image_get_node(fit_hdr, uname);
+
+       if (node_offset < 0) {
+               debug("Can not find %s subimage\n", uname);
+               return 1;
+       }
+
+       /* Verify MC firmware image */
+       if (!(fit_image_verify(fit_hdr, node_offset))) {
+               debug("Bad MC firmware hash");
+               return 1;
+       }
+
+       /* Get address and size of raw image */
+       fit_image_get_data(fit_hdr, node_offset, &data, &size);
+
+       *raw_image_addr = data;
+       *raw_image_size = size;
+
+       return 0;
+}
+
+int mc_init(bd_t *bis)
+{
+       int error = 0;
+       int timeout = 200000;
+       struct mc_ccsr_registers __iomem *mc_ccsr_regs = MC_CCSR_BASE_ADDR;
+       u64 mc_ram_addr;
+       u64 mc_dpl_offset;
+       u32 reg_gsr;
+       u32 mc_fw_boot_status;
+       void *fdt_hdr;
+       int dpl_size;
+       const void *raw_image_addr;
+       size_t raw_image_size = 0;
+
+       BUILD_BUG_ON(CONFIG_SYS_LS_MC_FW_LENGTH % 4 != 0);
+
+       /*
+        * The MC private DRAM block was already carved at the end of DRAM
+        * by board_init_f() using CONFIG_SYS_MEM_TOP_HIDE:
+        */
+       if (gd->bd->bi_dram[1].start) {
+               mc_ram_addr =
+                       gd->bd->bi_dram[1].start + gd->bd->bi_dram[1].size;
+       } else {
+               mc_ram_addr =
+                       gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size;
+       }
+
+       /*
+        * Management Complex cores should be held at reset out of POR.
+        * U-boot should be the first software to touch MC. To be safe,
+        * we reset all cores again by setting GCR1 to 0. It doesn't do
+        * anything if they are held at reset. After we setup the firmware
+        * we kick off MC by deasserting the reset bit for core 0, and
+        * deasserting the reset bits for Command Portal Managers.
+        * The stop bits are not touched here. They are used to stop the
+        * cores when they are active. Setting stop bits doesn't stop the
+        * cores from fetching instructions when they are released from
+        * reset.
+        */
+       out_le32(&mc_ccsr_regs->reg_gcr1, 0);
+       dmb();
+
+       error = parse_mc_firmware_fit_image(&raw_image_addr, &raw_image_size);
+       if (error != 0)
+               goto out;
+       /*
+        * Load the MC FW at the beginning of the MC private DRAM block:
+        */
+       mc_copy_image(
+               "MC Firmware",
+               (u64)raw_image_addr,
+               raw_image_size,
+               mc_ram_addr);
+
+       /*
+        * Calculate offset in the MC private DRAM block at which the MC DPL
+        * blob is to be placed:
+        */
+#ifdef CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET
+       BUILD_BUG_ON(
+               (CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET & 0x3) != 0 ||
+               CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET > 0xffffffff);
+
+       mc_dpl_offset = CONFIG_SYS_LS_MC_DRAM_DPL_OFFSET;
+#else
+       mc_dpl_offset = mc_get_dram_block_size() -
+                       roundup(CONFIG_SYS_LS_MC_DPL_LENGTH, 4096);
+
+       if ((mc_dpl_offset & 0x3) != 0 || mc_dpl_offset > 0xffffffff) {
+               printf("%s: Invalid MC DPL offset: %llu\n",
+                      __func__, mc_dpl_offset);
+               error = -EINVAL;
+               goto out;
+       }
+#endif
+
+       /* Check if DPL image is in NOR flash */
+#ifdef CONFIG_SYS_LS_MC_DPL_IN_NOR
+       fdt_hdr = (void *)CONFIG_SYS_LS_MC_DPL_ADDR;
+#else
+#error "No CONFIG_SYS_LS_MC_DPL_IN_xxx defined"
+#endif
+
+       dpl_size = fdt_totalsize(fdt_hdr);
+
+       /*
+        * Load the MC DPL blob at the far end of the MC private DRAM block:
+        */
+       mc_copy_image(
+               "MC DPL blob",
+               (u64)fdt_hdr,
+               dpl_size,
+               mc_ram_addr + mc_dpl_offset);
+
+       debug("mc_ccsr_regs %p\n", mc_ccsr_regs);
+
+       /*
+        * Tell MC where the MC Firmware image was loaded in DDR:
+        */
+       out_le32(&mc_ccsr_regs->reg_mcfbalr, (u32)mc_ram_addr);
+       out_le32(&mc_ccsr_regs->reg_mcfbahr, (u32)((u64)mc_ram_addr >> 32));
+       out_le32(&mc_ccsr_regs->reg_mcfapr, MCFAPR_BYPASS_ICID_MASK);
+
+       /*
+        * Tell MC where the DPL blob was loaded in DDR, by indicating
+        * its offset relative to the beginning of the DDR block
+        * allocated to the MC firmware. The MC firmware is responsible
+        * for checking that there is no overlap between the DPL blob
+        * and the runtime heap and stack of the MC firmware itself.
+        *
+        * NOTE: bits [31:2] of this offset need to be stored in bits [29:0] of
+        * the GSR MC CCSR register. So, this offset is assumed to be 4-byte
+        * aligned.
+        * Care must be taken not to write 1s into bits 31 and 30 of the GSR in
+        * this case as the SoC COP or PIC will be signaled.
+        */
+       out_le32(&mc_ccsr_regs->reg_gsr, (u32)(mc_dpl_offset >> 2));
+
+       /*
+        * Deassert reset and release MC core 0 to run
+        */
+       out_le32(&mc_ccsr_regs->reg_gcr1, GCR1_P1_DE_RST | GCR1_M_ALL_DE_RST);
+       dmb();
+       debug("Polling mc_ccsr_regs->reg_gsr ...\n");
+
+       for (;;) {
+               reg_gsr = in_le32(&mc_ccsr_regs->reg_gsr);
+               mc_fw_boot_status = (reg_gsr & GSR_FS_MASK);
+               if (mc_fw_boot_status & 0x1)
+                       break;
+
+               udelay(1000);   /* throttle polling */
+               if (timeout-- <= 0)
+                       break;
+       }
+
+       if (timeout <= 0) {
+               printf("%s: timeout booting management complex firmware\n",
+                      __func__);
+
+               /* TODO: Get an error status from an MC CCSR register */
+               error = -ETIMEDOUT;
+               goto out;
+       }
+
+       printf("Management complex booted (boot status: %#x)\n",
+              mc_fw_boot_status);
+
+       if (mc_fw_boot_status != 0x1) {
+               /*
+                * TODO: Identify critical errors from the GSR register's FS
+                * field and for those errors, set error to -ENODEV or other
+                * appropriate errno, so that the status property is set to
+                * failure in the fsl,dprc device tree node.
+                */
+       }
+
+out:
+       if (error != 0)
+               mc_boot_status = -error;
+       else
+               mc_boot_status = 0;
+
+       return error;
+}
+
+int get_mc_boot_status(void)
+{
+       return mc_boot_status;
+}
+
+/**
+ * Return the actual size of the MC private DRAM block.
+ *
+ * NOTE: For now this function always returns the minimum required size,
+ * However, in the future, the actual size may be obtained from an environment
+ * variable.
+ */
+unsigned long mc_get_dram_block_size(void)
+{
+       return CONFIG_SYS_LS_MC_DRAM_BLOCK_MIN_SIZE;
+}
index 21d7b44453286cbf78904f4620cb4fdc172fd2e6..fd44d7e2f6495227bbcc34e9ece430921082e4da 100644 (file)
@@ -113,17 +113,25 @@ static inline int fdt_status_disabled(void *fdt, int nodeoffset)
 {
        return fdt_set_node_status(fdt, nodeoffset, FDT_STATUS_DISABLED, 0);
 }
+static inline int fdt_status_fail(void *fdt, int nodeoffset)
+{
+       return fdt_set_node_status(fdt, nodeoffset, FDT_STATUS_FAIL, 0);
+}
 
-int fdt_set_status_by_alias(void *fdt, const charalias,
+int fdt_set_status_by_alias(void *fdt, const char *alias,
                            enum fdt_status status, unsigned int error_code);
-static inline int fdt_status_okay_by_alias(void *fdt, const charalias)
+static inline int fdt_status_okay_by_alias(void *fdt, const char *alias)
 {
        return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_OKAY, 0);
 }
-static inline int fdt_status_disabled_by_alias(void *fdt, const charalias)
+static inline int fdt_status_disabled_by_alias(void *fdt, const char *alias)
 {
        return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_DISABLED, 0);
 }
+static inline int fdt_status_fail_by_alias(void *fdt, const char *alias)
+{
+       return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_FAIL, 0);
+}
 
 #endif /* ifdef CONFIG_OF_LIBFDT */
 
diff --git a/include/fsl_mc.h b/include/fsl_mc.h
new file mode 100644 (file)
index 0000000..b9f089e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __FSL_MC_H__
+#define __FSL_MC_H__
+
+#include <common.h>
+
+#define MC_CCSR_BASE_ADDR \
+       ((struct mc_ccsr_registers __iomem *)0x8340000)
+
+#define BIT(x)                 (1 << (x))
+#define GCR1_P1_STOP           BIT(31)
+#define GCR1_P2_STOP           BIT(30)
+#define GCR1_P1_DE_RST         BIT(23)
+#define GCR1_P2_DE_RST         BIT(22)
+#define GCR1_M1_DE_RST         BIT(15)
+#define GCR1_M2_DE_RST         BIT(14)
+#define GCR1_M_ALL_DE_RST      (GCR1_M1_DE_RST | GCR1_M2_DE_RST)
+#define GSR_FS_MASK            0x3fffffff
+#define MCFAPR_PL_MASK         (0x1 << 18)
+#define MCFAPR_BMT_MASK                (0x1 << 17)
+#define MCFAPR_BYPASS_ICID_MASK        \
+       (MCFAPR_PL_MASK | MCFAPR_BMT_MASK)
+
+#define SOC_MC_PORTALS_BASE_ADDR    ((void __iomem *)0x00080C000000)
+#define SOC_MC_PORTAL_STRIDE       0x10000
+
+#define SOC_MC_PORTAL_ADDR(_portal_id) \
+       ((void __iomem *)((uintptr_t)SOC_MC_PORTALS_BASE_ADDR + \
+        (_portal_id) * SOC_MC_PORTAL_STRIDE))
+
+struct mc_ccsr_registers {
+       u32 reg_gcr1;
+       u32 reserved1;
+       u32 reg_gsr;
+       u32 reserved2;
+       u32 reg_sicbalr;
+       u32 reg_sicbahr;
+       u32 reg_sicapr;
+       u32 reserved3;
+       u32 reg_mcfbalr;
+       u32 reg_mcfbahr;
+       u32 reg_mcfapr;
+       u32 reserved4[0x2f1];
+       u32 reg_psr;
+       u32 reserved5;
+       u32 reg_brr[2];
+       u32 reserved6[0x80];
+       u32 reg_error[];
+};
+
+int mc_init(bd_t *bis);
+
+int get_mc_boot_status(void);
+#endif