summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/southbridge/intel/common/smbus.c172
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;
}