diff --git a/src/drivers/i2c/designware/dw_i2c.c b/src/drivers/i2c/designware/dw_i2c.c index af769ca02e..3db34ff7b8 100644 --- a/src/drivers/i2c/designware/dw_i2c.c +++ b/src/drivers/i2c/designware/dw_i2c.c @@ -345,29 +345,28 @@ static int dw_i2c_transfer_byte(struct dw_i2c_regs *regs, return 0; } -int dw_i2c_transfer(unsigned int bus, - const struct i2c_msg *segments, size_t count) +static int _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segments, + size_t count) { struct stopwatch sw; struct dw_i2c_regs *regs; size_t byte; int ret = -1; - if (count == 0 || !segments) - return -1; - regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus); if (!regs) { printk(BIOS_ERR, "I2C bus %u base address not found\n", bus); return -1; } - dw_i2c_enable(regs); + /* The assumption is that the host controller is disabled -- either + after running this function or from performing the intialization + sequence in dw_i2c_init(). */ - if (dw_i2c_wait_for_bus_idle(regs)) { - printk(BIOS_ERR, "I2C timeout waiting for bus %u idle\n", bus); - goto out; - } + /* Set target slave address */ + write32(®s->target_addr, segments->slave); + + dw_i2c_enable(regs); /* Process each segment */ while (count--) { @@ -378,13 +377,6 @@ int dw_i2c_transfer(unsigned int bus, segments->len); } - /* Set target slave address */ - if (read32(®s->target_addr) != segments->slave) { - dw_i2c_disable(regs); - write32(®s->target_addr, segments->slave); - dw_i2c_enable(regs); - } - /* Read or write each byte in segment */ for (byte = 0; byte < segments->len; byte++) { /* @@ -448,6 +440,31 @@ out: return ret; } +int dw_i2c_transfer(unsigned int bus, const struct i2c_msg *msg, size_t count) +{ + const struct i2c_msg *orig_msg = msg; + size_t i; + size_t start; + uint16_t addr; + + if (count == 0 || !msg) + return -1; + + /* Break up the transfers at the differing slave address boundary. */ + addr = orig_msg->slave; + + for (i = 0, start = 0; i < count; i++, msg++) { + if (addr != msg->slave) { + if (_dw_i2c_transfer(bus, &orig_msg[start], i - start)) + return -1; + start = i; + addr = msg->slave; + } + } + + return _dw_i2c_transfer(bus, &orig_msg[start], count - start); +} + /* Global I2C bus handler, defined in include/device/i2c_simple.h */ int platform_i2c_transfer(unsigned int bus, struct i2c_msg *msg, int count) {