diff options
Diffstat (limited to 'drivers/virtio/virtio_mmio.c')
-rw-r--r-- | drivers/virtio/virtio_mmio.c | 131 |
1 files changed, 81 insertions, 50 deletions
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 00d115b22bd8..cad569890908 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Virtio memory mapped device driver | 2 | * Virtio memory mapped device driver |
3 | * | 3 | * |
4 | * Copyright 2011, ARM Ltd. | 4 | * Copyright 2011-2014, ARM Ltd. |
5 | * | 5 | * |
6 | * This module allows virtio devices to be used over a virtual, memory mapped | 6 | * This module allows virtio devices to be used over a virtual, memory mapped |
7 | * platform device. | 7 | * platform device. |
@@ -50,36 +50,6 @@ | |||
50 | * | 50 | * |
51 | * | 51 | * |
52 | * | 52 | * |
53 | * Registers layout (all 32-bit wide): | ||
54 | * | ||
55 | * offset d. name description | ||
56 | * ------ -- ---------------- ----------------- | ||
57 | * | ||
58 | * 0x000 R MagicValue Magic value "virt" | ||
59 | * 0x004 R Version Device version (current max. 1) | ||
60 | * 0x008 R DeviceID Virtio device ID | ||
61 | * 0x00c R VendorID Virtio vendor ID | ||
62 | * | ||
63 | * 0x010 R HostFeatures Features supported by the host | ||
64 | * 0x014 W HostFeaturesSel Set of host features to access via HostFeatures | ||
65 | * | ||
66 | * 0x020 W GuestFeatures Features activated by the guest | ||
67 | * 0x024 W GuestFeaturesSel Set of activated features to set via GuestFeatures | ||
68 | * 0x028 W GuestPageSize Size of guest's memory page in bytes | ||
69 | * | ||
70 | * 0x030 W QueueSel Queue selector | ||
71 | * 0x034 R QueueNumMax Maximum size of the currently selected queue | ||
72 | * 0x038 W QueueNum Queue size for the currently selected queue | ||
73 | * 0x03c W QueueAlign Used Ring alignment for the current queue | ||
74 | * 0x040 RW QueuePFN PFN for the currently selected queue | ||
75 | * | ||
76 | * 0x050 W QueueNotify Queue notifier | ||
77 | * 0x060 R InterruptStatus Interrupt status register | ||
78 | * 0x064 W InterruptACK Interrupt acknowledge register | ||
79 | * 0x070 RW Status Device status register | ||
80 | * | ||
81 | * 0x100+ RW Device-specific configuration space | ||
82 | * | ||
83 | * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 | 53 | * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 |
84 | * | 54 | * |
85 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | 55 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
@@ -145,11 +115,16 @@ struct virtio_mmio_vq_info { | |||
145 | static u64 vm_get_features(struct virtio_device *vdev) | 115 | static u64 vm_get_features(struct virtio_device *vdev) |
146 | { | 116 | { |
147 | struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); | 117 | struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); |
118 | u64 features; | ||
119 | |||
120 | writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); | ||
121 | features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); | ||
122 | features <<= 32; | ||
148 | 123 | ||
149 | /* TODO: Features > 32 bits */ | 124 | writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); |
150 | writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL); | 125 | features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); |
151 | 126 | ||
152 | return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES); | 127 | return features; |
153 | } | 128 | } |
154 | 129 | ||
155 | static int vm_finalize_features(struct virtio_device *vdev) | 130 | static int vm_finalize_features(struct virtio_device *vdev) |
@@ -159,11 +134,20 @@ static int vm_finalize_features(struct virtio_device *vdev) | |||
159 | /* Give virtio_ring a chance to accept features. */ | 134 | /* Give virtio_ring a chance to accept features. */ |
160 | vring_transport_features(vdev); | 135 | vring_transport_features(vdev); |
161 | 136 | ||
162 | /* Make sure we don't have any features > 32 bits! */ | 137 | /* Make sure there is are no mixed devices */ |
163 | BUG_ON((u32)vdev->features != vdev->features); | 138 | if (vm_dev->version == 2 && |
139 | !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { | ||
140 | dev_err(&vdev->dev, "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n"); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); | ||
145 | writel((u32)(vdev->features >> 32), | ||
146 | vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); | ||
164 | 147 | ||
165 | writel(0, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SEL); | 148 | writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); |
166 | writel(vdev->features, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES); | 149 | writel((u32)vdev->features, |
150 | vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); | ||
167 | 151 | ||
168 | return 0; | 152 | return 0; |
169 | } | 153 | } |
@@ -275,7 +259,12 @@ static void vm_del_vq(struct virtqueue *vq) | |||
275 | 259 | ||
276 | /* Select and deactivate the queue */ | 260 | /* Select and deactivate the queue */ |
277 | writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); | 261 | writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); |
278 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | 262 | if (vm_dev->version == 1) { |
263 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | ||
264 | } else { | ||
265 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); | ||
266 | WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); | ||
267 | } | ||
279 | 268 | ||
280 | size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN)); | 269 | size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN)); |
281 | free_pages_exact(info->queue, size); | 270 | free_pages_exact(info->queue, size); |
@@ -312,7 +301,8 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, | |||
312 | writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); | 301 | writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); |
313 | 302 | ||
314 | /* Queue shouldn't already be set up. */ | 303 | /* Queue shouldn't already be set up. */ |
315 | if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN)) { | 304 | if (readl(vm_dev->base + (vm_dev->version == 1 ? |
305 | VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) { | ||
316 | err = -ENOENT; | 306 | err = -ENOENT; |
317 | goto error_available; | 307 | goto error_available; |
318 | } | 308 | } |
@@ -356,13 +346,6 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, | |||
356 | info->num /= 2; | 346 | info->num /= 2; |
357 | } | 347 | } |
358 | 348 | ||
359 | /* Activate the queue */ | ||
360 | writel(info->num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); | ||
361 | writel(VIRTIO_MMIO_VRING_ALIGN, | ||
362 | vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); | ||
363 | writel(virt_to_phys(info->queue) >> PAGE_SHIFT, | ||
364 | vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | ||
365 | |||
366 | /* Create the vring */ | 349 | /* Create the vring */ |
367 | vq = vring_new_virtqueue(index, info->num, VIRTIO_MMIO_VRING_ALIGN, vdev, | 350 | vq = vring_new_virtqueue(index, info->num, VIRTIO_MMIO_VRING_ALIGN, vdev, |
368 | true, info->queue, vm_notify, callback, name); | 351 | true, info->queue, vm_notify, callback, name); |
@@ -371,6 +354,33 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, | |||
371 | goto error_new_virtqueue; | 354 | goto error_new_virtqueue; |
372 | } | 355 | } |
373 | 356 | ||
357 | /* Activate the queue */ | ||
358 | writel(info->num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); | ||
359 | if (vm_dev->version == 1) { | ||
360 | writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); | ||
361 | writel(virt_to_phys(info->queue) >> PAGE_SHIFT, | ||
362 | vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | ||
363 | } else { | ||
364 | u64 addr; | ||
365 | |||
366 | addr = virt_to_phys(info->queue); | ||
367 | writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); | ||
368 | writel((u32)(addr >> 32), | ||
369 | vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); | ||
370 | |||
371 | addr = virt_to_phys(virtqueue_get_avail(vq)); | ||
372 | writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); | ||
373 | writel((u32)(addr >> 32), | ||
374 | vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); | ||
375 | |||
376 | addr = virt_to_phys(virtqueue_get_used(vq)); | ||
377 | writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); | ||
378 | writel((u32)(addr >> 32), | ||
379 | vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); | ||
380 | |||
381 | writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); | ||
382 | } | ||
383 | |||
374 | vq->priv = info; | 384 | vq->priv = info; |
375 | info->vq = vq; | 385 | info->vq = vq; |
376 | 386 | ||
@@ -381,7 +391,12 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, | |||
381 | return vq; | 391 | return vq; |
382 | 392 | ||
383 | error_new_virtqueue: | 393 | error_new_virtqueue: |
384 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | 394 | if (vm_dev->version == 1) { |
395 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); | ||
396 | } else { | ||
397 | writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); | ||
398 | WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); | ||
399 | } | ||
385 | free_pages_exact(info->queue, size); | 400 | free_pages_exact(info->queue, size); |
386 | error_alloc_pages: | 401 | error_alloc_pages: |
387 | kfree(info); | 402 | kfree(info); |
@@ -476,16 +491,32 @@ static int virtio_mmio_probe(struct platform_device *pdev) | |||
476 | 491 | ||
477 | /* Check device version */ | 492 | /* Check device version */ |
478 | vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION); | 493 | vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION); |
479 | if (vm_dev->version != 1) { | 494 | if (vm_dev->version < 1 || vm_dev->version > 2) { |
480 | dev_err(&pdev->dev, "Version %ld not supported!\n", | 495 | dev_err(&pdev->dev, "Version %ld not supported!\n", |
481 | vm_dev->version); | 496 | vm_dev->version); |
482 | return -ENXIO; | 497 | return -ENXIO; |
483 | } | 498 | } |
484 | 499 | ||
485 | vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); | 500 | vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); |
501 | if (vm_dev->vdev.id.device == 0) { | ||
502 | /* | ||
503 | * virtio-mmio device with an ID 0 is a (dummy) placeholder | ||
504 | * with no function. End probing now with no error reported. | ||
505 | */ | ||
506 | return -ENODEV; | ||
507 | } | ||
486 | vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); | 508 | vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); |
487 | 509 | ||
488 | writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); | 510 | /* Reject legacy-only IDs for version 2 devices */ |
511 | if (vm_dev->version == 2 && | ||
512 | virtio_device_is_legacy_only(vm_dev->vdev.id)) { | ||
513 | dev_err(&pdev->dev, "Version 2 not supported for devices %u!\n", | ||
514 | vm_dev->vdev.id.device); | ||
515 | return -ENODEV; | ||
516 | } | ||
517 | |||
518 | if (vm_dev->version == 1) | ||
519 | writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); | ||
489 | 520 | ||
490 | platform_set_drvdata(pdev, vm_dev); | 521 | platform_set_drvdata(pdev, vm_dev); |
491 | 522 | ||