diff options
author | Aaron Durbin <adurbin@chromium.org> | 2018-01-30 17:29:08 -0700 |
---|---|---|
committer | Aaron Durbin <adurbin@chromium.org> | 2018-02-02 05:05:39 +0000 |
commit | 97d58bcc09b2a462d0bd55d1f675b687277b04b7 (patch) | |
tree | 8f36fb65807aba4b9f94421d93505197d07e6d87 /src/drivers/i2c | |
parent | f18c0415efad4bca3b900e5c0658238a48afe3e4 (diff) |
drivers/i2c/designware: conform to controller restrictions
The designware i2c controller indicates that the slave address
shouldn't be programmed while the controller is enabled. Therefore,
switch the ordering of the slave target address and the enable.
Additionally, ensure the controller is disabled prior to the
start of the slave programming sequence.
Lastly, chunk up the i2c_msg segments at differing slave address
boundaries. That allows for simpler programming for the controller
by only doing one slave address transaction chunk at a time.
BUG=b:70232394,b:69250772
Change-Id: Iebc08e2db847cb182fad98e0ff3d799b9a64aca7
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/23513
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Subrata Banik <subrata.banik@intel.com>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Diffstat (limited to 'src/drivers/i2c')
-rw-r--r-- | src/drivers/i2c/designware/dw_i2c.c | 51 |
1 files changed, 34 insertions, 17 deletions
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) { |