diff options
author | Kyösti Mälkki <kyosti.malkki@gmail.com> | 2017-08-20 21:36:15 +0300 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2019-01-24 13:37:46 +0000 |
commit | a2dcf735e4dcfe23f641344c4808f31be9f7f9d2 (patch) | |
tree | 44b12afa90daff3955f24421be39de61dddc4831 /src/southbridge/intel/common | |
parent | 957511cd9235f0cabaf61d3ea275a9ed69d2dc45 (diff) | |
download | coreboot-a2dcf735e4dcfe23f641344c4808f31be9f7f9d2.tar.xz |
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>
Diffstat (limited to 'src/southbridge/intel/common')
-rw-r--r-- | src/southbridge/intel/common/smbus.c | 77 |
1 files changed, 37 insertions, 40 deletions
diff --git a/src/southbridge/intel/common/smbus.c b/src/southbridge/intel/common/smbus.c index b2a78c9fc1..fb7f8e5754 100644 --- a/src/southbridge/intel/common/smbus.c +++ b/src/southbridge/intel/common/smbus.c @@ -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 { |