diff options
-rw-r--r-- | src/southbridge/intel/common/smbus.c | 172 |
1 files changed, 77 insertions, 95 deletions
diff --git a/src/southbridge/intel/common/smbus.c b/src/southbridge/intel/common/smbus.c index 1302798e06..12fb9ea3c2 100644 --- a/src/southbridge/intel/common/smbus.c +++ b/src/southbridge/intel/common/smbus.c @@ -60,6 +60,11 @@ #define SMBUS_TIMEOUT (10 * 1000 * 100) #define SMBUS_BLOCK_MAXLEN 32 +/* block_cmd_loop flags */ +#define BLOCK_READ 0 +#define BLOCK_WRITE (1 << 0) +#define BLOCK_I2C (1 << 1) + static void smbus_delay(void) { inb(0x80); @@ -221,32 +226,18 @@ int do_smbus_write_byte(unsigned int smbus_base, u8 device, return complete_command(smbus_base); } -int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd, - unsigned int max_bytes, u8 *buf) +static int block_cmd_loop(unsigned int smbus_base, + u8 *buf, const unsigned int max_bytes, int flags) { u8 status; - int ret, slave_bytes; - int bytes_read = 0; unsigned int loops = SMBUS_TIMEOUT; + int ret, bytes = 0; + int is_write_cmd = flags & BLOCK_WRITE; + int sw_drives_nak = flags & BLOCK_I2C; - max_bytes = MIN(SMBUS_BLOCK_MAXLEN, max_bytes); - - /* Set up for a block data read. */ - ret = setup_command(smbus_base, I801_BLOCK_DATA, XMIT_READ(device)); - if (ret < 0) - return ret; - - /* Set the command/address... */ - outb(cmd, smbus_base + SMBHSTCMD); - - /* Reset number of bytes to transfer so we notice later it - * was really updated with the transaction. */ - outb(0, smbus_base + SMBHSTDAT0); - - /* Start the command */ - ret = execute_command(smbus_base); - if (ret < 0) - return ret; + /* Hardware limitations. */ + if (flags == (BLOCK_WRITE | BLOCK_I2C)) + return SMBUS_ERROR; /* Poll for transaction completion */ do { @@ -254,24 +245,35 @@ int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd, if (status & SMBHSTSTS_BYTE_DONE) { /* Byte done */ - if (bytes_read < max_bytes) { - *buf++ = inb(smbus_base + SMBBLKDAT); - bytes_read++; + if (is_write_cmd) { + bytes++; + if (bytes < max_bytes) + outb(*buf++, smbus_base + SMBBLKDAT); + } else { + if (bytes < max_bytes) + *buf++ = inb(smbus_base + SMBBLKDAT); + bytes++; + + /* Indicate that next byte is the last one. */ + if (sw_drives_nak && (bytes + 1 >= max_bytes)) { + outb(inb(smbus_base + SMBHSTCTL) + | SMBHSTCNT_LAST_BYTE, + smbus_base + SMBHSTCTL); + } + } /* Engine internally completes the transaction * and clears HOST_BUSY flag once the byte count - * from slave is reached. + * has been reached or LAST_BYTE was set. */ outb(SMBHSTSTS_BYTE_DONE, smbus_base + SMBHSTSTAT); } - } while (--loops && !host_completed(status)); - /* Post-check we received complete message. */ - slave_bytes = inb(smbus_base + SMBHSTDAT0); + } while (--loops && !host_completed(status)); dprintk("%s: status = %02x, len = %d / %d, loops = %d\n", - __func__, status, bytes_read, slave_bytes, loops); + __func__, status, bytes, max_bytes, SMBUS_TIMEOUT - loops); if (loops == 0) return recover_master(smbus_base, @@ -281,18 +283,50 @@ int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd, if (ret < 0) return ret; - if (bytes_read < slave_bytes) + return bytes; +} + +int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd, + unsigned int max_bytes, u8 *buf) +{ + int ret, slave_bytes; + + max_bytes = MIN(SMBUS_BLOCK_MAXLEN, max_bytes); + + /* Set up for a block data read. */ + ret = setup_command(smbus_base, I801_BLOCK_DATA, XMIT_READ(device)); + if (ret < 0) + return ret; + + /* Set the command/address... */ + outb(cmd, smbus_base + SMBHSTCMD); + + /* Reset number of bytes to transfer so we notice later it + * was really updated with the transaction. */ + outb(0, smbus_base + SMBHSTDAT0); + + /* Start the command */ + ret = execute_command(smbus_base); + if (ret < 0) + return ret; + + /* Poll for transaction completion */ + ret = block_cmd_loop(smbus_base, buf, max_bytes, BLOCK_READ); + if (ret < 0) + return ret; + + /* Post-check we received complete message. */ + slave_bytes = inb(smbus_base + SMBHSTDAT0); + if (ret < slave_bytes) return SMBUS_ERROR; - return bytes_read; + return ret; } int do_smbus_block_write(unsigned int smbus_base, u8 device, u8 cmd, const unsigned int bytes, const u8 *buf) { - u8 status; - int ret, bytes_sent = 0; - unsigned int loops = SMBUS_TIMEOUT; + int ret; if (bytes > SMBUS_BLOCK_MAXLEN) return SMBUS_ERROR; @@ -319,46 +353,21 @@ int do_smbus_block_write(unsigned int smbus_base, u8 device, u8 cmd, return ret; /* Poll for transaction completion */ - do { - status = inb(smbus_base + SMBHSTSTAT); - - if (status & SMBHSTSTS_BYTE_DONE) { - bytes_sent++; - if (bytes_sent < bytes) - outb(*buf++, smbus_base + SMBBLKDAT); - - /* Engine internally completes the transaction - * and clears HOST_BUSY flag once the byte count - * has been reached. - */ - outb(SMBHSTSTS_BYTE_DONE, smbus_base + SMBHSTSTAT); - } - } while (--loops && !host_completed(status)); - - dprintk("%s: status = %02x, len = %d / %d, loops = %d\n", - __func__, status, bytes_sent, bytes, loops); - - if (loops == 0) - return recover_master(smbus_base, - SMBUS_WAIT_UNTIL_DONE_TIMEOUT); - - ret = cb_err_from_stat(status); + ret = block_cmd_loop(smbus_base, (u8 *)buf, bytes, BLOCK_WRITE); if (ret < 0) return ret; - if (bytes_sent < bytes) + if (ret < bytes) return SMBUS_ERROR; - return bytes_sent; + return ret; } /* Only since ICH5 */ int do_i2c_block_read(unsigned int smbus_base, u8 device, unsigned int offset, const unsigned int bytes, u8 *buf) { - u8 status; - int ret, bytes_read = 0; - unsigned int loops = SMBUS_TIMEOUT; + int ret; /* Set up for a i2c block data read. * @@ -380,40 +389,13 @@ int do_i2c_block_read(unsigned int smbus_base, u8 device, return ret; /* Poll for transaction completion */ - do { - status = inb(smbus_base + SMBHSTSTAT); - - if (status & SMBHSTSTS_BYTE_DONE) { - - if (bytes_read < bytes) { - *buf++ = inb(smbus_base + SMBBLKDAT); - bytes_read++; - } - - if (bytes_read + 1 >= bytes) { - /* indicate that next byte is the last one */ - outb(inb(smbus_base + SMBHSTCTL) - | SMBHSTCNT_LAST_BYTE, - smbus_base + SMBHSTCTL); - } - - outb(SMBHSTSTS_BYTE_DONE, smbus_base + SMBHSTSTAT); - } - } while (--loops && !host_completed(status)); - - dprintk("%s: status = %02x, len = %d / %d, loops = %d\n", - __func__, status, bytes_read, bytes, loops); - - if (loops == 0) - return recover_master(smbus_base, - SMBUS_WAIT_UNTIL_DONE_TIMEOUT); - - ret = cb_err_from_stat(status); + ret = block_cmd_loop(smbus_base, buf, bytes, BLOCK_READ | BLOCK_I2C); if (ret < 0) return ret; - if (bytes_read < bytes) + /* Post-check we received complete message. */ + if (ret < bytes) return SMBUS_ERROR; - return bytes_read; + return ret; } |