aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/i2c')
-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)
{