DEV Community

DEBIX Industrial Computers
DEBIX Industrial Computers

Posted on

DEBIX Uboot: Enabling M7 Core and Resource Sharing with Linux

① Obtain M7 Firmware

  1. Obtain the firmware through the IAR project

  2. Obtain the firmware through the ARMCC

For details about how to obtain the firmware, see the documentation: https://cache.nxp.com.cn/secured/assets/documents/en/user-guide/MCUXSDKGSUG.pdf?__gda__=1663319159_a1de3ef22ab76fd8f1f3f83ee9c1b0b3&fileExt=.pdf.

Take hello_world project as an example, please prepare the hello_world.bin file.

② Uboot Starts the M7 Core1. Modify imx8mp-evk.dts device tree

  1. Modify imx8mp-evk.dts device tree
--- /arch/arm64/boot/dts/freescale/imx8mp-evk.dts

+++ /arch/arm64/boot/dts/freescale/imx8mp-evk.dts

@@ -15,6 +15,55 @@

chosen {

stdout-path = &uart2;

};

+   reserved-memory {

+   #address-cells = <2>;

+   #size-cells = <2>;

+   ranges;

+

+   m4_reserved: m4@0x80000000 {

+   no-map;

+   reg = <0 0x80000000 0 0x1000000>;

+   };

+

+   vdev0vring0: vdev0vring0@55000000 {

+   reg = <0 0x55000000 0 0x8000>;

+   no-map;

+   };

+

+   vdev0vring1: vdev0vring1@55008000 {

+   reg = <0 0x55008000 0 0x8000>;

+   no-map;

+   };

+

+   vdevbuffer: vdevbuffer@55400000 {

+   compatible = "shared-dma-pool";

+   reg = <0 0x55400000 0 0x100000>;

+   no-map;

+   };

+

+   rsc_table: rsc_table@550ff000 {

+   reg = <0 0x550ff000 0 0x1000>;

+   no-map;

+   };

+   };

+

+//edit by wei

+   imx8mp-cm7 {

+   compatible = "fsl,imx8mp-cm7";

+   rsc-da = <0x55000000>;

+   clocks = <&clk IMX8MP_CLK_M7_DIV>;

+   mbox-names = "tx", "rx", "rxdb";

+   mboxes = <&mu 0 1

+    &mu 1 1

+    &mu 3 1>;

+   memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>;

+   status = "okay";

+   };

gpio-leds {

compatible = "gpio-leds";

@@ -318,6 +367,7 @@

};

};

+

&flexspi {

pinctrl-names = "default";

pinctrl-0 = <&pinctrl_flexspi0>;

@@ -569,7 +619,7 @@

clock-frequency = <400000>;

pinctrl-names = "default";

pinctrl-0 = <&pinctrl_i2c3>;

-   status = "okay";

+   status = "disabled";

#if 1

codec: es8316@10 {

@@ -828,7 +878,7 @@

<&clk IMX8MP_CLK_DUMMY>;

clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3";

fsl,sai-mclk-direction-output;

-   status = "okay";

+   status = "disabled";

};

#if 0

@@ -867,10 +917,11 @@

status = "okay";

};

+//edit for wei

&uart4 {

-   pinctrl-names = "default";

-   pinctrl-0 = <&pinctrl_uart4>;

-   status = "okay";

+// pinctrl-names = "default";

+// pinctrl-0 = <&pinctrl_uart4>;

+   status = "disabled";

};

&usb3_phy0 {

@@ -1229,13 +1280,13 @@

MX8MP_IOMUXC_UART3_TXD__UART3_DCE_TX    0x49

>;

};

-

-   pinctrl_uart4: uart4grp {

-   fsl,pins = <

-   MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX    0x49

-   MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX    0x49

-   >;

-   };

+//edit by wei

+   // pinctrl_uart4: uart4grp {

+   // fsl,pins = <

+   // MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX 0x49

+   // MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX 0x49

+   // >;

+   // };

pinctrl_usb1_vbus: usb1grp {

fsl,pins = <
Enter fullscreen mode Exit fullscreen mode

2.Modify device clock clk-imx8mp.c

--- /drivers/clk/imx/clk-imx8mp.c

+++ /drivers/clk/imx/clk-imx8mp.c

@@ -728,6 +728,9 @@

check_m4_enabled();

+   if (of_find_compatible_node(NULL, NULL, "fsl,imx8mp-cm7"))

+   set_cm4_enable(true);

+

np = of_find_compatible_node(NULL, NULL, "fsl,imx8mp-anatop");

anatop_base = of_iomap(np, 0);

of_node_put(np);
Enter fullscreen mode Exit fullscreen mode
  1. Modify device driver of the M7 coreimx_rproc.c
--- /drivers/remoteproc/imx_rproc.c

+++ /drivers/remoteproc/imx_rproc.c

@@ -25,6 +25,11 @@

#include "remoteproc_internal.h"

+//add by wei

+#define TCML_ADDR_IMX8M    0x7e0000

+#define TCML_ADDR_IMX8_CM4_1   0x34fe0000

+#define TCML_ADDR_IMX8_CM4_2   0x38fe0000

+

#define IMX7D_SRC_SCR   0x0C

#define IMX7D_ENABLE_M4 BIT(3)

#define IMX7D_SW_M4P_RST    BIT(2)

@@ -131,10 +136,37 @@

int num_domains;

struct device   **pm_devices;

struct device_link  **pm_devices_link;

+   //add by wei

+   u32 m_core_ddr_addr;

+   u32 last_load_addr;

+   u32 m4_start_addr;

};

static struct imx_sc_ipc *ipc_handle;

+// add by wei

+static const struct imx_rproc_att imx_rproc_att_imx8mp[] = {

+   /* dev addr , sys addr  , size     , flags */

+   /* ITCM   */

+   {0x00000000, 0x007E0000, 0x00020000, ATT_OWN},

+   /* OCRAM_S */

+   {0x00180000, 0x00180000, 0x00009000, 0},

+   /* OCRAM */

+   {0x00900000, 0x00900000, 0x00090000, 0},

+   /* QSPI Code - alias */

+   {0x08000000, 0x08000000, 0x08000000, 0},

+   /* DDR (Code) - alias */

+   {0x10000000, 0x40000000, 0x0FFE0000, 0},

+   /* DTCM */

+   {0x20000000, 0x00800000, 0x00020000, ATT_OWN},

+   /* OCRAM_S - alias */

+   {0x20180000, 0x00180000, 0x00008000, ATT_OWN},

+   /* OCRAM */

+   {0x20200000, 0x00900000, 0x00090000, ATT_OWN},

+   /* DDR (Data) */

+   {0x40000000, 0x40000000, 0x80000000, 0},

+};

+

static const struct imx_rproc_att imx_rproc_att_imx8qm[] = {

/* dev addr , sys addr  , size     , flags */

{ 0x08000000, 0x08000000, 0x10000000, 0},

@@ -284,6 +316,13 @@

.method = IMX_ARM_SMCCC,

};

+static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mp = {

+   .att = imx_rproc_att_imx8mp,

+   .att_size = ARRAY_SIZE(imx_rproc_att_imx8mp),

+   .elf_mem_hook = true,

+   .method = IMX_ARM_SMCCC,

+};

+

static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = {

.src_reg    = IMX7D_SRC_SCR,

.src_mask   = IMX7D_M4_RST_MASK,

@@ -422,6 +461,54 @@

priv->txdb_ch = NULL;

}

+/*

+"M4 always start up from TCM. The SCU will copy the first 32 bytes of the binary to TCM

+ if the start address is not TCM. The TCM region [0x1FFE0000-0x1FFE001F] is reserved for this purpose."

+ Therefore:

+ For imx8q and imx8x, it is not necessary to copy the stack pointer and reset vector from ddr to tcm.

+ Instead, determine if firmware was loaded into TCM or DDR and provide correct start address to SCU.

+ Like 8M family, DDR4 address is defined in device tree node m4_reserved, m7_reserved, or mcore_reserved

+*/

+static void imx8_set_start_addr(struct rproc *rproc, u32 addr_tcm) {

+   struct imx_rproc *priv = rproc->priv;

+

+   if(priv->m_core_ddr_addr && priv->last_load_addr >= priv->m_core_ddr_addr) {

+   priv->m4_start_addr = priv->m_core_ddr_addr;

+   dev_info(priv->dev, "Setting Cortex M4 start address to DDR 0x%08x\n", priv->m4_start_addr);

+   } else {

+   priv->m4_start_addr = addr_tcm;

+   dev_info(priv->dev, "Setting Cortex M4 start address to TCM 0x%08x\n", priv->m4_start_addr);

+   }

+}

+

+/*

+ Stack pointer and reset vector must be initialized

+*/

+static void imx_8m_setup_stack(struct rproc *rproc)

+{

+   struct imx_rproc *priv = rproc->priv;

+   void __iomem *io_tcml = ioremap(TCML_ADDR_IMX8M, 8);

+

+   //initialize tcml stack pointer and reset vector

+   if(priv->m_core_ddr_addr && priv->last_load_addr >= priv->m_core_ddr_addr) {

+   void __iomem *io_ddr = ioremap(priv->m_core_ddr_addr, 8);

+   dev_info(priv->dev, "Setting up stack pointer and reset vector from firmware in DDR\n");

+   writel(readl(io_ddr), io_tcml);

+   writel(readl(io_ddr + 4), io_tcml + 4);

+   iounmap(io_ddr);

+   } else {

+   dev_info(priv->dev, "Setting up stack pointer and reset vector from firmware in TCML\n");

+   writel(readl(io_tcml), io_tcml);

+   writel(readl(io_tcml + 4), io_tcml + 4);

+   }

+

+   dev_info(priv->dev, "Stack: 0x%x\n", readl(io_tcml));

+   dev_info(priv->dev, "Reset Vector: 0x%x\n", readl(io_tcml + 4));

+

+   iounmap(io_tcml);

+}

+

+

static int imx_rproc_start(struct rproc *rproc)

{

struct imx_rproc *priv = rproc->priv;

@@ -432,10 +519,12 @@

switch (dcfg->method) {

case IMX_DIRECT_MMIO:

+   imx_8m_setup_stack(rproc);

ret = regmap_update_bits(priv->regmap, dcfg->src_reg,

dcfg->src_mask, dcfg->src_start);

break;

case IMX_ARM_SMCCC:

+   imx_8m_setup_stack(rproc);

arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_M4_START, 0, 0, 0, 0, 0, 0, &res);

ret = res.a0;

break;

@@ -449,12 +538,15 @@

return imx_rproc_ready(rproc);

}

-   if (priv->id == 1)

-   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, true, 0x38fe0000);

-   else if (!priv->id)

-   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, true, 0x34fe0000);

-   else

+   if (priv->id == 1) {

+   imx8_set_start_addr(rproc, TCML_ADDR_IMX8_CM4_2);

+   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, true, priv->m4_start_addr);

+   } else if (!priv->id) {

+   imx8_set_start_addr(rproc, TCML_ADDR_IMX8_CM4_1);

+   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, true, priv->m4_start_addr);

+   } else {

ret = -EINVAL;

+   }

break;

case IMX_IPC_ONLY:

return -ENOTSUPP;

@@ -512,9 +604,9 @@

break;

case IMX_IPC_ONLY:

return -ENOTSUPP;

@@ -512,9 +604,9 @@

break;

case IMX_SCU_API:

if (priv->id == 1)

-   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, false, 0x38fe0000);

+   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, false, priv->m4_start_addr);

else if (!priv->id)

-   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, false, 0x34fe0000);

+   ret = imx_sc_pm_cpu_start(ipc_handle, priv->rsrc, false, priv->m4_start_addr);

else

ret = -EINVAL;

break;

@@ -677,6 +769,13 @@

else

return -ENOMEM;

+   //get m4/m7 ddr address from device tree

+   if(0 == strcmp(it.node->name, "m4") || 0 == strcmp(it.node->name, "m7")

+   || 0 == strcmp(it.node->name, "m_core")) {

+   priv->m_core_ddr_addr = rmem->base;

+   dev_info(priv->dev, "%s ddr @ 0x%x\n", it.node->name, (u32) rmem->base);

+   }

+

rproc_add_carveout(rproc, mem);

index++;

}

@@ -724,6 +823,21 @@

spin_unlock_irqrestore(&priv->mu_lock, flags);

}

+static u64 imx_rproc_elf_get_boot_addr(struct rproc *rproc,

+         const struct firmware *fw)

+{

+   struct imx_rproc *priv = rproc->priv;

+

+

+   if (!priv->early_boot) {

+   //save the location of the last firmware load for start function

+   priv->last_load_addr = rproc_elf_get_boot_addr(rproc, fw);

+   return (u64)priv->last_load_addr;

+   }

+

+   return 0;

+}

+

static void imx_rproc_kick(struct rproc *rproc, int vqid)

{

struct imx_rproc *priv = rproc->priv;

@@ -788,7 +902,7 @@

.parse_fw   = imx_rproc_parse_fw,

.find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,

.sanity_check   = rproc_elf_sanity_check,

-   .get_boot_addr  = rproc_elf_get_boot_addr,

+   .get_boot_addr  = imx_rproc_elf_get_boot_addr,

};

static int imx_rproc_addr_init(struct imx_rproc *priv,

@@ -986,6 +1100,19 @@

return 0;

}


+static int imx_rproc_parse_dt(struct device *dev, struct imx_rproc *priv)

+{

+   int ret = 0;

+

+   if (priv->dcfg->method == IMX_SCU_API) {

+   ret = of_property_read_u32(dev->of_node, "core-index", &priv->id);

+   if (ret)

+   dev_err(dev, "No reg <core index id>\n");

+   }

+

+   return ret;

+}

+

static int imx_rproc_detect_mode(struct imx_rproc *priv)

{

const struct imx_rproc_dcfg *dcfg = priv->dcfg;

@@ -1020,12 +1147,6 @@

dev_err(dev, "No reg <core resource id>\n");

return ret;

}

-   ret = of_property_read_u32(dev->of_node, "core-index", &priv->id);

-   if (ret) {

-   dev_err(dev, "No reg <core index id>\n");

-   return ret;

-   }

-

priv->proc_nb.notifier_call = imx_rproc_partition_notify;

@@ -1143,6 +1264,8 @@

priv->rproc = rproc;

priv->dcfg = dcfg;

priv->dev = dev;

+   priv->m_core_ddr_addr = 0;

+   priv->last_load_addr = 0;

if (dcfg->method == IMX_DIRECT_MMIO) {

regmap = syscon_regmap_lookup_by_phandle(np, "syscon");

@@ -1164,6 +1287,10 @@

/* mbox is optional, so not fail here */

}

+   ret = imx_rproc_parse_dt(dev, priv);

+   if (ret)

+   goto err_put_mbox;

+

ret = imx_rproc_addr_init(priv, pdev);

if (ret) {

dev_err(dev, "failed on imx_rproc_addr_init\n");

@@ -1223,6 +1350,14 @@

{

struct rproc *rproc = platform_get_drvdata(pdev);

struct imx_rproc *priv = rproc->priv;

+   if (!IS_ERR(priv->txdb_ch))

+   mbox_free_channel(priv->txdb_ch);

+   if (!IS_ERR(priv->rxdb_ch))

+   mbox_free_channel(priv->rxdb_ch);

+   if (!IS_ERR(priv->tx_ch))

+   mbox_free_channel(priv->tx_ch);

+   if (!IS_ERR(priv->rx_ch))

+   mbox_free_channel(priv->rx_ch);

if (!priv->early_boot)

clk_disable_unprepare(priv->clk);

@@ -1239,7 +1374,7 @@

{ .compatible = "fsl,imx8mq-cm4", .data = &imx_rproc_cfg_imx8mq },

{ .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq },

{ .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn },

-   { .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn },

+   { .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mp },

{ .compatible = "fsl,imx8qxp-cm4", .data = &imx_rproc_cfg_imx8qxp },

{ .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm },

{},

Enter fullscreen mode Exit fullscreen mode

remoteproc_core.c

--- /drivers/remoteproc/remoteproc_core.c

+++ /drivers/remoteproc/remoteproc_core.c

@@ -1114,6 +1114,14 @@

if (!rproc->table_ptr)

return 0;

+   //add by wei

+

+   if ((int)rproc->table_ptr->num < 0) {

+   WARN_ON(rproc->table_ptr->num);

+   rproc->table_ptr->num=0;

+   return 0;

+   }

+

for (i = 0; i < rproc->table_ptr->num; i++) {

int offset = rproc->table_ptr->offset[i];

struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;

@@ -1373,7 +1381,7 @@

* that any subsequent changes will be applied to the loaded version.

*/

loaded_table = rproc_find_loaded_rsc_table(rproc, fw);

-   if (loaded_table) {

+   if (loaded_table && rproc->cached_table) {

memcpy(loaded_table, rproc->cached_table, rproc->table_sz);

rproc->table_ptr = loaded_table;

}
Enter fullscreen mode Exit fullscreen mode

remoteproc_elf_loader.c

--- /drivers/remoteproc/remoteproc_elf_loader.c

+++ /drivers/remoteproc/remoteproc_elf_loader.c

@@ -133,16 +133,16 @@

{

if (!rproc->ops->elf_memcpy)

memcpy(dest, src, count);

-

-   rproc->ops->elf_memcpy(rproc, dest, src, count);

+   else

+   rproc->ops->elf_memcpy(rproc, dest, src, count);

}

static void rproc_elf_memset(struct rproc *rproc, void *s, int c, size_t count)

{

if (!rproc->ops->elf_memset)

memset(s, c, count);

-

-   rproc->ops->elf_memset(rproc, s, c, count);

+   else

+   rproc->ops->elf_memset(rproc, s, c, count);

}
Enter fullscreen mode Exit fullscreen mode
  1. Copy the File

Copy the file hello_world.bin to the Micro SD card in the same partition with the image and device tree.

  1. Hardware Connection

Connect UART2 and UART4 to PC, set the baud rate as 115200. Please refer to the figure below:

  1. Start the M7 Core

Go to the uboot CLI and run the following command:

fatload mmc 1:1 0x48000000 hello_world.bin;cp.b 0x48000000 0x7e0000 20000;  
bootaux 0x7e0000
Enter fullscreen mode Exit fullscreen mode

You can see that UART4S prints hello world on the serial port terminal. This indicates that the M7 core is working properly.

After executing the bootcmd command, if you can enter the system normally, it means that the A53 core and M7 can work simultaneously.

  1. uboot Quick Start

Go to the uboot CLI and run the following command:

setenv m7_image hello_world.bin

setenv m7_loadaddr 0x7e0000

setenv m7_copyaddr 0x96000000

setenv m7_loadimage "fatload mmc '${mmcdev}':'${mmcpart}' '${m7_copyaddr}' '${m7_image}'; cp.b '${m7_copyaddr}' '${m7_loadaddr}' 0x20000"

setenv run_m7_image "run m7_loadimage; dcache flush; bootaux '${m7_loadaddr}'"  
Enter fullscreen mode Exit fullscreen mode

After you enter the uboot, you can type the run run_m7_image command to directly start the M7 kernel.

③ Develop the M7 Core Firmware in Debix1.Obtain SDK Source Code

1.Obtain SDK Source Code

https://mcuxpresso.nxp.com/en/download?hash=502a027cddc304d8e16cc44319a90811&uvid=455793&dl=1&js=1&to_vault=true

Download the SDK resource package and copy the SDK package to the Debix board directory:

Create a folder M7_SDK and unzip the SDK package into this folder M7_SDK:

mkdir M7_SDK
unzip SDK_2_12_0_EVK-MIMX8MP.zip -d M7_SD
Enter fullscreen mode Exit fullscreen mode

2.Install cmake

①Install cmake:

sudo apt install cmake

②Replace the dynamic link library, otherwise during later the cmake compilation, it will prompt you that libcurl.so.4 is not found:

sudo rm /usr/lib/libcurl.so.4
sudo ln -s /usr/lib/libcurl.so.4.7.0 /usr/local/lib/libcurl.so.4
Enter fullscreen mode Exit fullscreen mode

Use ls -l /usr/local/lib/libcurl.so.4 to see if the dynamic link library is installed successfully:

3.Obtain gcc-arm-none-eabi Cross Tool Chain

Enter the directory M7_SDK/, get the tool chain and configure the environment variables:

wget https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-aarch64-linux.tar.bz2
tar -xvf gcc-arm-none-eabi-9-2019-q4-major-aarch64-linux.tar.bz2
Enter fullscreen mode Exit fullscreen mode

Edit .bashrc file, configure the environment variables, and add the following lines to the last line:

sudo vi ~/.bashrc
export ARMGCC_DIR=~/M7_SDK/gcc-arm-none-eabi-9-2019-q4-major
source ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

4.Compile Firmware

Enter the armcc directory of the hello_world project:

cd ~/M7_SDK/boards/evkmimx8mp/demo_apps/hello_world/armgcc/

Run the build_all.sh script, it will generate these folders below in the current directory. Usually use the release version:

./build_all.sh

5.Use the FirmwareCreate a root directory in the M7_SDK directory, mount partition 1 of the SD card to this directory, and then copy the hello_world.bin file compiled in the previous step to this directory:

cd ~/M7_SDK

mkdir root && cd root

sudo cp -f ../boards/evkmimx8mp/demo_apps/hello_world/armgcc/release/hello_world.bin ./
Enter fullscreen mode Exit fullscreen mode

Reset Debix and execute the run run_m7_image command in uboot to load the new firmware running the M7 core.

④ Burn the M7 Core Firmware to Debix and Start It

  1. Prepare the firmwareDuring the third compile step, the resulting release folder will have a firmware for hello_world.elf.
  2. Check the M7 core statusSwitch to the root user and modify the kernel log printing level.

sysctl kernel.printk=7;

Check the M7 core status:
cat /sys/class/remoteproc/remoteproc0/state

If the M7 core is started in uboot, it needs to be turned off first:
echo stop > /sys/class/remoteproc/remoteproc0/state

3.Burning firmwarel
echo/home/debix/M7_SDK/boards/evkmimx8mp/demo_apps/hello_world/armgcc/release/hello_world.elf > /sys/class/remoteproc/remoteproc0/firmware

4.Start the M7 core
echo start > /sys/class/remoteproc/remoteproc0/state

Check the serial port. The following output indicates that the M7 core has started normally, and at this point, serial port 4 will output log information.

Top comments (0)