Booting M4 from Linux

Hey,

I want to boot my M4 core after Linux boots up, but I cannot start the core no matter what I do. If I have UBOOT flash and start the M4 core then I can reconfigure, re-flash, stop, start and restart the M4 from Linux using the m4fwloader. There are no issues and everything works fine.

If I try to do the very same thing but without starting the M4 via UBOOT then nothing works and I get some strange behaviors. Here is what I tried:

  1. I tried using TCML, but after I mmap and read/write to TCML then the OS hangs and becomes completely unresponsive.
  2. I though “Maybe because the M4 core is off, so is the TCML. I can use OCRAM_S”. I recompiled the code and set the linker to OCRAM_S. However after flashing nothing happens. No crash or hang, just nothing happens. I have the M4 using UARTB and theres no output. If I flash and run the same binary via UBOOT, then it works just fine.
  3. I tried regular OCRAM, but same as attempt 2.

I think I read somewhere that when Linux boots, if it sees that the M4 is not on, it disables the M4 clock. This may be the root cause and would explain everything. But I could not find anything in the IMX7DRM. I looked through all the registers that have anything to do with the M4 (probing and prodding using mmap), but I either missed it, or I am not understanding something.

Please help! Thanks.

Dear @Iosif.grigoryev

When powering up the i.MX7, the M4 is turned off. So Linux does not need to turn off any M4 clocks, they already are off.

When starting the M4 from U-Boot, the required clocks are turned on and some fix-ups on the device tree are applied, such as reserving some DRAM for communication. A typical M4 application will also reserve resources such as the second UART port, which usually leads in uncritical driver load errors while Linux is booting.
Once Linux is up and loaded the driver e.g. for this second UART, the M4 is not able anymore to access this peripheral.

In order to load your application from Linux, I recommend:

  • Load a dummy M4 application in U-Boot, which for example consists of only one single instruction: branch to itself, in order to generate an infinite loop.
    This will initialize all required clocks (in case the m4fwloader does not do this)
  • Make sure that Linux does not occupy any resources which are used by the M4 application
  • Make sure you stop the M4 CPU, before loading a new program into memory. Otherwise the M4 never stops executing code, and thus will execute your partially loaded application with unpredictable results.
  • Beside m4fwloader there is the official “Linux-way” of starting an external CPU such as the M4. The framework is called remoteproc.
    However, we never tested whether it properly supports the i.MX7.

Regards, Andy

I understand your solution, and I was about to use it as a last resort. But it still bothers me because I know that I am missing something. I know it is not a device tree collision because, as I mentioned earlier, if I have UBOOT start the M4 it and it all works great. If I want I can stop the M4, re-flash it and start it again, and it works as I expect.

You said:
“When powering up the i.MX7, the M4 is turned off. So Linux does not need to turn off any M4 clocks, they already are off.”

But in https://www.nxp.com/docs/en/application-note/AN5317.pdf it says the opposite (Last paragraph in page 2).

To test this out, here is another experiment I made. In UBOOT, I ran the command:
“mw 0x3039000C AA && run bootcmd”

This told the M4 to start, but I did not flash it at all. I assume it started and then crashed after an instruction or 2 (does not matter). Once Linux boots, I stop the M4, flash the TCML, set the Stack and PC pointers and then start the M4 and it works great.

If I not run the “mw 0x3039000C AA” command, I cannot even read/write to TCML because Linux just hangs.

Is there something else that Linux does that prevents me from reading / writing to TCML?

I figured it out!

The CCM_TARGET_ROOTn at offset 0x80 (0x30388080) is the M4 root clk gate controller (ARM_M4_CLK_ROOT). When Linux boots and sees that the M4 is off (IMX7D_SRC_M4RCR at 0x3039000C is 0xAB) then it disables the ARM_M4_CLK_ROOT.

in other words when Linx boots it does:
if ((*0x3039000C) = 0xAB) then
(*0x30388080) = 0x1000000;
end if;

By setting ARM_M4_CLK_ROOT (0x30388080) to 0x11000000 this enables the M4 root clock and sets it source. I can then write to TCML and start the M4.

Dear @Iosif.grigoryev

Clocking

You are correct about the clocking.
In the past I just noticed that I cannot hookup my JTAG debugger to the M4 core before properly starting it. I always assumed that also the clocks are not running. Now I see that it is sufficient to set the ENABLE_M4 bit and clear the W_M4C_NON_SCLR_RST bit. (As you also mentioned this is not sufficient for a proper start, as the reset vector and stack pointer are not yet configured).
According to the i.MX7 reference manual, the clock is even configured correctly to 240MHz by the boot ROM.

Linux

U-Boot already does some fixups to the device tree if the M4 is running. Setting the ENABLE_M4 bit seems to be enough to trigger this modifications.
In the boot process Linux tries to turn off all unused peripherals, so it is well possible that it disables the TCM clock (ARM_M4_CLK_ROOT) if the M4 is not enabled. Try to enable the clock gat CCGR1.

Another thing to look at is the RDC (Resource domain controller). It allows to lock particular resources to a particular domain, e.g. to the M4. This prevents accessing such a resource from any other domain. However, I don’t think this is the reason for the TCM not being accessible in your case.

Regards, Andy