summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2007-05-01 23:26:29 +0200
committerJean Delvare <khali@hyperion.delvare>2007-05-01 23:26:29 +0200
commit209d27c3b1676c0497108f0642c51a08b98a7856 (patch)
tree434c063c4bd163ff242ae8a18bf65701bb6d39e6
parent1ecac07abaca1a4084d0259d4a15b55188852a2e (diff)
downloadtalos-obmc-linux-209d27c3b1676c0497108f0642c51a08b98a7856.tar.gz
talos-obmc-linux-209d27c3b1676c0497108f0642c51a08b98a7856.zip
i2c: Emulate SMBus block read over I2C
Let the I2C bus drivers emulate the SMBus Block Read and Block Process Call transactions if they wish. This requires to define a new message flag, which i2c-core will use to let the underlying I2C bus driver know that the first received byte will specify the length of the read message. Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r--drivers/i2c/i2c-core.c34
-rw-r--r--include/linux/i2c.h1
2 files changed, 27 insertions, 8 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ae07b874a5d7..fd921ce0b75b 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -590,8 +590,9 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
- "len=%d\n", ret, msgs[ret].flags & I2C_M_RD ?
- 'R' : 'W', msgs[ret].addr, msgs[ret].len);
+ "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
+ ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
+ (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
@@ -1050,9 +1051,9 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
- dev_err(&adapter->dev, "Block read not supported "
- "under I2C emulation!\n");
- return -1;
+ msg[1].flags |= I2C_M_RECV_LEN;
+ msg[1].len = 1; /* block length will be added by
+ the underlying bus driver */
} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
@@ -1066,9 +1067,21 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
- dev_dbg(&adapter->dev, "Block process call not supported "
- "under I2C emulation!\n");
- return -1;
+ num = 2; /* Another special case */
+ read_write = I2C_SMBUS_READ;
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&adapter->dev, "%s called with invalid "
+ "block proc call size (%d)\n", __FUNCTION__,
+ data->block[0]);
+ return -1;
+ }
+ msg[0].len = data->block[0] + 2;
+ for (i = 1; i < msg[0].len; i++)
+ msgbuf0[i] = data->block[i-1];
+ msg[1].flags |= I2C_M_RECV_LEN;
+ msg[1].len = 1; /* block length will be added by
+ the underlying bus driver */
+ break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].len = I2C_SMBUS_BLOCK_MAX;
@@ -1132,6 +1145,11 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
data->block[i+1] = msgbuf1[i];
break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ for (i = 0; i < msgbuf1[0] + 1; i++)
+ data->block[i] = msgbuf1[i];
+ break;
}
return 0;
}
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 568dd1007b53..563c9651dd37 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -366,6 +366,7 @@ struct i2c_msg {
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
+#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
OpenPOWER on IntegriCloud