From: Sungho Bae <[email protected]>

Implement the transport side of noirq system-sleep PM for virtio-mmio:

 - vm_reset_vqs(): iterate all virtqueues, call virtqueue_reinit_vring()
   to reset the vring state in place, then reprogram the MMIO queue
   registers (QUEUE_SEL, QUEUE_NUM, descriptor/avail/used addresses,
   QUEUE_READY) so the device can use the same rings immediately after
   restore.  No memory is allocated or freed.

 - virtio_mmio_freeze_noirq() / virtio_mmio_restore_noirq(): thin
   wrappers that forward to the virtio core noirq helpers.  The
   restore_noirq path also writes GUEST_PAGE_SIZE for legacy (v1)
   devices, matching the existing restore callback.

 - Wire vm_reset_vqs into virtio_mmio_config_ops and register the
   noirq callbacks via SET_NOIRQ_SYSTEM_SLEEP_PM_OPS().

With this in place, a virtio-mmio driver can implement freeze_noirq /
restore_noirq to participate in the noirq PM phase, enabling use cases
such as virtio-clock or virtio-regulator that must be operational
before other devices are restored.

Signed-off-by: Sungho Bae <[email protected]>
---
 drivers/virtio/virtio_mmio.c | 79 ++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 595c2274fbb5..7bf193ba4173 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -336,6 +336,65 @@ static void vm_del_vqs(struct virtio_device *vdev)
        free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
 }
 
+static int vm_reset_vqs(struct virtio_device *vdev)
+{
+       struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+       struct virtqueue *vq;
+
+       virtio_device_for_each_vq(vdev, vq) {
+               unsigned int num = virtqueue_get_vring_size(vq);
+
+               virtqueue_reinit_vring(vq);
+
+               writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+               writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
+
+               if (vm_dev->version == 1) {
+                       u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT;
+
+                       /*
+                        * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have
+                        * something that doesn't fit in 32bit, fail the setup
+                        * rather than pretending to be successful.
+                        */
+                       if (q_pfn >> 32) {
+                               dev_err(&vdev->dev,
+                                       "platform bug: legacy virtio-mmio must 
not be used with RAM above 0x%llxGB\n",
+                                       0x1ULL << (32 + PAGE_SHIFT - 30));
+                               return -E2BIG;
+                       }
+
+                       writel(PAGE_SIZE,
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
+                       writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+               } else {
+                       u64 addr;
+
+                       addr = virtqueue_get_desc_addr(vq);
+                       writel((u32)addr,
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
+                       writel((u32)(addr >> 32),
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
+
+                       addr = virtqueue_get_avail_addr(vq);
+                       writel((u32)addr,
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW);
+                       writel((u32)(addr >> 32),
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH);
+
+                       addr = virtqueue_get_used_addr(vq);
+                       writel((u32)addr,
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW);
+                       writel((u32)(addr >> 32),
+                              vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH);
+
+                       writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY);
+               }
+       }
+
+       return 0;
+}
+
 static void vm_synchronize_cbs(struct virtio_device *vdev)
 {
        struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
@@ -528,6 +587,7 @@ static const struct virtio_config_ops 
virtio_mmio_config_ops = {
        .reset          = vm_reset,
        .find_vqs       = vm_find_vqs,
        .del_vqs        = vm_del_vqs,
+       .reset_vqs      = vm_reset_vqs,
        .get_features   = vm_get_features,
        .finalize_features = vm_finalize_features,
        .bus_name       = vm_bus_name,
@@ -553,8 +613,27 @@ static int virtio_mmio_restore(struct device *dev)
        return virtio_device_restore(&vm_dev->vdev);
 }
 
+static int virtio_mmio_freeze_noirq(struct device *dev)
+{
+       struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev);
+
+       return virtio_device_freeze_noirq(&vm_dev->vdev);
+}
+
+static int virtio_mmio_restore_noirq(struct device *dev)
+{
+       struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev);
+
+       if (vm_dev->version == 1)
+               writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+
+       return virtio_device_restore_noirq(&vm_dev->vdev);
+}
+
 static const struct dev_pm_ops virtio_mmio_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze, virtio_mmio_restore)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze_noirq,
+                                     virtio_mmio_restore_noirq)
 };
 #endif
 
-- 
2.34.1


Reply via email to