sb/intel/common: SMBus block_cmd_loop()
For debugging prints, report the number of loop spent
polling instead.
Change-Id: I61865aaafc9f41acd85c5dc98817d12642965ba4
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: https://review.coreboot.org/c/21121
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/southbridge/intel/common/smbus.c b/src/southbridge/intel/common/smbus.c
index 1302798..12fb9ea 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,13 +226,70 @@
return complete_command(smbus_base);
}
+static int block_cmd_loop(unsigned int smbus_base,
+ u8 *buf, const unsigned int max_bytes, int flags)
+{
+ u8 status;
+ unsigned int loops = SMBUS_TIMEOUT;
+ int ret, bytes = 0;
+ int is_write_cmd = flags & BLOCK_WRITE;
+ int sw_drives_nak = flags & BLOCK_I2C;
+
+ /* Hardware limitations. */
+ if (flags == (BLOCK_WRITE | BLOCK_I2C))
+ return SMBUS_ERROR;
+
+ /* Poll for transaction completion */
+ do {
+ status = inb(smbus_base + SMBHSTSTAT);
+
+ if (status & SMBHSTSTS_BYTE_DONE) { /* Byte done */
+
+ 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
+ * has been reached or LAST_BYTE was set.
+ */
+ 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, max_bytes, SMBUS_TIMEOUT - loops);
+
+ if (loops == 0)
+ return recover_master(smbus_base,
+ SMBUS_WAIT_UNTIL_DONE_TIMEOUT);
+
+ ret = cb_err_from_stat(status);
+ if (ret < 0)
+ return ret;
+
+ return bytes;
+}
+
int do_smbus_block_read(unsigned int smbus_base, u8 device, u8 cmd,
unsigned int max_bytes, u8 *buf)
{
- u8 status;
int ret, slave_bytes;
- int bytes_read = 0;
- unsigned int loops = SMBUS_TIMEOUT;
max_bytes = MIN(SMBUS_BLOCK_MAXLEN, max_bytes);
@@ -249,50 +311,22 @@
return ret;
/* Poll for transaction completion */
- do {
- status = inb(smbus_base + SMBHSTSTAT);
-
- if (status & SMBHSTSTS_BYTE_DONE) { /* Byte done */
-
- if (bytes_read < max_bytes) {
- *buf++ = inb(smbus_base + SMBBLKDAT);
- bytes_read++;
- }
-
- /* Engine internally completes the transaction
- * and clears HOST_BUSY flag once the byte count
- * from slave is reached.
- */
- outb(SMBHSTSTS_BYTE_DONE, smbus_base + SMBHSTSTAT);
- }
- } while (--loops && !host_completed(status));
-
- /* Post-check we received complete message. */
- slave_bytes = inb(smbus_base + SMBHSTDAT0);
-
- dprintk("%s: status = %02x, len = %d / %d, loops = %d\n",
- __func__, status, bytes_read, slave_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, max_bytes, BLOCK_READ);
if (ret < 0)
return ret;
- if (bytes_read < slave_bytes)
+ /* 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 @@
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 @@
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;
}