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>
This commit is contained in:
Aaron Durbin 2018-01-30 17:29:08 -07:00
parent f18c0415ef
commit 97d58bcc09
1 changed files with 34 additions and 17 deletions

View File

@ -345,29 +345,28 @@ static int dw_i2c_transfer_byte(struct dw_i2c_regs *regs,
return 0; return 0;
} }
int dw_i2c_transfer(unsigned int bus, static int _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segments,
const struct i2c_msg *segments, size_t count) size_t count)
{ {
struct stopwatch sw; struct stopwatch sw;
struct dw_i2c_regs *regs; struct dw_i2c_regs *regs;
size_t byte; size_t byte;
int ret = -1; int ret = -1;
if (count == 0 || !segments)
return -1;
regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus); regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
if (!regs) { if (!regs) {
printk(BIOS_ERR, "I2C bus %u base address not found\n", bus); printk(BIOS_ERR, "I2C bus %u base address not found\n", bus);
return -1; 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)) { /* Set target slave address */
printk(BIOS_ERR, "I2C timeout waiting for bus %u idle\n", bus); write32(&regs->target_addr, segments->slave);
goto out;
} dw_i2c_enable(regs);
/* Process each segment */ /* Process each segment */
while (count--) { while (count--) {
@ -378,13 +377,6 @@ int dw_i2c_transfer(unsigned int bus,
segments->len); 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 */ /* Read or write each byte in segment */
for (byte = 0; byte < segments->len; byte++) { for (byte = 0; byte < segments->len; byte++) {
/* /*
@ -448,6 +440,31 @@ out:
return ret; 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 */ /* Global I2C bus handler, defined in include/device/i2c_simple.h */
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *msg, int count) int platform_i2c_transfer(unsigned int bus, struct i2c_msg *msg, int count)
{ {