Hi, For anyone interested, I have implemented i2c slave mode transfers. The i2c-dev subsystem is extended with a new flag I2C_M_SLAVE. Basically, a struct i2c_msg holding flag I2C_M_SLAVE will wait in slave mode for a write request on specified target address, and will report the received content to the user application. An example of driver implementation has been done for i2c-mpc. There's no support for read request yet, and I'm wondering whether it would make sense. The patch has been tested on powerpc MPC8313E. Note: the present i2c-mpc implementation may be improved, esp. regarding current timing issues. The need for I2C host adapter slave support is quite uncommon. Last question was raised 2 years ago[1]. This kind of support can prove useful in multi-master based protocols like found in IPMI. With appropriate hacking, FreeIPMI[2] software can nicely make use of such subsystem. Talking about i2c-dev, does the I2C_M_SLAVE flag is the appropriate way of modelling the API extension? Is there any interest for this extension to be accepted in the linux kernel? Any comments, advices are welcome. [1] http://thread.gmane.org/gmane.linux.drivers.i2c/43/focus=43 [2] http://www.gnu.org/software/freeipmi/ Signed-off-by: Stephane Fillod <stephane.fillod@xxxxxxxxxxxxxxx> --- a/include/linux/i2c.h 24 Feb 2010 18:52:17 -0000 +++ b/include/linux/i2c.h 26 Feb 2010 15:50:46 -0000 @@ -492,6 +492,7 @@ struct i2c_msg { __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ +#define I2C_M_SLAVE 0x0002 /* from master client to slave adap */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ --- a/drivers/i2c/i2c-dev.c 24 Feb 2010 18:52:17 -0000 +++ b/drivers/i2c/i2c-dev.c 26 Feb 2010 15:49:51 -0000 @@ -270,12 +270,18 @@ static noinline int i2cdev_ioctl_rdrw(st res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); while (i-- > 0) { - if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { + if (res >= 0 && (rdwr_pa[i].flags & (I2C_M_RD|I2C_M_SLAVE))) { if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf, rdwr_pa[i].len)) res = -EFAULT; } kfree(rdwr_pa[i].buf); + if (res >= 0 && (rdwr_pa[i].flags & I2C_M_SLAVE)) { + struct i2c_msg __user *umsgs; + umsgs = ((struct i2c_rdwr_ioctl_data __user *)arg)->msgs; + if (put_user(rdwr_pa[i].len, &umsgs[i].len)) + res = -EFAULT; + } } kfree(data_ptrs); kfree(rdwr_pa); --- a/drivers/i2c/busses/i2c-mpc.c 3 Dec 2009 03:51:21 -0000 +++ b/drivers/i2c/busses/i2c-mpc.c 27 Apr 2010 11:01:47 -0000 @@ -4,7 +4,7 @@ * This is a combined i2c adapter and algorithm driver for the * MPC107/Tsi107 PowerPC northbridge and processors that include - * the same I2C unit (8240, 8245, 85xx). + * the same I2C unit (8240, 8245, 83xx, 85xx). * * Release 0.8 * @@ -31,6 +31,7 @@ #define DRV_NAME "mpc-i2c" +#define MPC_I2C_ADDR 0x00 #define MPC_I2C_FDR 0x04 #define MPC_I2C_CR 0x08 #define MPC_I2C_SR 0x0c @@ -73,6 +74,11 @@ struct mpc_i2c_match_data { u32 prescaler; }; +static inline void writeaddr(struct mpc_i2c *i2c, u32 x) +{ + writeb(x, i2c->base + MPC_I2C_ADDR); +} + static inline void writeccr(struct mpc_i2c *i2c, u32 x) { writeb(x, i2c->base + MPC_I2C_CR); @@ -428,6 +434,68 @@ static int mpc_read(struct mpc_i2c *i2c, return length; } +static int mpc_slave_xrecv(struct mpc_i2c *i2c, int target, + u8 *data, int length, int restart) +{ + unsigned timeout = i2c->adap.timeout; + int i, result, srw; + int readlen = 0; + u32 flags; + + /* Listening address */ + writeaddr(i2c, target); + /* Start with MEN */ + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MTX); + + /* Give I2C signals sufficent time to settle */ + udelay(30); + + result = i2c_wait(i2c, timeout, 1); + if (result < 0) { + writeccr(i2c, 0); + return result; + } +#if 0 + srw = readb(i2c->base + MPC_I2C_SR) & CSR_SRW; +#else + /* TODO: handle and test SRW!=0 */ + srw = 0; +#endif + flags = srw ? CCR_MTX : 0; + + if (length) { + if (length == 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK | flags); + else + writeccr(i2c, CCR_MIEN | CCR_MEN | flags); + /* Dummy read/write */ + if (srw) + writeb(0, i2c->base + MPC_I2C_DR); + else + readb(i2c->base + MPC_I2C_DR); + } + + for (i = 0; i < length; i++) { + result = i2c_wait(i2c, timeout, 0); + if (result < 0) + return i > 0 && result == -ETIMEDOUT ? i-1 : result; + /* Generate stop on last byte */ + if (i == length - 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK | flags); + data[i] = readb(i2c->base + MPC_I2C_DR); + readlen++; + /* check early STOP after 2 clock cycles at 100 kHz */ + udelay(20); + if (!(readb(i2c->base + MPC_I2C_SR) & CSR_MBB)) + break; + } + /* clear I2CSR register */ + out_8(i2c->base + MPC_I2C_SR, 0); + /* hang up */ + writeccr(i2c, CCR_MEN); + return readlen; +} + static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct i2c_msg *pmsg; @@ -459,9 +527,14 @@ static int mpc_xfer(struct i2c_adapter * pmsg = &msgs[i]; dev_dbg(i2c->dev, "Doing %s %d bytes to 0x%02x - %d of %d messages\n", - pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->flags & I2C_M_SLAVE ? "recv" : + pmsg->flags & I2C_M_RD ? "read" : "write", pmsg->len, pmsg->addr, i + 1, num); - if (pmsg->flags & I2C_M_RD) + if (pmsg->flags & I2C_M_SLAVE) { + ret = mpc_slave_xrecv(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + if (ret >= 0) + pmsg->len = ret; + } else if (pmsg->flags & I2C_M_RD) ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); else Regards -- Stephane Fillod -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html