i2c: i801: Add Block Write-Block Read Process Call support
authorAlexander Sverdlin <alexander.sverdlin@nokia.com>
Tue, 18 Jun 2019 17:06:50 +0000 (17:06 +0000)
committerWolfram Sang <wsa@the-dreams.de>
Fri, 21 Jun 2019 21:21:37 +0000 (23:21 +0200)
Add SMBUS 2.0 Block Write-Block Read Process Call command support.

Signed-off-by: Alexander Sverdlin <alexander.sverdlin@nokia.com>
Reviewed-by: Jean Delvare <jdelvare@suse.de>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Documentation/i2c/busses/i2c-i801
drivers/i2c/busses/i2c-i801.c

index ee9984f..cafb7e0 100644 (file)
@@ -88,7 +88,7 @@ SMBus controller.
 Process Call Support
 --------------------
 
-Not supported.
+Block process call is supported on the 82801EB (ICH5) and later chips.
 
 
 I2C Block Read Support
index 88e4dd2..70d48f4 100644 (file)
@@ -77,7 +77,7 @@
  * Software PEC                                no
  * Hardware PEC                                yes
  * Block buffer                                yes
- * Block process call transaction      no
+ * Block process call transaction      yes
  * I2C block read transaction          yes (doesn't use the block buffer)
  * Slave mode                          no
  * SMBus Host Notify                   yes
 #define I801_PROC_CALL         0x10    /* unimplemented */
 #define I801_BLOCK_DATA                0x14
 #define I801_I2C_BLOCK_DATA    0x18    /* ICH5 and later */
+#define I801_BLOCK_PROC_CALL   0x1C
 
 /* I801 Host Control register bits */
 #define SMBHSTCNT_INTREN       BIT(0)
@@ -518,10 +519,23 @@ static int i801_transaction(struct i801_priv *priv, int xact)
 
 static int i801_block_transaction_by_block(struct i801_priv *priv,
                                           union i2c_smbus_data *data,
-                                          char read_write, int hwpec)
+                                          char read_write, int command,
+                                          int hwpec)
 {
        int i, len;
        int status;
+       int xact = hwpec ? SMBHSTCNT_PEC_EN : 0;
+
+       switch (command) {
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               xact |= I801_BLOCK_PROC_CALL;
+               break;
+       case I2C_SMBUS_BLOCK_DATA:
+               xact |= I801_BLOCK_DATA;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
 
        inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */
 
@@ -533,12 +547,12 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
                        outb_p(data->block[i+1], SMBBLKDAT(priv));
        }
 
-       status = i801_transaction(priv, I801_BLOCK_DATA |
-                                 (hwpec ? SMBHSTCNT_PEC_EN : 0));
+       status = i801_transaction(priv, xact);
        if (status)
                return status;
 
-       if (read_write == I2C_SMBUS_READ) {
+       if (read_write == I2C_SMBUS_READ ||
+           command == I2C_SMBUS_BLOCK_PROC_CALL) {
                len = inb_p(SMBHSTDAT0(priv));
                if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
                        return -EPROTO;
@@ -676,6 +690,9 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
        int result;
        const struct i2c_adapter *adap = &priv->adapter;
 
+       if (command == I2C_SMBUS_BLOCK_PROC_CALL)
+               return -EOPNOTSUPP;
+
        result = i801_check_pre(priv);
        if (result < 0)
                return result;
@@ -807,7 +824,8 @@ static int i801_block_transaction(struct i801_priv *priv,
         && command != I2C_SMBUS_I2C_BLOCK_DATA
         && i801_set_block_buffer_mode(priv) == 0)
                result = i801_block_transaction_by_block(priv, data,
-                                                        read_write, hwpec);
+                                                        read_write,
+                                                        command, hwpec);
        else
                result = i801_block_transaction_byte_by_byte(priv, data,
                                                             read_write,
@@ -899,6 +917,15 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
                        outb_p(command, SMBHSTCMD(priv));
                block = 1;
                break;
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               /*
+                * Bit 0 of the slave address register always indicate a write
+                * command.
+                */
+               outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
+               outb_p(command, SMBHSTCMD(priv));
+               block = 1;
+               break;
        default:
                dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
                        size);
@@ -959,6 +986,8 @@ static u32 i801_func(struct i2c_adapter *adapter)
               I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
               I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
               ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
+              ((priv->features & FEATURE_BLOCK_PROC) ?
+               I2C_FUNC_SMBUS_BLOCK_PROC_CALL : 0) |
               ((priv->features & FEATURE_I2C_BLOCK_READ) ?
                I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) |
               ((priv->features & FEATURE_HOST_NOTIFY) ?
@@ -1654,6 +1683,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
        case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
        case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
        case PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS:
+               priv->features |= FEATURE_BLOCK_PROC;
                priv->features |= FEATURE_I2C_BLOCK_READ;
                priv->features |= FEATURE_IRQ;
                priv->features |= FEATURE_SMBUS_PEC;
@@ -1673,6 +1703,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
                priv->features |= FEATURE_IDF;
                /* fall through */
        default:
+               priv->features |= FEATURE_BLOCK_PROC;
                priv->features |= FEATURE_I2C_BLOCK_READ;
                priv->features |= FEATURE_IRQ;
                /* fall through */