We have a custom i.MX6DL board. We have I2C2 connected to the MMPF0100 PMIC. About every 1 in 146 boots the I2C from the i.MX6 to the PMIC glitches. U-boot can correctly and successfully communicate with the PMIC as seen below:
Then u-boot loads and hands control to the Kernel. But on the first transaction in kernel space the following happens:
The Kernel sends the PMIC I2C address, the PMIC ACKs it's address, but then the i.MX6 never sends another command. I enabled the I2C recovery mode, which generates a stop condition and attempts to communicate with the PMIC again. This fails. At this point the Kernel gives up and since the power rails are not up the Kernel fails to boot.
It looks like someone had a similar issue: https://stackoverflow.com/questions/54092447/linux-i2c-communication-issues
I'm now trying different I2C drive strengths. What would cause the I2C module in the i.MX6 to never generate the next byte?
已解决! 转到解答。
I found the root cause. There is a bug in the sdma code. We were using 4.9.9 kernel. Below is the offending function:
static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
u32 address)
{
struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd;
void *buf_virt;
dma_addr_t buf_phys;
int ret;
unsigned long flags;
buf_virt = dma_alloc_coherent(NULL,
size,
&buf_phys, GFP_KERNEL);
if (!buf_virt) {
return -ENOMEM;
}
spin_lock_irqsave(&sdma->channel_0_lock, flags);
bd0->mode.command = C0_SETPM;
bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
bd0->mode.count = size / 2;
bd0->buffer_addr = buf_phys;
bd0->ext_buffer_addr = address;
memcpy(buf_virt, buf, size);
ret = sdma_run_channel0(sdma);
spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
dma_free_coherent(NULL, size, buf_virt, buf_phys);
return ret;
}
In the 4.9.11 kernel the BD_INTR flag was removed. After removing this flag, the unit booted thousands of times with no issues.
Hi @nlbutts,
I hope you are doing well.
I am exploring the issue, and it looks like there might be a possibility of noise on the SCLK pin, which causes the glitch. For further debugging of the case, I have some queries which are as follows,
Best Regards,
Dhruvit.
I found the root cause. There is a bug in the sdma code. We were using 4.9.9 kernel. Below is the offending function:
static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
u32 address)
{
struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd;
void *buf_virt;
dma_addr_t buf_phys;
int ret;
unsigned long flags;
buf_virt = dma_alloc_coherent(NULL,
size,
&buf_phys, GFP_KERNEL);
if (!buf_virt) {
return -ENOMEM;
}
spin_lock_irqsave(&sdma->channel_0_lock, flags);
bd0->mode.command = C0_SETPM;
bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;
bd0->mode.count = size / 2;
bd0->buffer_addr = buf_phys;
bd0->ext_buffer_addr = address;
memcpy(buf_virt, buf, size);
ret = sdma_run_channel0(sdma);
spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
dma_free_coherent(NULL, size, buf_virt, buf_phys);
return ret;
}
In the 4.9.11 kernel the BD_INTR flag was removed. After removing this flag, the unit booted thousands of times with no issues.