diff mbox

[v2,2/7] pm_smbus: Add the ability to force block transfer enable

Message ID 1464798420-12018-3-git-send-email-minyard@acm.org
State New
Headers show

Commit Message

Corey Minyard June 1, 2016, 4:26 p.m. UTC
From: Corey Minyard <cminyard@mvista.com>


The PIIX4 hardware has block transfer buffer always enabled in
the hardware, but the i801 does not.  Add a parameter to pm_smbus_init
to force on the block transfer so the PIIX4 handler can enable this
by default, as it was disabled by default before.

Signed-off-by: Corey Minyard <cminyard@mvista.com>

---
 hw/acpi/piix4.c           |   2 +-
 hw/i2c/pm_smbus.c         | 111 +++++++++++++++++++++++++++++-----------------
 hw/i2c/smbus_ich9.c       |   2 +-
 include/hw/i2c/pm_smbus.h |   3 +-
 4 files changed, 74 insertions(+), 44 deletions(-)

-- 
2.7.4
diff mbox

Patch

diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index b3e3bb3..d4ae836 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -467,7 +467,7 @@  static void piix4_pm_realize(PCIDevice *dev, Error **errp)
     pci_conf[0x90] = s->smb_io_base | 1;
     pci_conf[0x91] = s->smb_io_base >> 8;
     pci_conf[0xd2] = 0x09;
-    pm_smbus_init(DEVICE(dev), &s->smb);
+    pm_smbus_init(DEVICE(dev), &s->smb, true);
     memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1);
     memory_region_add_subregion(pci_address_space_io(dev),
                                 s->smb_io_base, &s->smb.io);
diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c
index 17d253f..4099c07 100644
--- a/hw/i2c/pm_smbus.c
+++ b/hw/i2c/pm_smbus.c
@@ -132,34 +132,44 @@  static void smb_transaction(PMSMBus *s)
         if (read) {
             ret = smbus_read_block(bus, addr, cmd, s->smb_data,
                                    sizeof(s->smb_data), !i2c_enable);
+            if (ret < 0) {
+                goto error;
+            }
             s->smb_index = 0;
             s->op_done = false;
             if (s->smb_auxctl & AUX_BLK) {
                 s->smb_stat |= STS_INTR;
             } else {
-                s->smb_stat |= STS_HOST_BUSY;
+                s->smb_blkdata = s->smb_data[0];
+                s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
             }
-            goto datablk;
+            s->smb_data0 = ret;
+            goto out;
         } else {
-            if (s->smb_auxctl & AUX_BLK || s->smb_index == s->smb_data0) {
+            if (s->smb_auxctl & AUX_BLK) {
                 if (s->smb_index != s->smb_data0) {
                     s->smb_index = 0;
                     goto error;
                 }
                 /* Data is already all written to the queue, just do
                    the operation. */
+                s->smb_index = 0;
                 ret = smbus_write_block(bus, addr, cmd, s->smb_data,
                                         s->smb_data0, !i2c_enable);
+                if (ret < 0) {
+                    goto error;
+                }
                 s->op_done = true;
-                s->smb_index = 0;
                 s->smb_stat |= STS_INTR;
                 s->smb_stat &= ~STS_HOST_BUSY;
             } else {
                 s->op_done = false;
-                s->smb_stat |= STS_HOST_BUSY;
+                s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
+                s->smb_data[0] = s->smb_blkdata;
+                s->smb_index = 0;
                 ret = 0;
             }
-            goto doneblk;
+            goto out;
         }
         break;
     default:
@@ -181,18 +191,8 @@  done:
     if (ret < 0) {
         goto error;
     }
-    s->smb_stat |= STS_BYTE_DONE | STS_INTR;
-    return;
-datablk:
-    if (ret < 0) {
-        goto error;
-    }
-    s->smb_data0 = ret;
-doneblk:
-    if (ret < 0) {
-        goto error;
-    }
-    s->smb_stat |= STS_BYTE_DONE;
+    s->smb_stat |= STS_INTR;
+out:
     return;
 
 error:
@@ -217,12 +217,43 @@  static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
     case SMBHSTSTS:
         s->smb_stat &= ~(val & ~STS_HOST_BUSY);
         if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
-            s->smb_stat |= STS_BYTE_DONE;
+            uint8_t read = s->smb_addr & 0x01;
+
+            s->smb_index++;
+            if (!read && s->smb_index == s->smb_data0) {
+                uint8_t prot = (s->smb_ctl >> 2) & 0x07;
+                uint8_t cmd = s->smb_cmd;
+                uint8_t addr = s->smb_addr >> 1;
+                int ret;
+
+                ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
+                                        s->smb_data0,
+                                        prot != PROT_I2C_BLOCK_DATA);
+                if (ret < 0) {
+                    s->smb_stat |= STS_DEV_ERR;
+                    goto out;
+                }
+                s->op_done = true;
+                s->smb_stat |= STS_INTR;
+                s->smb_stat &= ~STS_HOST_BUSY;
+            } else if (!read) {
+                s->smb_data[s->smb_index] = s->smb_blkdata;
+                s->smb_stat |= STS_BYTE_DONE;
+            } if (s->smb_ctl & CTL_LAST_BYTE) {
+                s->op_done = true;
+                s->smb_blkdata = s->smb_data[s->smb_index];
+                s->smb_index = 0;
+                s->smb_stat |= STS_INTR;
+                s->smb_stat &= ~STS_HOST_BUSY;
+            } else {
+                s->smb_blkdata = s->smb_data[s->smb_index];
+                s->smb_stat |= STS_BYTE_DONE;
+            }
         }
         break;
     case SMBHSTCNT:
-        s->smb_ctl = val;
-        if (s->smb_ctl & CTL_START) {
+        s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */
+        if (val & CTL_START) {
             if (!s->op_done) {
                 s->smb_index = 0;
                 s->op_done = true;
@@ -252,13 +283,10 @@  static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
         if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
             s->smb_index = 0;
         }
-        s->smb_data[s->smb_index++] = val;
-        if (!(s->smb_auxctl & AUX_BLK) && s->smb_ctl & CTL_START &&
-            !s->op_done && s->smb_index == s->smb_data0) {
-            smb_transaction(s);
-            s->op_done = true;
-            s->smb_stat |= STS_INTR;
-            s->smb_stat &= ~STS_HOST_BUSY;
+        if (s->smb_auxctl & AUX_BLK) {
+            s->smb_data[s->smb_index++] = val;
+        } else {
+            s->smb_blkdata = val;
         }
         break;
     case SMBAUXCTL:
@@ -268,6 +296,7 @@  static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
         break;
     }
 
+ out:
     if (s->set_irq) {
         s->set_irq(s, smb_irq_value(s));
     }
@@ -301,18 +330,15 @@  static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
         if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
             s->smb_index = 0;
         }
-        val = s->smb_data[s->smb_index++];
-        if (s->smb_ctl & CTL_START && !s->op_done &&
-            s->smb_index == s->smb_data0) {
-            s->op_done = true;
-            s->smb_index = 0;
-            s->smb_stat &= ~STS_HOST_BUSY;
-        }
-        if (s->smb_ctl & CTL_LAST_BYTE) {
-            s->op_done = true;
-            s->smb_index = 0;
-            s->smb_stat |= STS_INTR;
-            s->smb_stat &= ~STS_HOST_BUSY;
+        if (s->smb_auxctl & AUX_BLK) {
+            val = s->smb_data[s->smb_index++];
+            if (!s->op_done && s->smb_index == s->smb_data0) {
+                s->op_done = true;
+                s->smb_index = 0;
+                s->smb_stat &= ~STS_HOST_BUSY;
+            }
+        } else {
+            val = s->smb_blkdata;
         }
         break;
     case SMBAUXCTL:
@@ -366,11 +392,14 @@  const VMStateDescription pmsmb_vmstate = {
     }
 };
 
-void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
 {
     smb->op_done = true;
     smb->reset = pm_smbus_reset;
     smb->smbus = i2c_init_bus(parent, "i2c");
+    if (force_aux_blk) {
+        smb->smb_auxctl |= AUX_BLK;
+    }
     memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
                           "pm-smbus", 64);
 }
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
index a01cb92..2c4f297 100644
--- a/hw/i2c/smbus_ich9.c
+++ b/hw/i2c/smbus_ich9.c
@@ -90,7 +90,7 @@  static void ich9_smbus_realize(PCIDevice *d, Error **errp)
     pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
     /* TODO bar0, bar1: 64bit BAR support*/
 
-    pm_smbus_init(&d->qdev, &s->smb);
+    pm_smbus_init(&d->qdev, &s->smb, false);
     pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
                      &s->smb.io);
 }
diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h
index bfe740a..1005d1f 100644
--- a/include/hw/i2c/pm_smbus.h
+++ b/include/hw/i2c/pm_smbus.h
@@ -14,6 +14,7 @@  typedef struct PMSMBus {
     uint8_t smb_data0;
     uint8_t smb_data1;
     uint8_t smb_data[PM_SMBUS_MAX_MSG_SIZE];
+    uint8_t smb_blkdata;
     uint8_t smb_auxctl;
     uint32_t smb_index;
 
@@ -32,7 +33,7 @@  typedef struct PMSMBus {
     bool op_done;
 } PMSMBus;
 
-void pm_smbus_init(DeviceState *parent, PMSMBus *smb);
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk);
 
 extern const VMStateDescription pmsmb_vmstate;