summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2018-01-30 17:29:08 -0700
committerAaron Durbin <adurbin@chromium.org>2018-02-02 05:05:39 +0000
commit97d58bcc09b2a462d0bd55d1f675b687277b04b7 (patch)
tree8f36fb65807aba4b9f94421d93505197d07e6d87 /src/drivers
parentf18c0415efad4bca3b900e5c0658238a48afe3e4 (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')
-rw-r--r--src/drivers/i2c/designware/dw_i2c.c51
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(&regs->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(&regs->target_addr) != segments->slave) {
- dw_i2c_disable(regs);
- write32(&regs->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)
{