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;
}
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)
{