This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM6442: Access to DDR4 controller register stalls indefinitely while in LPDDR4 frequency update

Part Number: AM6442


Dear TI team,

we've seen an issue a few months ago (~April '21) with the U-Boot driver for initializing the LPDDR4 memory on our custom hardware.

At the time we found a workaround for this, and postponed looking into this further since we expected this issue to surface again with the SK-AM64, which is using LPDDR4, too.

I began looking into this again, and at first it seemed like the issue was gone. I checked the code, and while there is support for the SK-AM64 now, there are no changes in the U-Boot code that would suggest this problem is fixed. For a while I was suspecting that a newer version of the DDR register configuration tool might have changed some setting that avoided this problem (see related thread), but I've checked with a DDR .dtsi file generated with DDR tool verison 0.08.10.00, and it still showing the same effect.

I tried to recreate the issue, and realized that it appears to be timing sensitive. I'm suspecting that this is the reason why the code "works" for the SK-AM64. The EVM is probably not affected, since the problematic code isn't used on the EVM with its DDR4 memory. I don't feel comfortable relying on the code to be "fast enough" so that it doesn't run into this issue.


With LPDDR4 memory the DDR controller and memory start at a much lower frequency FSP0. As part of the initialization sequence the frequency is switched to FSP1 and FSP2. The necessary clock is generated outside of the DDR4 controller, and the controller apparently signals via some CTRLMMR register bits the desired frequency. Software needs to read these registers, needs to reconfigure the DDRSS0 input clock, and signals completion back to the DDR controller via the CTRLMMR registers.

The code for this sequence is implemented in the U-Boot driver:

k3_lpddr4_info_handler -> k3_lpddr4_ack_freq_upd_req -> clk_set_rate

The issue that I'm seeing is that during this sequence, the R5f can stall indefinitely on the read from the LPDDR4__DRAM_CLASS__REG register that is used to determine if the current system uses a DDR4 or LPDDR4 memory. When my U-Boot is stuck, I can unfreeze it via R5F debug registers, and I can see that it is always stuck on the load from the LPDDR4__DRAM_CLASS__REG. In this situation none of the DDR controller registers appear to be accessible, and if I try to access them the core stalls. If I try reading any of these registers via CCS the debugger looses connection.

Originally we had logging and debug code enabled in U-Boot, and I'm guessing that this is what caused the problem to show. With a U-Boot compiled with little logging and no debug messages the problem doesn't show. If I halt the R5f BEFORE it starts initializing the DDR4 controller and then let it run, everything is fine. If I halt the R5f inside the k3_lpddr4_info_handler, the processors stalls once it tried to determine the type of DDR memory.

If I just put a small counting loop inside the info_handler, it depends on the delay whether the code stalls or works. If I count to 100,000 it works, if I count to 1,000,000 it fails.

I've been told that my colleagues have been seeing this issue even without any artifical delay loops - I'll need to talk to them how the U-Boot was configured at the time.

Is there any explanation why the R5f stalls indefinitely when trying to read from DDR controller registers during the FSP frequency update?

Is there any explanation why it works when the read from the DDR controller happens "soon enough", but fails when too much time passed?

Our workaround modifies the driver to cache the content of this register in a data structure, because this is the only DDR controller register that is actually read during the FSP frequency update, and the type of memory is of course fixed for the execution of the bootloader. If there's a reason why reading the registers is problematic during the FSP frequency update I believe this fix would be "correct", but the DDR controller is basically undocumented, and it is impossible for us to be certain of this.

Regards,

Dominic

  • Dominic,

    Just checking the place inside k3_lpddr4_info_handler where you see an indefinite stall is the driverdt->readreg below i colored red (copy paste is from https://git.ti.com/cgit/ti-u-boot/ti-u-boot/tree/drivers/ram/k3-ddrss/k3-ddrss.c )?  

    #define TH_OFFSET_FROM_REG(REG, SHIFT, offset) do {\
    	char *i, *pstr = xstr(REG); offset = 0;\
    	for (i = &pstr[SHIFT]; *i != '\0'; ++i) {\
    		offset = offset * 10 + (*i - '0'); } \
    	} while (0)
    
    static u32 k3_lpddr4_read_ddr_type(void)
    {
    	u32 status = 0U;
    	u32 offset = 0U;
    	u32 regval = 0U;
    	u32 dram_class = 0U;
    
    	TH_OFFSET_FROM_REG(LPDDR4__DRAM_CLASS__REG, CTL_SHIFT, offset);
    	status = driverdt->readreg(&pd, LPDDR4_CTL_REGS, offset, &regval);
    	if (status > 0U) {
    		printf("%s: Failed to read DRAM_CLASS\n", __func__);
    		hang();
    	}
    
    	dram_class = ((regval & TH_FLD_MASK(LPDDR4__DRAM_CLASS__FLD)) >>
    		TH_FLD_SHIFT(LPDDR4__DRAM_CLASS__FLD));
    	return dram_class;
    }

    Pekka

  • Hello Pekka,

    yes, that's where I'm seeing the stall. That read is performed when the LPDDR4 FSP switch is requested. Surprisingly this appears to work if the code gets there "fast enough" and fails if it is too slow, e.g. due to logging (I tried with a small delay-loop counting 1,000,000 to 0 -> fails, 100,000 to 0 -> works).

    Regards,

    Dominic

  • Hello Dominic,
    I'm passing the e2e to my colleague on his follow-up comments on your inquiry.
    Best,
    -Hong

  • Hi Dominic, can you tell me which call is getting stuck?  There are 2 in the driver below.  I believe it is probably the one in ack_freq_upd_req(), since it is around that point that the PLL to the DDR subsystem is changing and there may be a window of time where a register read from the controller is problematic.  If that is the problematic read, can you modify the code to remove that register read, and just pass the value from the initial read in k3_ddrss_init_freq().  If this is successful, I will have to submit this as a code bug

    static void k3_lpddr4_ack_freq_upd_req(void)
    {
    	u32 dram_class;
    
    	debug("--->>> LPDDR4 Initialization is in progress ... <<<---\n");
    
    	dram_class = k3_lpddr4_read_ddr_type();
    
    	switch (dram_class) {
    	case DENALI_CTL_0_DRAM_CLASS_DDR4:
    		break;
    	case DENALI_CTL_0_DRAM_CLASS_LPDDR4:
    		k3_lpddr4_freq_update();
    		break;
    	default:
    		printf("Unrecognized dram_class cannot update frequency!\n");
    	}
    }
    
    static int k3_ddrss_init_freq(struct k3_ddrss_desc *ddrss)
    {
    	u32 dram_class;
    	int ret;
    
    	dram_class = k3_lpddr4_read_ddr_type();
  • Hello James,

    it's the call from k3_lpddr4_ack_freq_upd_req. Yes, this is due to the DDR controller asking "the system" to switch to a new FSP.

    Like I wrote in my original post we have a workaround that gets rid of the read by caching the read, and that solves the immediate issue. What I don't know, since the DDR controller is basically undocumented, is whether this is "by design" and expected, or if it's a mere side effect of an underlying problem.

    The thing that I don't understand is why this read works when it happens "soon enough" and why it fails "later". E.g. if the controller is still accessible "earlier", then what happens if I switch the clock "too soon"?

    If it can be explained why our workaround is necessary we want to bring it upstream, but it's kind of difficult for me to phrase WHY this change is necessary, when the SK-AM64 apparently works without (probably because it is "fast enough", but we've only got EVMs and our custom hardware, so I can't verify that assumption).

    Regards,

    Dominic

  • Dominic, i will need to investigate this with our design team.

    Regards,

    james

  • Hi Dominic, sorry for the delay.  Unfortunately, i don't have a good answer for you yet.  But i do have a question.  The frequency update is called several times during the initialization.  Do you know if the stall always occurs on the first call, or some of the subsequent ones?  

    I can't see any reason why the R5F is stalling when accessing these registers.

    Regards,

    James

  • Hello James,

    there are mulitple frequency update steps, but these are performed in a loop inside k3_lpddr4_freq_update(). The read from the controller register happens only once, when am64_lpddr4_info_handler() calls am64_lpddr4_ack_freq_upd_req(), before it enters k3_lpddr4_freq_update().

    -> The stall occurs on the first call (there is no second call).

    The code remains in am64_lpddr4_freq_update() for a while, waiting for clock-change requests, configuring the clock, and acknowledging the change, waiting for the request to be cleared. Cycling through the FSPs is apparently a state machine that is driven from within the DDR controller.

    The actual sequence is this:

    • the cadence driver calls the am64_lpddr4_info_handler at a point where the initialization sequence has been started, but the FSP change is not yet requested by the controller
    • the k3-ddrss reads the control register to determine the type of memory (DDR4 vs. LPDDR4)
    • am64_lpddr4_freq_update waits for the FSP change requests and performs the handshakes and clock changes, without touching any of the DDR controller registers, only CTRLMMR and clocking changes.

    I've put in some debug code to measure how "long" it takes until the FSP change request is registered, by simply counting how often I can poll CTRLMMR_DDR4_FSP_CLKCHNG_REQ until bit 7 is set. That number varies greatly, between ~70 times and ~1600 times.

    If I try and read the DDR control register as long as the FSP request is not yet set, I can successfully read every once in a while (1-2 out of 10) - I guess that's plausible, since reading the FSP request register and reading from the controller can't happen atomically, so by the time I read from the DDR control register the FSP request might already be set.

    If I try and read the DDR control register (0x0f308000) AFTER the FSP change request is set, I have 100% failure rate (tried ~100 times).

    It seems that the DDR control register can't be accessed once the FSP change request is set.

    That means there's no chance that the clock switch is performed "too soon", since the code actually waits for the FSP change request bit to be set. If the code is "too slow" and the FSP frequency change request bit is already set by the controller, the DDR control register access stalls indefinitely.

    Regards,

    Dominic

  • Hi Dominic, i have discussed this with our team, and our theory here is that the PLL frequency change is temporarily stopping the clock to the DDR subsystem, and this is affecting the read from the controller register (the read is being missed by the controller since it has no clock).  Once the init sequence is started in the controller (with the write to the START bit), there is no way to determine the time when the FSP request is generated by the controller and when the PLL to the DDR subsystem is temporarily halted, so our recommendation is to not access any of the DDRSS regs once the init sequence is started until the last FSP is completed.  

    The read of the DRAM_CLASS bits would have to come before the init is started with the START bit, which i think is what your workaround is doing.

    Let me know if you need more info.  I can alert our software teams of this finding to modify the driver.

    Regards,

    James