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

