sb/intel/common: SMBus execute_command()

Implement the common start of transaction.

Fixes a problem where smbus_wait_until_active()
can miss SMBHSTSTS_HOST_BUSY being set, if
transaction completes very fast. Or if we are
single-stepping or executing under SerialIce
emulation.

Change-Id: Icb27d7d6a1c54968950ca292dbae05415f97e461
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: https://review.coreboot.org/c/21119
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
This commit is contained in:
Kyösti Mälkki 2017-08-20 21:36:15 +03:00 committed by Patrick Georgi
parent 957511cd92
commit a2dcf735e4
1 changed files with 37 additions and 40 deletions

View File

@ -101,19 +101,31 @@ static int setup_command(unsigned int smbus_base, u8 ctrl, u8 xmitadd)
return 0;
}
static int smbus_wait_until_active(u16 smbus_base)
static int execute_command(unsigned int smbus_base)
{
unsigned long loops;
loops = SMBUS_TIMEOUT;
unsigned int loops = SMBUS_TIMEOUT;
u8 status;
/* Start the command. */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* Poll for it to start. */
do {
unsigned char val;
smbus_delay();
val = inb(smbus_base + SMBHSTSTAT);
if ((val & SMBHSTSTS_HOST_BUSY)) {
break;
}
} while (--loops);
return loops ? 0 : -1;
/* If we poll too slow, we could miss HOST_BUSY flag
* set and detect INTR or x_ERR flags instead here.
*/
status = inb(smbus_base + SMBHSTSTAT);
status &= ~(SMBHSTSTS_SMBALERT_STS | SMBHSTSTS_INUSE_STS);
} while (--loops && status == 0);
if (loops == 0)
return recover_master(smbus_base,
SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT);
return 0;
}
static int smbus_wait_until_done(u16 smbus_base)
@ -149,12 +161,9 @@ int do_smbus_read_byte(unsigned int smbus_base, u8 device,
outb(0, smbus_base + SMBHSTDAT0);
/* Start the command */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* poll for it to start */
if (smbus_wait_until_active(smbus_base) < 0)
return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
ret = execute_command(smbus_base);
if (ret < 0)
return ret;
/* Poll for transaction completion */
if (smbus_wait_until_done(smbus_base) < 0)
@ -190,12 +199,9 @@ int do_smbus_write_byte(unsigned int smbus_base, u8 device,
outb(data, smbus_base + SMBHSTDAT0);
/* Start the command */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* poll for it to start */
if (smbus_wait_until_active(smbus_base) < 0)
return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
ret = execute_command(smbus_base);
if (ret < 0)
return ret;
/* Poll for transaction completion */
if (smbus_wait_until_done(smbus_base) < 0)
@ -236,12 +242,9 @@ int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd,
outb(0, smbus_base + SMBHSTDAT0);
/* Start the command */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* poll for it to start */
if (smbus_wait_until_active(smbus_base) < 0)
return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
ret = execute_command(smbus_base);
if (ret < 0)
return ret;
/* Poll for transaction completion */
do {
@ -306,12 +309,9 @@ int do_smbus_block_write(unsigned int smbus_base, u8 device, u8 cmd,
outb(*buf++, smbus_base + SMBBLKDAT);
/* Start the command */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* poll for it to start */
if (smbus_wait_until_active(smbus_base) < 0)
return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
ret = execute_command(smbus_base);
if (ret < 0)
return ret;
/* Poll for transaction completion */
do {
@ -367,12 +367,9 @@ int do_i2c_block_read(unsigned int smbus_base, u8 device,
outb(offset, smbus_base + SMBHSTDAT1);
/* Start the command */
outb((inb(smbus_base + SMBHSTCTL) | SMBHSTCNT_START),
smbus_base + SMBHSTCTL);
/* poll for it to start */
if (smbus_wait_until_active(smbus_base) < 0)
return SMBUS_WAIT_UNTIL_ACTIVE_TIMEOUT;
ret = execute_command(smbus_base);
if (ret < 0)
return ret;
/* Poll for transaction completion */
do {