why repeated start based i2c operation are not supported in linux? -


i want read i2c slave need multi start operation read register values.

as up-to extent have traced i2c driver in linux kernel 3.18.21, found not support multi start operation , have no way read i2c slave (power on ethernet manager pd69104b1).

i still finding way can extended driver if needed i2c slave or else needed.

i use i2c-tools 3.2.1. try

$ i2cdump -y 0 0x20  

but can see same values means read first register every time.

$ i2cget -y 0 0x20 0x12 

or other register address returns same value first register.

this slave support 2 read operation:

  • byte read - write address value need multi start
  • block read - start reading , i2c slave give register values in sequence 0x00 0x01.... (first register, second , third, fourth....etc)

i try possible ways:

  • i2c_smbus_access()
  • i2c_smbus_write_byte()
  • i2c_smbus_read_block_data()
  • write()
  • read()

but of time i2c bus goes timeout error , hang situations.

anyone has idea how achieve in linux?

update0:

this i2c slaves need unique read cycles:

  • change of direction: s addr wr [a] regaddress [a] s addr rd [a] [regvalue] p

  • short read: s addr rd [a] [regvalue] p

here last value returned i2c slave not expect ack.

i tried use i2c_m_no_rd_ack not help. read value , ff.

this poe i2c slave have i2c time out of 14ms on scl bit of doubt. looks i2c non standard i2c can work on 0hz i.e. scl can stretched master long want. linux not real time os achieving time out can not guaranteed , i2c slave scl timeout reset may happen. current conclusion is!

i2c message notation used from: https://www.kernel.org/doc/documentation/i2c/i2c-protocol

why repeated start based i2c operation not supported in linux?

as matter of fact, supported.

if looking way perform repeated start condition in user-space, need ioctl() i2c_rdwr request, it's described here (see last code snippet in original question) , here (code in question).

below described way perform repeated start in kernel-space.


in linux kernel i2c read operations repeated start condition performed default combined (write/read) messages.

here example how perform combined i2c transfer:

/**  * read set of registers via i2c using "repeated start" condition.  *  * 2 i2c messages being sent function:  *   1. i2c write operation (write register address) no stop bit in end  *   2. i2c read operation  *  * @client: i2c client structure  * @reg: register address (subaddress)  * @len: bytes count read  * @buf: buffer contain read data  *  * returns 0 on success or negative value on error.  */ static int i2c_read_regs(struct i2c_client *client, u8 reg, u8 len, u8 *buf) {     int ret;     struct i2c_msg msg[2] = {         {             .addr = client->addr,             .len = 1,             .buf = &reg,         },         {             .addr = client->addr,             .flags = i2c_m_rd,             .len = len,             .buf = buf,         }     };      ret = i2c_transfer(client->adapter, msg, 2);     if (ret < 0) {         dev_err(&client->dev, "i2c read failed\n");         return ret;     }      return 0; } 

to read 1 byte (single register value) can use next helper function:

/**  * read 1 register via i2c using "repeated start" condition.  *  * @client: i2c client structure  * @reg: register address (subaddress)  * @val: variable store read value  *  * returns 0 on success or negative value on error.  */ static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val) {     return i2c_read_regs(client, reg, 1, val); } 

below illustration i2c_read_regs(client, reg, 1, val) call:

  • device address client->addr
  • register address reg
  • 1 means want read 1 byte of data (pink rectangle on picture)
  • read data reside @ val

enter image description here


note: if i2c controller (or driver) doesn't support repeated starts in combined messages, still can use bit-bang implementation of i2c, i2c-gpio driver.


if nothing works, can try next last resort. reason can't quite remember, in order make repeated start work needed add i2c_m_nostart .flags of first message, this:

struct i2c_msg msg[2] = {     {         .addr = client->addr,         .flags = i2c_m_nostart,         .len = 1,         .buf = &reg,     },     {         .addr = client->addr,         .flags = i2c_m_rd,         .len = len,         .buf = buf,     } }; 

as noted in documentation/i2c/i2c-protocol:

if set i2c_m_nostart variable first partial message, not generate addr, generate startbit s.


references:

[1] i2c on stlinux


Comments