AnsweredAssumed Answered

i.MX6UL hangs when starting LCD if IMX6UL_CLK_CAAM_ACLK clock are disabled

Question asked by Jose Diaz de Grenu de Pedro on Feb 3, 2017
Latest reply on Mar 7, 2018 by Jose Diaz de Grenu de Pedro

Hello,

 

I found out that a closed (HAB enabled) device (i.MX6UL-2) was not booting. After some debugging, the root cause seems to be enabling the LCD after the HAB has been used to authenticate images. This is what happens when reproducing the problem:

  1. U-Boot (signed) boots, starts to boot the device.
  2. During boot, U-Boot authenticates the kernel image before running it. In the process of doing so, it enables some clocks, then uses the HAB to authenticate, and the disables those clocks. (See code fragment in [1])

    Note: This creates a significant difference: In this case those clocks are disabled, but usually all clocks are enabled (See clock configuration [2])
  3. The kernel starts booting. Eventually the LCD driver loads, and then the target hangs.
    Further debugging shows that the line which makes the kernel hang is:

    writel(CTRL_RUN, host->base + LCDC_CTRL + REG_SET);

    documented as:



    Note however that the device does not hang on that instruction, but some time after that has been executed.

 

The following workarounds have been tested and avoid the problem:

  • Remove the authentication from U-Boot.
    • Then the clocks are never disabled, and all works fine.
  • Remove the LCD driver.
    • Then the RUN register of the eLCDIF interface is never set, and the device does not hang
  • Just removing the problematic line and leaving the LCD driver probe.
    • That is, commenting
      //writel(CTRL_RUN, host->base + LCDC_CTRL + REG_SET);
    • Obviously, with this 'workaround' LCD stops working
  • Enable the clocks very soon in the kernel:
    • The following patch works:
      diff --git a/arch/arm/mach-imx/clk-imx6ul.c b/arch/arm/mach-imx/clk-imx6ul.c
      index 0957ab83de57..2949e4fa70b1 100644
      --- a/arch/arm/mach-imx/clk-imx6ul.c
      +++ b/arch/arm/mach-imx/clk-imx6ul.c
      @@ -399,6 +399,18 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
      clks[IMX6UL_CLK_PWM6] = imx_clk_gate2("pwm6", "perclk", base + 0x80, 28);
      clks[IMX6UL_CLK_PWM7] = imx_clk_gate2("Pwm7", "perclk", base + 0x80, 30);

      + clk_prepare_enable(clks[IMX6UL_CLK_CAAM_MEM]);
      + clk_prepare_enable(clks[IMX6UL_CLK_CAAM_ACLK]);
      + clk_prepare_enable(clks[IMX6UL_CLK_CAAM_IPG]);
      + clk_prepare_enable(clks[IMX6UL_CLK_EIM]);
    • Enabling the clocks a bit later (for example in machine code) does not work. Even introducing delays (up to 2 seconds)

 

It seems very strange that those clocks are used somehow by the eLCDIF controller. Could the documentation be wrong and those clocks are actually others (bad register offset)? Could those clocks actually be necessary for LCD?

I would like confirmation from NXP about this.

 

Also, seems like there is a similar problem when disabling the OCOTP clocks (see Linux kernel hangs on i.MX6UL after early read of OTP registers).

 

 

Code fragments:

 

[1] U-Boot code for authenticating images:

 

uint32_t authenticate_image(uint32_t ddr_start, uint32_t image_size)
{

        (...)

         hab_caam_clock_enable(1);

         (...)

          hab_caam_clock_enable(0);

}

void hab_caam_clock_enable(unsigned char enable)
{
u32 reg;

/* CG4 ~ CG6, CAAM clocks */
reg = __raw_readl(&imx_ccm->CCGR0);
if (enable)
reg |= (MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK |
MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK |
MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK);
else
reg &= ~(MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK |
MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK |
MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK);
__raw_writel(reg, &imx_ccm->CCGR0);

/* EMI slow clk */
reg = __raw_readl(&imx_ccm->CCGR6);
if (enable)
reg |= MXC_CCM_CCGR6_EMI_SLOW_MASK;
else
reg &= ~MXC_CCM_CCGR6_EMI_SLOW_MASK;
__raw_writel(reg, &imx_ccm->CCGR6);
}

[2] By default all clocks are enabled (fragment from DCD, in U-Boot)

 

/*
* Device Configuration Data (DCD)
*
* Each entry must have the format:
* Addr-type Address Value
*
* where:
* Addr-type register length (1,2 or 4 bytes)
* Address absolute address of the register
* value value to be stored in the register
*/

/* Enable all clocks */
DATA 4 0x020c4068 0xffffffff
DATA 4 0x020c406c 0xffffffff
DATA 4 0x020c4070 0xffffffff
DATA 4 0x020c4074 0xffffffff
DATA 4 0x020c4078 0xffffffff
DATA 4 0x020c407c 0xffffffff
DATA 4 0x020c4080 0xffffffff

Outcomes