Xavier i2c I2C_M_REV_DIR_ADDR

Hello,

I have a Xavier devkit.

I’m struggling with an i2c device on /dev/i2c-8 which uses a non-standard protocol for updating it’s FW.

The write transactions go through without problem. But the read transactions require the address rw/wr bit to be set to 1 and this is where the problems start.

A read transaction looks like this:

    /******************************************************************************/
    // Read transaction

    //{DEVICE_ADDR[7:1], 1'b1}
    //  0xAC  Loader Module (shown with bit 0 masked to a 0)
    //<ACK_FROM_SLAVE>

    //REGISTER_ADDRESS_MSB
    //  16-bit register address to allow 65536 registers on slave
    //<ACK_FROM_SLAVE>

    //REGISTER_ADDRESS_LSB
    //  16-bit register address to allow 65536 registers on slave
    //<ACK_FROM_SLAVE>

    //NUMBER_OF_WORDS_MSB
    //  Number of 16-bit words to write or read, max of 100
    //<ACK_FROM_SLAVE>

    //NUMBER_OF_WORDS_LSB
    //  Number of 16-bit words to write or read, max of 100
    //<ACK_FROM_SLAVE>

    //WORD_1_MSB_FROM_SLAVE
    //<ACK_FROM_MASTER>

    //WORD_1_LSB_FROM_SLAVE
    //<ACK_FROM_MASTER>

    //...

    //FINAL_BYTE_FROM_SLAVE
    //<ACK_FROM_SLAVE>
    /******************************************************************************/

So with the device on address 0x28, and register 0x0000 returning 0x0123 I expect things to go like this:

S 0x51 ACK 0 ACK 0 ACK 0 ACK 1 ACK 0x01 ACK 0x23 ACK P

When I open the bus I check for the i2c functs

    int open(const QString &bus)
    {
        if ((m_fd = fileOps::open(bus.toLatin1(), O_RDWR)) < -1)
        {
            qDebug() << Q_FUNC_INFO << "Failed to open i2c bus " << bus;
            return -1;
        }

        unsigned long funcs = 0;

        if (ioctl(m_fd, I2C_FUNCS, &funcs))
        {
            qDebug() << Q_FUNC_INFO << "Could not retrieve bus functionalities";
            goto Error;
        }

        if (!(funcs & I2C_FUNC_PROTOCOL_MANGLING))
        {
            qDebug() << Q_FUNC_INFO << "The required I2C_FUNC_PROTOCOL_MANGLING functionality is not available";
            goto Error;
        }

        if (!(funcs & I2C_FUNC_NOSTART))
        {
            qDebug() << Q_FUNC_INFO << "The required I2C_FUNC_NOSTART functionality is not available";
            goto Error;
        }

        if (!(funcs & I2C_FUNC_10BIT_ADDR))
        {
            qDebug() << Q_FUNC_INFO << "Bus " << bus << " does not support 10-bit chip addressing";
        }

        return m_fd;

    Error:
        fileOps::close(m_fd);
        return -1;
    }

I2C_FUNC_PROTOCOL_MANGLING and I2C_FUNC_NOSTART are both present so it should be all good.

My read function looks like this:

    int read_word(quint16 reg, quint16 &word)
    {
        if (!isOpen())
        {
            qDebug() << Q_FUNC_INFO << "Device not open";
            return -1;
        }

        int retval = 0;
        quint8 header[4];
        quint8 output[2];
        struct i2c_msg msgs[3];
        struct i2c_rdwr_ioctl_data msgset[1];

        // Header
        msgs[0].addr = m_slaveAddress;
        msgs[0].flags = I2C_M_REV_DIR_ADDR;
        msgs[0].len = 4;
        msgs[0].buf = header;
        msgs[0].buf[0] = reg >> 8; // REGISTER_ADDRESS_MSB
        msgs[0].buf[1] = reg % 256; // REGISTER_ADDRESS_LSB
        msgs[0].buf[2] = 0; // NUMBER_OF_WORDS_MSB
        msgs[0].buf[3] = 1; // NUMBER_OF_WORDS_LSB

        // word
        msgs[1].addr = m_slaveAddress;
        msgs[1].flags = I2C_M_RD | I2C_M_NOSTART;
        msgs[1].len = 2;
        msgs[1].buf = output;

        msgset[0].msgs = msgs;
        msgset[0].nmsgs = 2;

        if ((retval = ioctl(m_fd, I2C_RDWR, &msgset)) < 0)
        {
            int errsv = errno;
            qWarning() << "ioctl(I2C_RDWR) failed" << "|" << "retval:" << retval << "|" << "errsv:" << errsv << strerror(errsv);
            return retval;
        }

        qDebug() << "MSB" << output[0];
        qDebug() << "LSB" << output[1];

        word = output[1] + (output[0] << 8);
        return 0;
    }

Reading some man pages it seems that I2C_M_REV_DIR_ADDR should do the trick of inverting the rd/wr bit but it has no effect.

I’m looking at the signals form oscilloscope and the rd/wr bit is at 0 regardless of I2C_M_REV_DIR_ADDR being set in the first message.

With msgs[0].flags set to I2C_M_REV_DIR_ADDR is the same as having 0; The i2c traffic looks like this:

S 0x50 ACK 0 ACK 0 ACK 0 ACK 1 ACK 0xFF ACK 0xFF ACK P

With msgs[0].flags set to I2C_M_RD the address byte is 0x51 but all the reset is garbage

S 0x51 ACK 0xFF ACK 0xFF ACK 0xFF ACK 0xFF ACK 0xFF ACK 0xFF ACK P

Why does I2C_M_REV_DIR_ADDR have no effect?

Thanks
-Damien

Could you use i2cget/i2cset public utility to check if have the same symptom?

i2cget/i2cset are implemented with SMBus. So, I cannot. With the custom protocol, I need to use ioctl with I2C_RDWR.

Could you help check any others bus.

Thanks

Hi Damien,
Can you first set the address position of slave via
msgs[0].flags = 0;
msgs[0].len=1;
msgs[0].buf=<8 bit position>;

if you want to write first in this position then
msgs[1].flags = 0;
msgs[1].len=;
msgs[1].buf=;

then read:
msgs[2].flags = 0x1 or I2C_M_RD;
msgs[2].len=;
msgs[2].buf= output;

Using I2C_M_RD in your code in msgs[0].flags will obviously give garbage values since you need to write slave position first.
and i think, since you have set I2C_M_REV_DIR_ADDR in msgs[0].flags and I2C_M_RD in msgs[1].flags, it has revered direction from read to write and that’s why you may not be able to read it. But not very sure if it is happening because of this reason. You can try 2 things: first the flow I gave above. else in your existing code, replace I2C_M_REV_DIR_ADDR with 0x0 and let us know the results in both ways.

Thanks,
Shubhi