i.MX: SDMA not working? Strange things happen? Maybe it’s all about power management.

This post was written by eli on July 6, 2014
Posted Under: ARM,Freescale,Linux kernel

I ran into a weird problem while attempting to enable SDMA for UARTs on an i.MX53 processor running Freescale’s 2.6.35.3 Linux kernel: To begin with, the UART would only transmit 48 bytes, which is probably a result of only one watermark event arriving (the initial kickoff filled the UART’s FIFO with 32 bytes, and then one SDMA event occurred when the FIFO reached 16 bytes’ fill, so another 16 bytes were sent).

So it seemed like the SDMA core misses the UART’s watermark events. More scrutinized experiments with my own test scripts revealed a variety of weird behaviors, including what appeared to be preemption of the SDMA script’s process, even though the reference manual is quite clear about it: Context switching of SDMA scripts is voluntary. And still, the flow of data on the UART’s tx lines was stopped for 5-6 ms periods randomly, even when I ran a busy-wait loop in the SDMA script, polling the “not full” flag of the UART’s transmission FIFO.

So it looked like something stopped the SDMA script from running in the middle of the loop (which included no “yield” nor “done” command). Or maybe a completely different issue? Maybe the peripheral bus wasn’t completely coherent? Anything seemed possible at some point.

As the title implies, the problem was power management, and poor settings of the SDMA’s behavior during low power modes.

It goes like this: Every time the Linux kernel’s scheduler has no process to run, it executes an WFI ARM processor command, halting the processor until an interrupt arrives (from a peripheral or just the scheduler’s tick clock). But before doing that, the kernel calls an architecture-dependent function, arch_idle(), which possibly shuts down or slows down clocks in order to increase power savings.

The kernel I used didn’t configure the SDMA’s behavior in the lower-power WAIT mode correctly, causing it halt and miss events while the processor was in this mode. The word is that to overcome this, the CCM_CCGR bits for SDMA clocks should be set to 11 (bits 31-30 in CCM_CCGR4). There is probably also a need to enable aips_tz1_clk to keep the SDMA and aips_tz1 clocks running. But since the application I worked on didn’t have any power restrictions, I decided to avoid these power mode switches altogether.

This was done by editing arch/arm/mach-mx5/system.c in the kernel tree, where it said:

void arch_idle(void)
{
 if (likely(!mxc_jtag_enabled)) {
   if (ddr_clk == NULL)
     ddr_clk = clk_get(NULL, "ddr_clk");
   if (gpc_dvfs_clk == NULL)
     gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
   /* gpc clock is needed for SRPG */
   clk_enable(gpc_dvfs_clk);
   mxc_cpu_lp_set(arch_idle_mode);

and delete the last line in the listing above — the call to mxc_cpu_lp_set(), which changes the processor’s power mode.

This solved the SDMA problem for me.

As a matter of fact, I would suggest commenting out this line during the development phase of any i.MX-based system, and return it once everything works. True, this shouldn’t be an issue if the clocks are properly configured. But if they’re not, something will fail, and the natural tendency is to focus the drivers of the failing functionality, and not looking for power management issues.

When the power reduction function is re-enabled at some later point, it’s quite evident what the problem is, if something fails then. So even if the target product is battery-driven, do yourself a favor, and drop that line in system.c until you’re finished struggling with other things.

Add a Comment

required, use real name
required, will not be published
optional, your blog address