diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index af0ae80ef324..65b1bc9586ab 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -25,6 +25,7 @@
 #include <linux/uaccess.h>
 #include <linux/vfio.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/vfio_pci_core.h>
 
@@ -1936,16 +1937,23 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_core_device *vdev, char __user
 ssize_t vfio_pci_config_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 			   size_t count, loff_t *ppos, bool iswrite)
 {
+	struct device *dev = &vdev->pdev->dev;
 	size_t done = 0;
 	int ret = 0;
 	loff_t pos = *ppos;
 
 	pos &= VFIO_PCI_OFFSET_MASK;
 
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret < 0)
+		return ret;
+
 	while (count) {
 		ret = vfio_config_do_rw(vdev, buf, count, &pos, iswrite);
-		if (ret < 0)
+		if (ret < 0) {
+			pm_runtime_put(dev);
 			return ret;
+		}
 
 		count -= ret;
 		done += ret;
@@ -1953,6 +1961,7 @@ ssize_t vfio_pci_config_rw(struct vfio_pci_core_device *vdev, char __user *buf,
 		pos += ret;
 	}
 
+	pm_runtime_put(dev);
 	*ppos += done;
 
 	return done;
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 05a68ca9d9e7..beac6e05f97f 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -234,7 +234,14 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat
 	ret = pci_set_power_state(pdev, state);
 
 	if (!ret) {
-		vdev->power_state_d3 = (pdev->current_state >= PCI_D3hot);
+		/*
+		 * If 'platform_pm_engaged' is true then 'power_state_d3' can
+		 * be cleared only when user makes the explicit request to
+		 * move out of low power state by using power management ioctl.
+		 */
+		if (!vdev->platform_pm_engaged)
+			vdev->power_state_d3 =
+				(pdev->current_state >= PCI_D3hot);
 
 		/* D3 might be unsupported via quirk, skip unless in D3 */
 		if (needs_save && pdev->current_state >= PCI_D3hot) {
@@ -266,6 +273,25 @@ static int vfio_pci_core_runtime_suspend(struct device *dev)
 {
 	struct vfio_pci_core_device *vdev = dev_get_drvdata(dev);
 
+	down_read(&vdev->memory_lock);
+
+	/* 'platform_pm_engaged' will be false if there are no users. */
+	if (!vdev->platform_pm_engaged) {
+		up_read(&vdev->memory_lock);
+		return 0;
+	}
+
+	/*
+	 * The user will move the device into D3hot state first before invoking
+	 * power management ioctl. Move the device into D0 state here and then
+	 * the pci-driver core runtime PM suspend will move the device into
+	 * low power state. Also, for the devices which have NoSoftRst-,
+	 * it will help in restoring the original state (saved locally in
+	 * 'vdev->pm_save').
+	 */
+	vfio_pci_set_power_state(vdev, PCI_D0);
+	up_read(&vdev->memory_lock);
+
 	/*
 	 * If INTx is enabled, then mask INTx before going into runtime
 	 * suspended state and unmask the same in the runtime resume.
@@ -395,6 +421,19 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
 
 	/*
 	 * This function can be invoked while the power state is non-D0.
+	 * This non-D0 power state can be with or without runtime PM.
+	 * Increment the usage count corresponding to pm_runtime_put()
+	 * called during setting of 'platform_pm_engaged'. The device will
+	 * wake up if it has already went into suspended state. Otherwise,
+	 * the next vfio_pci_set_power_state() will change the
+	 * device power state to D0.
+	 */
+	if (vdev->platform_pm_engaged) {
+		pm_runtime_resume_and_get(&pdev->dev);
+		vdev->platform_pm_engaged = false;
+	}
+
+	/*
 	 * This function calls __pci_reset_function_locked() which internally
 	 * can use pci_pm_reset() for the function reset. pci_pm_reset() will
 	 * fail if the power state is non-D0. Also, for the devices which
@@ -1192,6 +1231,80 @@ long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
 }
 EXPORT_SYMBOL_GPL(vfio_pci_core_ioctl);
 
+#ifdef CONFIG_PM
+static int vfio_pci_core_feature_pm(struct vfio_device *device, u32 flags,
+				    void __user *arg, size_t argsz)
+{
+	struct vfio_pci_core_device *vdev =
+		container_of(device, struct vfio_pci_core_device, vdev);
+	struct pci_dev *pdev = vdev->pdev;
+	struct vfio_device_feature_power_management vfio_pm = { 0 };
+	int ret = 0;
+
+	ret = vfio_check_feature(flags, argsz,
+				 VFIO_DEVICE_FEATURE_SET |
+				 VFIO_DEVICE_FEATURE_GET,
+				 sizeof(vfio_pm));
+	if (ret != 1)
+		return ret;
+
+	if (flags & VFIO_DEVICE_FEATURE_GET) {
+		down_read(&vdev->memory_lock);
+		vfio_pm.low_power_state = vdev->platform_pm_engaged ?
+				VFIO_DEVICE_LOW_POWER_STATE_ENTER :
+				VFIO_DEVICE_LOW_POWER_STATE_EXIT;
+		up_read(&vdev->memory_lock);
+		if (copy_to_user(arg, &vfio_pm, sizeof(vfio_pm)))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (copy_from_user(&vfio_pm, arg, sizeof(vfio_pm)))
+		return -EFAULT;
+
+	/*
+	 * The vdev power related fields are protected with memory_lock
+	 * semaphore.
+	 */
+	down_write(&vdev->memory_lock);
+	switch (vfio_pm.low_power_state) {
+	case VFIO_DEVICE_LOW_POWER_STATE_ENTER:
+		if (!vdev->power_state_d3 || vdev->platform_pm_engaged) {
+			ret = EINVAL;
+			break;
+		}
+
+		vdev->platform_pm_engaged = true;
+
+		/*
+		 * The pm_runtime_put() will be called again while returning
+		 * from ioctl after which the device can go into runtime
+		 * suspended.
+		 */
+		pm_runtime_put_noidle(&pdev->dev);
+		break;
+
+	case VFIO_DEVICE_LOW_POWER_STATE_EXIT:
+		if (!vdev->platform_pm_engaged) {
+			ret = EINVAL;
+			break;
+		}
+
+		vdev->platform_pm_engaged = false;
+		vdev->power_state_d3 = false;
+		pm_runtime_get_noresume(&pdev->dev);
+		break;
+
+	default:
+		ret = EINVAL;
+		break;
+	}
+
+	up_write(&vdev->memory_lock);
+	return ret;
+}
+#endif
+
 static int vfio_pci_core_feature_token(struct vfio_device *device, u32 flags,
 				       void __user *arg, size_t argsz)
 {
@@ -1226,6 +1339,10 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
 	switch (flags & VFIO_DEVICE_FEATURE_MASK) {
 	case VFIO_DEVICE_FEATURE_PCI_VF_TOKEN:
 		return vfio_pci_core_feature_token(device, flags, arg, argsz);
+#ifdef CONFIG_PM
+	case VFIO_DEVICE_FEATURE_POWER_MANAGEMENT:
+		return vfio_pci_core_feature_pm(device, flags, arg, argsz);
+#endif
 	default:
 		return -ENOTTY;
 	}
@@ -2189,6 +2306,15 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
 		goto err_unlock;
 	}
 
+	/*
+	 * Some of the devices in the dev_set can be in the runtime suspended
+	 * state. Increment the usage count for all the devices in the dev_set
+	 * before reset and decrement the same after reset.
+	 */
+	ret = vfio_pci_dev_set_pm_runtime_get(dev_set);
+	if (ret)
+		goto err_unlock;
+
 	list_for_each_entry(cur_vma, &dev_set->device_list, vdev.dev_set_list) {
 		/*
 		 * Test whether all the affected devices are contained by the
@@ -2244,6 +2370,9 @@ static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
 		else
 			mutex_unlock(&cur->vma_lock);
 	}
+
+	list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list)
+		pm_runtime_put(&cur->pdev->dev);
 err_unlock:
 	mutex_unlock(&dev_set->lock);
 	return ret;
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index e84f31e44238..337983a877d6 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -126,6 +126,7 @@ struct vfio_pci_core_device {
 	bool			needs_pm_restore;
 	bool			power_state_d3;
 	bool			pm_intx_masked;
+	bool			platform_pm_engaged;
 	struct pci_saved_state	*pci_saved_state;
 	struct pci_saved_state	*pm_save;
 	int			ioeventfds_nr;
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index fea86061b44e..53ff890dbd27 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -986,6 +986,24 @@ enum vfio_device_mig_state {
 	VFIO_DEVICE_STATE_RUNNING_P2P = 5,
 };
 
+/*
+ * Use platform-based power management for moving the device into low power
+ * state.  This low power state is device specific.
+ *
+ * For PCI, this low power state is D3cold.  The native PCI power management
+ * does not support the D3cold power state.  For moving the device into D3cold
+ * state, change the PCI state to D3hot with standard configuration registers
+ * and then call this IOCTL to setting the D3cold state.  Similarly, if the
+ * device in D3cold state, then call this IOCTL to exit from D3cold state.
+ */
+struct vfio_device_feature_power_management {
+#define VFIO_DEVICE_LOW_POWER_STATE_EXIT	0x0
+#define VFIO_DEVICE_LOW_POWER_STATE_ENTER	0x1
+	__u64	low_power_state;
+};
+
+#define VFIO_DEVICE_FEATURE_POWER_MANAGEMENT	3
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**
