Hi all,
I'm working with an mx287, linux-3.7.6 (buildroot) and need one of the i2c's to be a slave (100KHz). I have reviewed the mx28_i2c_slave_demo.patch that has been referenced in several discussions on this site. The patch appears to be for an older kernel (not sure which one), and linux-3.7.6 seems to have changed to the extent that it did not help enough on this issue.
I have managed to get the DMA to read in the data from a master i2c. However the data and clock lines are held low after the last byte received by the i2c slave. I have tried to clear the CLOCK_HELD in the I2C_CTRL0 register and/or set the FORCE_DATA_IDLE and FORCE_CLK_IDLE in the CRTL1 register (these were done in almost every combination) from the DATA_ENGINE_CMPLT_IRQ interrupt, with no success. Can anyone provide some direction as how to get the data and clock to be released?
Per MCIMX28RM section 27.2.2.6 Slave Mode Protocol,
The DMA engine stops with the clock held and the hardware ready to acknowledge the last byte when the clock is released. Software decides whether the last byte is acknowledged or not.
How can this be done with DMA?
Per the Slave Mode Protocol, I need to release the clock after the last byte, did this in DATA_ENGINE_CMPLT_IRQ interrupt with CLOCK_HELD in the I2C_CTRL0 register and it didn't work. Any help would be appreciated.
And for some code,
initialization code:
writel(i2c->slave_address, i2c->regs + MXS_I2C_CTRL1_SET);
writel((MXS_I2C_CTRL1_ACK_MODE), i2c->regs + MXS_I2C_CTRL1_SET); |
writel((MXS_I2C_CTRL0_DIRECTION | MXS_I2C_CTRL0_MASTER_MODE |
MXS_I2C_CTRL0_RETAIN_CLOCK | MXS_I2C_CTRL0_CLOCK_HELD), | ||
i2c->regs + MXS_I2C_CTRL0_CLR); |
writel(( MXS_I2C_CTRL0_SLAVE_ADDR_EN | MXS_I2C_CTRL0_RUN),
i2c->regs + MXS_I2C_CTRL0_SET); |
DMA descriptor after CTRL1_SLAVE_IRQ interrupt and detection that a write is seen from the i2c master:
i2c->pio_data[0] = flags | MXS_CMD_I2C_READ |
MXS_I2C_CTRL0_XFER_COUNT(msg->len);
desc = dmaengine_prep_slave_sg(i2c->dmach,
(struct scatterlist *)&i2c->pio_data[0],
1, DMA_TRANS_NONE, 0);
sg_init_one(&i2c->sg_io[0], msg->buf, msg->len);
desc = dmaengine_prep_slave_sg(i2c->dmach, &i2c->sg_io[0], 1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
/* |
* The last descriptor must have this callback,
* to finish the DMA transaction.
*/
desc->callback = mxs_i2c_dma_irq_callback;
desc->callback_param = i2c;
/* Start the transfer. */
dmaengine_submit(desc);
dma_async_issue_pending(i2c->dmach);
Thanks,
amf
解決済! 解決策の投稿を見る。
Hello Marcin,
In i2c slave, please try NOT to set the HW_I2C_CTRL0 RUN bit while waiting for the slave address match.
Regards,
Peter
Resolved this issue and found the next,
What was not defined in the original post was MXS_CMD_I2C_READ
#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_RUN | MXS_I2C_CTRL0_ACKNOWLEDGE | MXS_I2C_CTRL0_RETAIN_CLOCK)
This is used to set I2C_CTRL0 register after the slave address was detected. The issue is MXS_I2C_CTRL0_RETAIN_CLOCK, the clock does not need to be retained when receiving the data after the slave address
MXS_CMD_I2C_READ should have been defined as
#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_RUN | MXS_I2C_CTRL0_ACKNOWLEDGE)
which fixed the issue with data and clock lines being held low.
Now for the new problem,
When sending (i2c write) 2-bytes of data from Aardvark I2C (as master), what shows up in the buffer after DMA finishes is the slave address and the first byte sent. The HW_I2C_DATA register contains all three bytes, address and 2-data bytes. The HW_I2C_QUEUESTATE register RD_QUEUE_CNT field contains a value of 2, which is also the value set in HW_I2C_CTRL0 register XFER_COUNT field during receive data set up from CTRL1_SLAVE_IRQ interrupt.
So how do I get the second data byte to show up in the buffer provided to DMA?
The i2c slave demo patch is based on linux 2.6.35 which is the kernel version of our latest MX28 Linux BSP release. The patch intends to read the data from master one byte by one byte. Thus it has the RETAIN_CLOCK bit set in I2C READ command to allow software to decide to whether to acknowledge the coming data or not.
The i.MX28 i2c slave DMA always fetch the address byte as the first byte to the destination buffer. To get actual data byte, you have to read it from the HW_I2C_DATA register.
One problem with i.MX28 when using DMA as a single data chunk in i2c slave mode is that the byte send from the i2c master must match the XFER_COUNT in the i2c slave DMA. If the XFER_COUNT has requested more bytes than the master going to send, the transaction will stop by an early termination condition at the i.MX28 slave side. Received data leaving in DMA FIFO will be lost because there is no way to flush the FIFO to the DMA buffer address. Setting the TERMINATEFLUSH bit in the DMA descriptor will NOT help to resolve the problem. The workaround is to split the single data chunk into many one byte transfers such that the FIFO can always flush when the one byte DMA transfer ends.
PeterChan
Thank you for the feedback,
In the workaround solution, does this imply that the master must do the one byte transfers? If so, then this will not work for us being we are not in control of the i2c master.
Does the PIO Queue Mode feature work for i2C slave? I'm trying to work with it, but without much luck.
Thanks,
amf
I didn't mean to limit the i2c slave transaction ito 1 byte length. I meant to setup a DMA descriptor to receive 1 a byte and held the clock, and then setup another DMA descriptor to receive the next byte and so on until early termination by master.
Hello PeterChan,
Thank for your example, I already ported your driver to 3.5 kernel and everything is working.
I have one problem. My device should work like EEPROM memory. In each time there can be write or read request from I2C master. I already prepared two buffers one for read and one for write. But unfortunately I have some problem with i2c direction.
When I init i2c driver as follows – with direction set to 0 – I can only read from device.
writel( MXS_I2C_CTRL0_DIRECTION | MXS_I2C_CTRL0_MASTER_MODE | MXS_I2C_CTRL0_RETAIN_CLOCK | MXS_I2C_CTRL0_CLOCK_HELD, i2c->regs + MXS_I2C_CTRL0_CLR);
writel( MXS_I2C_CTRL0_SLAVE_ADDR_EN |MXS_I2C_CTRL0_RUN, i2c->regs + MXS_I2C_CTRL0_SET);
For following configuration – dir = 1 – I can only write to device. (For every read request I got 0xff from slave)
writel( MXS_I2C_CTRL0_MASTER_MODE | MXS_I2C_CTRL0_RETAIN_CLOCK | MXS_I2C_CTRL0_CLOCK_HELD, i2c->regs + MXS_I2C_CTRL0_CLR);
writel( MXS_I2C_CTRL0_SLAVE_ADDR_EN | MXS_I2C_CTRL0_DIRECTION |
MXS_I2C_CTRL0_RUN, /* run */ i2c->regs + MXS_I2C_CTRL0_SET);
I tried to add writel in I2C isr but this also didn't work. Is there any possibility to switch direction after I2C address detect?
BR
Wojciech Slenska
Hello Wojciech,
Unfortunately, the direction seems to be configured when the I2C transaction start. It can't be changed even if you switch the direction in the address match phase. I don't have workaround on that.
Is it feasible to use bit-banging in your application?
Regards,
Peter
Hi Peter,
It looks much easier when we analyze description form datasheet - eg. " Figure 27-8. I2C Slave Mode Flow Chart"
But according to current discussion it looks a little bit different.
Does it mean that only way to change direction is to perform slave reconfiguration (to get search engine fully working)?
In such case we need to drop one frame, when the direction will be changed W->R or R->W. This is not good solution
So could we handle this in different way?
Best Regards,
Marcin
Marcin, if your question has been answered please click Correct Answer/Helpful Answer.
Thanks,
Yixing
Hello Marcin,
In i2c slave, please try NOT to set the HW_I2C_CTRL0 RUN bit while waiting for the slave address match.
Regards,
Peter