Messages in this thread Patch in this message | | | Date | Thu, 24 Feb 2005 17:27:25 -0600 | From | Corey Minyard <> | Subject | [PATCH] I2C patch 2 - break up the SMBus formatting |
| |
This is one in a series of patches for adding a non-blocking interface to the I2C driver for supporting the IPMI SMBus driver. This patch reorganizes the formatting code to make it more suitable for the upcoming non-blocking changes. It also adds an op queue entry that is used to pass data around (for now) and will be used for queueing in the non-blocking case.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc4/drivers/i2c/i2c-core.c =================================================================== --- linux-2.6.11-rc4.orig/drivers/i2c/i2c-core.c +++ linux-2.6.11-rc4/drivers/i2c/i2c-core.c @@ -583,51 +583,65 @@ * ---------------------------------------------------- */ -int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) +static void i2c_transfer_entry(struct i2c_adapter * adap, + struct i2c_op_q_entry * entry) { - int ret; - + entry->xfer_type = I2C_OP_I2C; + entry->complete = NULL; if (adap->algo->master_xfer) { - dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num); + dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", + entry->i2c.num); down(&adap->bus_lock); - ret = adap->algo->master_xfer(adap,msgs,num); + entry->result = adap->algo->master_xfer(adap, entry->i2c.msgs, + entry->i2c.num); up(&adap->bus_lock); - - return ret; + if (entry->complete) + entry->complete(adap, entry); } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); - return -ENOSYS; + entry->result = -ENOSYS; } } +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) +{ + struct i2c_op_q_entry *entry; + int rv; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->i2c.msgs = msgs; + entry->i2c.num = num; + + i2c_transfer_entry(adap, entry); + rv = entry->result; + kfree(entry); + return rv; +} + int i2c_master_send(struct i2c_client *client,const char *buf ,int count) { int ret; struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; - if (client->adapter->algo->master_xfer) { - msg.addr = client->addr; - msg.flags = client->flags & I2C_M_TEN; - msg.len = count; - msg.buf = (char *)buf; - - dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n", - count); + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (char *)buf; - down(&adap->bus_lock); - ret = adap->algo->master_xfer(adap,&msg,1); - up(&adap->bus_lock); + dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n", + count); - /* if everything went ok (i.e. 1 msg transmitted), return #bytes - * transmitted, else error code. - */ - return (ret == 1 )? count : ret; - } else { - dev_err(&client->adapter->dev, "I2C level transfers not supported\n"); - return -ENOSYS; - } + ret = i2c_transfer(adap, &msg, 1); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; } int i2c_master_recv(struct i2c_client *client, char *buf ,int count) @@ -635,31 +649,25 @@ struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; int ret; - if (client->adapter->algo->master_xfer) { - msg.addr = client->addr; - msg.flags = client->flags & I2C_M_TEN; - msg.flags |= I2C_M_RD; - msg.len = count; - msg.buf = buf; - dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n", - count); - - down(&adap->bus_lock); - ret = adap->algo->master_xfer(adap,&msg,1); - up(&adap->bus_lock); + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n", + count); - dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n", - ret, count, client->addr); + ret = i2c_transfer(adap, &msg, 1); + + dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr); - /* if everything went ok (i.e. 1 msg transmitted), return #bytes - * transmitted, else error code. - */ - return (ret == 1 )? count : ret; - } else { - dev_err(&client->adapter->dev, "I2C level transfers not supported\n"); - return -ENOSYS; - } + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; } @@ -1052,184 +1060,263 @@ } } -/* Simulate a SMBus command using the i2c protocol - No checking of parameters is done! */ -static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, - unsigned short flags, - char read_write, u8 command, int size, - union i2c_smbus_data * data) -{ - /* So we need to generate a series of msgs. In the case of writing, we - need to use only one message; when reading, we need two. We initialize - most things with sane defaults, to keep the code below somewhat - simpler. */ - unsigned char msgbuf0[34]; - unsigned char msgbuf1[34]; - int num = read_write == I2C_SMBUS_READ?2:1; - struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, - { addr, flags | I2C_M_RD, 0, msgbuf1 } - }; +static void i2c_smbus_complete_entry(struct i2c_adapter * adap, + struct i2c_op_q_entry * entry) +{ + if (entry->result < 0) + return; + + if(entry->result >= 0 && entry->swpec && + entry->smbus.size != I2C_SMBUS_QUICK && + entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA && + (entry->smbus.read_write == I2C_SMBUS_READ || + entry->smbus.size == I2C_SMBUS_PROC_CALL_PEC || + entry->smbus.size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) { + if(i2c_smbus_check_pec(entry->smbus.addr, + entry->smbus.command, + entry->smbus.size, + entry->partial, + entry->smbus.data)) + entry->result = -EINVAL; + } +} + +static void i2c_smbus_format_entry(struct i2c_adapter * adap, + struct i2c_op_q_entry * entry) +{ + entry->swpec = 0; + entry->partial = 0; + entry->smbus.flags &= I2C_M_TEN | I2C_CLIENT_PEC; + if((entry->smbus.flags & I2C_CLIENT_PEC) && + !(i2c_check_functionality(adap, I2C_FUNC_SMBUS_HWPEC_CALC))) { + entry->swpec = 1; + if(entry->smbus.read_write == I2C_SMBUS_READ && + entry->smbus.size == I2C_SMBUS_BLOCK_DATA) + entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC; + else if(entry->smbus.size == I2C_SMBUS_PROC_CALL) + entry->smbus.size = I2C_SMBUS_PROC_CALL_PEC; + else if(entry->smbus.size == I2C_SMBUS_BLOCK_PROC_CALL) { + unsigned char *data = entry->smbus.data->block; + i2c_smbus_add_pec(entry->smbus.addr, + entry->smbus.command, + I2C_SMBUS_BLOCK_DATA, + entry->smbus.data); + entry->partial = data[data[0] + 1]; + entry->smbus.size = I2C_SMBUS_BLOCK_PROC_CALL_PEC; + } else if(entry->smbus.read_write == I2C_SMBUS_WRITE && + entry->smbus.size != I2C_SMBUS_QUICK && + entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA) + entry->smbus.size = + i2c_smbus_add_pec(entry->smbus.addr, + entry->smbus.command, + entry->smbus.size, + entry->smbus.data); + } + + entry->complete = i2c_smbus_complete_entry; +} + +static void i2c_smbus_emu_complete(struct i2c_adapter * adap, + struct i2c_op_q_entry * entry) +{ + unsigned char *msgbuf0 = entry->i2c.msgs[0].buf; + unsigned char *msgbuf1 = entry->i2c.msgs[1].buf; int i; - msgbuf0[0] = command; - switch(size) { + if (entry->smbus.read_write != I2C_SMBUS_READ) + return; + + switch(entry->smbus.size) { + case I2C_SMBUS_BYTE: + entry->smbus.data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + entry->smbus.data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + entry->smbus.data->word = msgbuf1[0]|(msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + /* fixed at 32 for now */ + entry->smbus.data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX; + for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++) + entry->smbus.data->block[i+1] = msgbuf1[i]; + break; + } + + entry->xfer_type = I2C_OP_SMBUS; + i2c_smbus_complete_entry(adap, entry); +} + +static int i2c_smbus_emu_format(struct i2c_adapter *adap, + struct i2c_op_q_entry * entry) +{ + /* So we need to generate a series of msgs. In the case of + writing, we need to use only one message; when reading, we + need two. We initialize most things with sane defaults, to + keep the code below somewhat simpler. */ + unsigned char *msgbuf0 = entry->msgbuf0; + unsigned char *msgbuf1 = entry->msgbuf1; + int num = entry->smbus.read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg *msg = entry->msg; + int i; + + entry->i2c.msgs = msg; + entry->i2c.msgs[0].buf = msgbuf0; + entry->i2c.msgs[1].buf = msgbuf1; + + msg[0].addr = entry->smbus.addr; + msg[0].flags = entry->smbus.flags; + msg[0].len = 1; + msg[1].addr = entry->smbus.addr; + msg[1].flags = entry->smbus.flags | I2C_M_RD; + msg[1].len = 0; + + msgbuf0[0] = entry->smbus.command; + switch(entry->smbus.size) { case I2C_SMBUS_QUICK: msg[0].len = 0; /* Special case: The read/write field is used as data */ - msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + msg[0].flags = (entry->smbus.flags | + ((entry->smbus.read_write==I2C_SMBUS_READ) + ? I2C_M_RD : 0)); num = 1; break; case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_READ) { + if (entry->smbus.read_write == I2C_SMBUS_READ) { /* Special case: only a read! */ - msg[0].flags = I2C_M_RD | flags; + msg[0].flags = I2C_M_RD | entry->smbus.flags; num = 1; } break; case I2C_SMBUS_BYTE_DATA: - if (read_write == I2C_SMBUS_READ) + if (entry->smbus.read_write == I2C_SMBUS_READ) msg[1].len = 1; else { msg[0].len = 2; - msgbuf0[1] = data->byte; + msgbuf0[1] = entry->smbus.data->byte; } break; case I2C_SMBUS_WORD_DATA: - if (read_write == I2C_SMBUS_READ) + if (entry->smbus.read_write == I2C_SMBUS_READ) msg[1].len = 2; else { msg[0].len=3; - msgbuf0[1] = data->word & 0xff; - msgbuf0[2] = (data->word >> 8) & 0xff; + msgbuf0[1] = entry->smbus.data->word & 0xff; + msgbuf0[2] = (entry->smbus.data->word >> 8) & 0xff; } break; case I2C_SMBUS_PROC_CALL: num = 2; /* Special case */ - read_write = I2C_SMBUS_READ; + entry->smbus.read_write = I2C_SMBUS_READ; msg[0].len = 3; msg[1].len = 2; - msgbuf0[1] = data->word & 0xff; - msgbuf0[2] = (data->word >> 8) & 0xff; + msgbuf0[1] = entry->smbus.data->word & 0xff; + msgbuf0[2] = (entry->smbus.data->word >> 8) & 0xff; break; case I2C_SMBUS_BLOCK_DATA: case I2C_SMBUS_BLOCK_DATA_PEC: - if (read_write == I2C_SMBUS_READ) { - dev_err(&adapter->dev, "Block read not supported " + if (entry->smbus.read_write == I2C_SMBUS_READ) { + dev_err(&adap->dev, "Block read not supported " "under I2C emulation!\n"); return -1; } else { - msg[0].len = data->block[0] + 2; + msg[0].len = entry->smbus.data->block[0] + 2; if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { - dev_err(&adapter->dev, "smbus_access called with " + dev_err(&adap->dev, + "smbus_access called with " "invalid block write size (%d)\n", - data->block[0]); + entry->smbus.data->block[0]); return -1; } - if(size == I2C_SMBUS_BLOCK_DATA_PEC) + if(entry->smbus.size == I2C_SMBUS_BLOCK_DATA_PEC) (msg[0].len)++; for (i = 1; i <= msg[0].len; i++) - msgbuf0[i] = data->block[i-1]; + msgbuf0[i] = entry->smbus.data->block[i-1]; } break; case I2C_SMBUS_BLOCK_PROC_CALL: case I2C_SMBUS_BLOCK_PROC_CALL_PEC: - dev_dbg(&adapter->dev, "Block process call not supported " + dev_dbg(&adap->dev, "Block process call not supported " "under I2C emulation!\n"); return -1; case I2C_SMBUS_I2C_BLOCK_DATA: - if (read_write == I2C_SMBUS_READ) { + if (entry->smbus.read_write == I2C_SMBUS_READ) { msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX; } else { - msg[0].len = data->block[0] + 1; + msg[0].len = entry->smbus.data->block[0] + 1; if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) { - dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with " + dev_err(&adap->dev, + "i2c_smbus_xfer_emulated called with " "invalid block write size (%d)\n", - data->block[0]); + entry->smbus.data->block[0]); return -1; } - for (i = 1; i <= data->block[0]; i++) - msgbuf0[i] = data->block[i]; + for (i = 1; i <= entry->smbus.data->block[0]; i++) + msgbuf0[i] = entry->smbus.data->block[i]; } break; default: - dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n", - size); + dev_err(&adap->dev, + "smbus_access called with invalid size (%d)\n", + entry->smbus.size); return -1; } - if (i2c_transfer(adapter, msg, num) < 0) - return -1; - - if (read_write == I2C_SMBUS_READ) - switch(size) { - case I2C_SMBUS_BYTE: - data->byte = msgbuf0[0]; - break; - case I2C_SMBUS_BYTE_DATA: - data->byte = msgbuf1[0]; - break; - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - data->word = msgbuf1[0] | (msgbuf1[1] << 8); - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - /* fixed at 32 for now */ - data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX; - for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++) - data->block[i+1] = msgbuf1[i]; - break; - } + entry->xfer_type = I2C_OP_I2C; + entry->i2c.msgs = msg; + entry->i2c.num = num; + entry->complete = i2c_smbus_emu_complete; return 0; } - -s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, +s32 i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data) { - s32 res; - int swpec = 0; - u8 partial = 0; - - flags &= I2C_M_TEN | I2C_CLIENT_PEC; - if((flags & I2C_CLIENT_PEC) && - !(i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HWPEC_CALC))) { - swpec = 1; - if(read_write == I2C_SMBUS_READ && - size == I2C_SMBUS_BLOCK_DATA) - size = I2C_SMBUS_BLOCK_DATA_PEC; - else if(size == I2C_SMBUS_PROC_CALL) - size = I2C_SMBUS_PROC_CALL_PEC; - else if(size == I2C_SMBUS_BLOCK_PROC_CALL) { - i2c_smbus_add_pec(addr, command, - I2C_SMBUS_BLOCK_DATA, data); - partial = data->block[data->block[0] + 1]; - size = I2C_SMBUS_BLOCK_PROC_CALL_PEC; - } else if(read_write == I2C_SMBUS_WRITE && - size != I2C_SMBUS_QUICK && - size != I2C_SMBUS_I2C_BLOCK_DATA) - size = i2c_smbus_add_pec(addr, command, size, data); - } - - if (adapter->algo->smbus_xfer) { - down(&adapter->bus_lock); - res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, - command,size,data); - up(&adapter->bus_lock); - } else - res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, - command,size,data); - - if(res >= 0 && swpec && - size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA && - (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC || - size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) { - if(i2c_smbus_check_pec(addr, command, size, partial, data)) - return -1; + struct i2c_op_q_entry *entry; + struct i2c_algorithm *algo = adap->algo; + int result; + + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->xfer_type = I2C_OP_SMBUS; + entry->smbus.addr = addr; + entry->smbus.flags = flags; + entry->smbus.read_write = read_write; + entry->smbus.command = command; + entry->smbus.size = size; + entry->smbus.data = data; + + i2c_smbus_format_entry(adap, entry); + + if (algo->smbus_xfer) { + down(&adap->bus_lock); + entry->result = adap->algo->smbus_xfer(adap, + entry->smbus.addr, + entry->smbus.flags, + entry->smbus.read_write, + entry->smbus.command, + entry->smbus.size, + entry->smbus.data); + up(&adap->bus_lock); + if (entry->complete) + entry->complete(adap, entry); + } else { + if (i2c_smbus_emu_format(adap, entry)) + entry->result = -EINVAL; + else + i2c_transfer_entry(adap, entry); } - return res; -} + result = entry->result; + kfree(entry); + return result; +} /* You should always define `functionality'; the 'else' is just for backward compatibility. */ Index: linux-2.6.11-rc4/include/linux/i2c.h =================================================================== --- linux-2.6.11-rc4.orig/include/linux/i2c.h +++ linux-2.6.11-rc4/include/linux/i2c.h @@ -32,7 +32,9 @@ #include <linux/types.h> #include <linux/i2c-id.h> #include <linux/device.h> /* for struct device */ +#include <linux/completion.h> #include <asm/semaphore.h> +#include <asm/atomic.h> /* --- General options ------------------------------------------------ */ @@ -43,6 +45,7 @@ struct i2c_driver; struct i2c_client_address_data; union i2c_smbus_data; +struct i2c_op_q_entry; /* * The master routines are the ones normally used to transmit data to devices @@ -229,7 +232,6 @@ /* data fields that are valid for all devices */ struct semaphore bus_lock; - struct semaphore clist_lock; int timeout; int retries; @@ -242,6 +244,7 @@ #endif /* def CONFIG_PROC_FS */ int nr; + struct semaphore clist_lock; struct list_head clients; struct list_head list; char name[I2C_NAME_SIZE]; @@ -395,6 +398,92 @@ __u8 *buf; /* pointer to msg data */ }; +/* + * Hold a queue of I2C operations to perform, and used to pass data + * around. + */ +typedef void (*i2c_op_done_cb)(struct i2c_op_q_entry *entry); + +#define I2C_OP_I2C 0 +#define I2C_OP_SMBUS 1 +struct i2c_op_q_entry { + /* The result will be set to the result of the operation when + it completes. */ + s32 result; + + /**************************************************************/ + /* Public interface. The user should set these up (and the + proper structure below). */ + int xfer_type; + + /* Handler may be called from interrupt context, so be + careful. */ + i2c_op_done_cb handler; + void *handler_data; + + /* Note that this is not a union because an smbus operation + may be converted into an i2c operation (thus both + structures will be used). The data in these may be changd + by the driver. */ + struct { + struct i2c_msg *msgs; + int num; + } i2c; + struct { + /* Addr and flags are filled in by the non-blocking + send routine that takes a client. */ + u16 addr; + unsigned short flags; + + char read_write; + u8 command; + + /* Note that the size is *not* the length of the data. + It is the transaction type, like I2C_SMBUS_QUICK + and the ones after that below. If this is a block + transaction, the length of the rest of the data is + in the first byte of the data, for both transmit + and receive. */ + int size; + union i2c_smbus_data *data; + } smbus; + + /**************************************************************/ + /* For use by the bus interface. The bus interface sets the + timeout in microseconds until the next poll operation. + This *must* be set in the start operation. The time_left + and data can be used for anything the bus interface likes. + data will be set to NULL before being started so the bus + interface can use that to tell if it has been set up + yet. */ + unsigned int call_again_us; + long time_left; + void *data; + + /**************************************************************/ + /* Internals */ + struct list_head link; + struct completion *start; + atomic_t completed; + unsigned int started : 1; + unsigned int use_timer : 1; + u8 swpec; + u8 partial; + void (*complete)(struct i2c_adapter *adap, + struct i2c_op_q_entry *entry); + + /* It's wierd, but we use a usecount to track if an q entry is + in use and when it should be reported back to the user. */ + atomic_t usecount; + + /* These are here for SMBus emulation over I2C. I don't like + them taking this much room in the data structure, but they + need to be available in this case. */ + unsigned char msgbuf0[34]; + unsigned char msgbuf1[34]; + struct i2c_msg msg[2]; +}; + /* To determine what functionality is present */ #define I2C_FUNC_I2C 0x00000001 | |