2011-06-07 03:29:05     I2C abort with no information

Document created by Aaronwu Employee on Aug 27, 2013
Version 1Show Document
  • View in full screen mode

2011-06-07 03:29:05     I2C abort with no information

isabelle leonardi (FRANCE)

Message: 101046   

 

Hi,

 

I'm using uclinux distribution 2010-RC5 on a BF548.

 

The software periodically performs I2C read transaction (by calling i2c_smbus_read_byte_data). Sometimes the transaction fails on timeout but there is no error notified on interrupt (no interrupt with MERR bit set). The last interrupt I get shows that MEN bit is reset while the transaction is not finished.

 

During a normal read sequence, I can observe 4 or 5 interrupts:

 

- IT1: INT_STAT = 0xC040 - MASTER_CTL = 0x01 on entry - MASTER_CTL = 0x25 on return

 

- IT2: INT_STAT = 0xC010 - MASTER_CTL = 0x25 on entry - MASTER_CTL = 0x45 on return

 

- IT3: INT_STAT = 0xC000 - MASTER_CTL = 0x45 on entry - MASTER_CTL = 0x45 on return

 

- IT4: INT_STAT = 0xC080 - MASTER_CTL = 0x05 on entry - MASTER_CTL = 0x05 on return

 

- IT5: INT_STAT = 0xC010 - MASTER_CTL = 0x04 on entry - MASTER_CTL = 0x00 on return

 

During the failing read sequence, I can observe only 1 interrupt:

 

- IT1: INT_STAT = 0xC050 - MASTER_CTL = 0x00 on entry - MASTER_STAT = 0x00

 

 

 

What can be the reason why the transaction is aborted without any notified error?

 

 

 

The interrupt handler for I2C bfin_twi_interrupt_entry in drivers/i2c/busses/i2c-bfin-twi.c does not particularly handle this case. The whole transaction simply fails on timeout (after 500 jiffies!!). Would it be correct to handle the unexpected case of a MEN reset in the handler by changing the interrupt handler as follows?

 

...line 203

 

    if (twi_int_status & MCOMP) {

        if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {

            if ((read_MASTER_CTL(iface) & MEN) == 0)

            {

                iface->result = -1;

                write_INT_MASK(iface, 0);

                write_MASTER_CTL(iface, 0);

            }

            else

            {

                if (iface->readNum == 0) {

                    /* set the read number to 1 and ask for manual

                     * stop in block combine mode

                     */

                    iface->readNum = 1;

                    iface->manual_stop = 1;

                    write_MASTER_CTL(iface,

                        read_MASTER_CTL(iface) | (0xff << 6));

                } else {

                    /* set the readd number in other

                     * combine mode.

                     */

                    write_MASTER_CTL(iface,

                        (read_MASTER_CTL(iface) &

                        (~(0xff << 6))) |

                        (iface->readNum << 6));

                }

                /* remove restart bit and enable master receive */

                write_MASTER_CTL(iface,

                    read_MASTER_CTL(iface) & ~RSTART);

 

            }

        } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&

                iface->cur_msg+1 < iface->msg_num) {

            iface->cur_msg++;

            iface->transPtr = iface->pmsg[iface->cur_msg].buf;

            iface->writeNum = iface->readNum =

                iface->pmsg[iface->cur_msg].len;

            /* Set Transmit device address */

            write_MASTER_ADDR(iface,

                iface->pmsg[iface->cur_msg].addr);

            if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)

                iface->read_write = I2C_SMBUS_READ;

            else {

                iface->read_write = I2C_SMBUS_WRITE;

                /* Transmit first data */

                if (iface->writeNum > 0) {

                    write_XMT_DATA8(iface,

                        *(iface->transPtr++));

                    iface->writeNum--;

                }

            }

 

            if (iface->pmsg[iface->cur_msg].len <= 255) {

                write_MASTER_CTL(iface,

                    (read_MASTER_CTL(iface) &

                    (~(0xff << 6))) |

                    (iface->pmsg[iface->cur_msg].len << 6));

                iface->manual_stop = 0;

            } else {

                write_MASTER_CTL(iface,

                    (read_MASTER_CTL(iface) |

                    (0xff << 6)));

                iface->manual_stop = 1;

            }

            /* remove restart bit and enable master receive */

            write_MASTER_CTL(iface,

                read_MASTER_CTL(iface) & ~RSTART);

        } else {

            iface->result = 1;

            write_INT_MASK(iface, 0);

            write_MASTER_CTL(iface, 0);

        }

        complete(&iface->complete);

    }

 

Thanks for your help,

 

Isabelle

QuoteReplyEditDelete

 

 

2011-06-17 07:01:00     Re: I2C abort with no information

Sonic Zhang (CHINA)

Message: 101382   

 

Yes, this unexpected MEN reset should be handled for both  TWI_I2C_MODE_COMBINED and TWI_I2C_MODE_REPEAT. I will commit a patch soon.

 

This abort looks like the first transmit interrupt is not served in time because some higher interrupt handler is running in kernel. So, the RESART bit is not set before next I2C clock. When the TWI interrupt handler is called finally, the I2C session is aborted (MEN bit is reset) and both SMITSERV and MCOMP int status bits are set. Don't know why ERR status is not set in this case.

Attachments

    Outcomes