i2c: i801: Add runtime PM support with autosuspend
authorJarkko Nikula <jarkko.nikula@linux.intel.com>
Thu, 10 Mar 2016 12:12:22 +0000 (14:12 +0200)
committerWolfram Sang <wsa@the-dreams.de>
Tue, 12 Apr 2016 21:21:25 +0000 (23:21 +0200)
Allow runtime PM so that PM and PCI core can put the device into low-power
state when idle and resume it back when needed in those platforms that
support PM for i801 device.

Enable also autosuspend with 1 second delay in order to not needlessly
toggle power state on and off if there are multiple transactions during
short time.

Device is resumed at the beginning of bus access and marked idle ready
for autosuspend at the end of it.

Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Tested-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
drivers/i2c/busses/i2c-i801.c

index 7a29f1436ddbad5216518a744d792597955b3efc..64b1208bca5e15af2295e9b26b9581af67f474c4 100644 (file)
@@ -94,6 +94,7 @@
 #include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/itco_wdt.h>
+#include <linux/pm_runtime.h>
 
 #if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
                defined CONFIG_DMI
@@ -714,9 +715,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
 {
        int hwpec;
        int block = 0;
-       int ret, xact = 0;
+       int ret = 0, xact = 0;
        struct i801_priv *priv = i2c_get_adapdata(adap);
 
+       pm_runtime_get_sync(&priv->pci_dev->dev);
+
        hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
                && size != I2C_SMBUS_QUICK
                && size != I2C_SMBUS_I2C_BLOCK_DATA;
@@ -773,7 +776,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
        default:
                dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
                        size);
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               goto out;
        }
 
        if (hwpec)      /* enable/disable hardware PEC */
@@ -796,11 +800,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
                       ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
 
        if (block)
-               return ret;
+               goto out;
        if (ret)
-               return ret;
+               goto out;
        if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
-               return 0;
+               goto out;
 
        switch (xact & 0x7f) {
        case I801_BYTE: /* Result put in SMBHSTDAT0 */
@@ -812,7 +816,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
                             (inb_p(SMBHSTDAT1(priv)) << 8);
                break;
        }
-       return 0;
+
+out:
+       pm_runtime_mark_last_busy(&priv->pci_dev->dev);
+       pm_runtime_put_autosuspend(&priv->pci_dev->dev);
+       return ret;
 }
 
 
@@ -1413,6 +1421,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
        pci_set_drvdata(dev, priv);
 
+       pm_runtime_set_autosuspend_delay(&dev->dev, 1000);
+       pm_runtime_use_autosuspend(&dev->dev);
+       pm_runtime_put_autosuspend(&dev->dev);
+       pm_runtime_allow(&dev->dev);
+
        return 0;
 }
 
@@ -1420,6 +1433,9 @@ static void i801_remove(struct pci_dev *dev)
 {
        struct i801_priv *priv = pci_get_drvdata(dev);
 
+       pm_runtime_forbid(&dev->dev);
+       pm_runtime_get_noresume(&dev->dev);
+
        i801_del_mux(priv);
        i2c_del_adapter(&priv->adapter);
        pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);