Blame view

drivers/virtio/virtio_mmio.c 20 KB
f33f5fe25   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
edfd52e63   Pawel Moll   virtio: Add platf...
2
3
4
  /*
   * Virtio memory mapped device driver
   *
1862ee22c   Pawel Moll   virtio-mmio: Upda...
5
   * Copyright 2011-2014, ARM Ltd.
edfd52e63   Pawel Moll   virtio: Add platf...
6
7
8
9
   *
   * This module allows virtio devices to be used over a virtual, memory mapped
   * platform device.
   *
81a054ce0   Pawel Moll   virtio-mmio: Devi...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
   * The guest device(s) may be instantiated in one of three equivalent ways:
   *
   * 1. Static platform device in board's code, eg.:
   *
   *	static struct platform_device v2m_virtio_device = {
   *		.name = "virtio-mmio",
   *		.id = -1,
   *		.num_resources = 2,
   *		.resource = (struct resource []) {
   *			{
   *				.start = 0x1001e000,
   *				.end = 0x1001e0ff,
   *				.flags = IORESOURCE_MEM,
   *			}, {
   *				.start = 42 + 32,
   *				.end = 42 + 32,
   *				.flags = IORESOURCE_IRQ,
   *			},
   *		}
   *	};
   *
   * 2. Device Tree node, eg.:
   *
   *		virtio_block@1e000 {
   *			compatible = "virtio,mmio";
   *			reg = <0x1e000 0x100>;
   *			interrupts = <42>;
   *		}
   *
   * 3. Kernel module (or command line) parameter. Can be used more than once -
   *    one device will be created for each one. Syntax:
   *
   *		[virtio_mmio.]device=<size>@<baseaddr>:<irq>[:<id>]
   *    where:
   *		<size>     := size (can use standard suffixes like K, M or G)
   *		<baseaddr> := physical base address
   *		<irq>      := interrupt number (as passed to request_irq())
   *		<id>       := (optional) platform device id
   *    eg.:
   *		virtio_mmio.device=0x100@0x100b0000:48 \
   *				virtio_mmio.device=1K@0x1001e000:74
   *
edfd52e63   Pawel Moll   virtio: Add platf...
52
   * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
edfd52e63   Pawel Moll   virtio: Add platf...
53
   */
81a054ce0   Pawel Moll   virtio-mmio: Devi...
54
  #define pr_fmt(fmt) "virtio-mmio: " fmt
38c4ab8e4   Graeme Gregory   virtio_mmio: add ...
55
  #include <linux/acpi.h>
f7f6634d2   Robin Murphy   virtio_mmio: Set ...
56
  #include <linux/dma-mapping.h>
edfd52e63   Pawel Moll   virtio: Add platf...
57
58
59
60
61
62
63
64
65
66
  #include <linux/highmem.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/list.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/virtio.h>
  #include <linux/virtio_config.h>
51be7a9a2   Michael S. Tsirkin   virtio_mmio: expo...
67
  #include <uapi/linux/virtio_mmio.h>
edfd52e63   Pawel Moll   virtio: Add platf...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  #include <linux/virtio_ring.h>
  
  
  
  /* The alignment to use between consumer and producer parts of vring.
   * Currently hardcoded to the page size. */
  #define VIRTIO_MMIO_VRING_ALIGN		PAGE_SIZE
  
  
  
  #define to_virtio_mmio_device(_plat_dev) \
  	container_of(_plat_dev, struct virtio_mmio_device, vdev)
  
  struct virtio_mmio_device {
  	struct virtio_device vdev;
  	struct platform_device *pdev;
  
  	void __iomem *base;
  	unsigned long version;
  
  	/* a list of queues so we can dispatch IRQs */
  	spinlock_t lock;
  	struct list_head virtqueues;
  };
  
  struct virtio_mmio_vq_info {
  	/* the actual virtqueue */
  	struct virtqueue *vq;
edfd52e63   Pawel Moll   virtio: Add platf...
96
97
98
99
100
101
102
  	/* the list node for the virtqueues list */
  	struct list_head node;
  };
  
  
  
  /* Configuration interface */
d02547736   Michael S. Tsirkin   virtio: add suppo...
103
  static u64 vm_get_features(struct virtio_device *vdev)
edfd52e63   Pawel Moll   virtio: Add platf...
104
105
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
106
107
108
109
110
  	u64 features;
  
  	writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
  	features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES);
  	features <<= 32;
edfd52e63   Pawel Moll   virtio: Add platf...
111

1862ee22c   Pawel Moll   virtio-mmio: Upda...
112
113
  	writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
  	features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES);
edfd52e63   Pawel Moll   virtio: Add platf...
114

1862ee22c   Pawel Moll   virtio-mmio: Upda...
115
  	return features;
edfd52e63   Pawel Moll   virtio: Add platf...
116
  }
5c609a5ef   Michael S. Tsirkin   virtio: allow fin...
117
  static int vm_finalize_features(struct virtio_device *vdev)
edfd52e63   Pawel Moll   virtio: Add platf...
118
119
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
edfd52e63   Pawel Moll   virtio: Add platf...
120
121
122
  
  	/* Give virtio_ring a chance to accept features. */
  	vring_transport_features(vdev);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
123
124
125
126
127
128
129
130
131
132
133
  	/* Make sure there is are no mixed devices */
  	if (vm_dev->version == 2 &&
  			!__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) {
  		dev_err(&vdev->dev, "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!
  ");
  		return -EINVAL;
  	}
  
  	writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
  	writel((u32)(vdev->features >> 32),
  			vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES);
93d389f82   Michael S. Tsirkin   virtio: assert 32...
134

1862ee22c   Pawel Moll   virtio-mmio: Upda...
135
136
137
  	writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
  	writel((u32)vdev->features,
  			vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES);
5c609a5ef   Michael S. Tsirkin   virtio: allow fin...
138
139
  
  	return 0;
edfd52e63   Pawel Moll   virtio: Add platf...
140
141
142
143
144
145
  }
  
  static void vm_get(struct virtio_device *vdev, unsigned offset,
  		   void *buf, unsigned len)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
704a0b5f2   Michael S. Tsirkin   virtio_mmio: fix ...
146
147
148
149
150
151
152
153
154
155
156
157
158
  	void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
  	u8 b;
  	__le16 w;
  	__le32 l;
  
  	if (vm_dev->version == 1) {
  		u8 *ptr = buf;
  		int i;
  
  		for (i = 0; i < len; i++)
  			ptr[i] = readb(base + offset + i);
  		return;
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
159

704a0b5f2   Michael S. Tsirkin   virtio_mmio: fix ...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  	switch (len) {
  	case 1:
  		b = readb(base + offset);
  		memcpy(buf, &b, sizeof b);
  		break;
  	case 2:
  		w = cpu_to_le16(readw(base + offset));
  		memcpy(buf, &w, sizeof w);
  		break;
  	case 4:
  		l = cpu_to_le32(readl(base + offset));
  		memcpy(buf, &l, sizeof l);
  		break;
  	case 8:
  		l = cpu_to_le32(readl(base + offset));
  		memcpy(buf, &l, sizeof l);
  		l = cpu_to_le32(ioread32(base + offset + sizeof l));
  		memcpy(buf + sizeof l, &l, sizeof l);
  		break;
  	default:
  		BUG();
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
182
183
184
185
186
187
  }
  
  static void vm_set(struct virtio_device *vdev, unsigned offset,
  		   const void *buf, unsigned len)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
704a0b5f2   Michael S. Tsirkin   virtio_mmio: fix ...
188
189
190
191
192
193
194
195
196
197
198
  	void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
  	u8 b;
  	__le16 w;
  	__le32 l;
  
  	if (vm_dev->version == 1) {
  		const u8 *ptr = buf;
  		int i;
  
  		for (i = 0; i < len; i++)
  			writeb(ptr[i], base + offset + i);
edfd52e63   Pawel Moll   virtio: Add platf...
199

704a0b5f2   Michael S. Tsirkin   virtio_mmio: fix ...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  		return;
  	}
  
  	switch (len) {
  	case 1:
  		memcpy(&b, buf, sizeof b);
  		writeb(b, base + offset);
  		break;
  	case 2:
  		memcpy(&w, buf, sizeof w);
  		writew(le16_to_cpu(w), base + offset);
  		break;
  	case 4:
  		memcpy(&l, buf, sizeof l);
  		writel(le32_to_cpu(l), base + offset);
  		break;
  	case 8:
  		memcpy(&l, buf, sizeof l);
  		writel(le32_to_cpu(l), base + offset);
  		memcpy(&l, buf + sizeof l, sizeof l);
  		writel(le32_to_cpu(l), base + offset + sizeof l);
  		break;
  	default:
  		BUG();
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
225
  }
87e7bf145   Michael S. Tsirkin   virtio_mmio: gene...
226
227
228
229
230
231
232
233
234
  static u32 vm_generation(struct virtio_device *vdev)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  
  	if (vm_dev->version == 1)
  		return 0;
  	else
  		return readl(vm_dev->base + VIRTIO_MMIO_CONFIG_GENERATION);
  }
edfd52e63   Pawel Moll   virtio: Add platf...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  static u8 vm_get_status(struct virtio_device *vdev)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  
  	return readl(vm_dev->base + VIRTIO_MMIO_STATUS) & 0xff;
  }
  
  static void vm_set_status(struct virtio_device *vdev, u8 status)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  
  	/* We should never be setting status to 0. */
  	BUG_ON(status == 0);
  
  	writel(status, vm_dev->base + VIRTIO_MMIO_STATUS);
  }
  
  static void vm_reset(struct virtio_device *vdev)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  
  	/* 0 status means a reset. */
  	writel(0, vm_dev->base + VIRTIO_MMIO_STATUS);
  }
  
  
  
  /* Transport interface */
  
  /* the notify function used when creating a virt queue */
46f9c2b92   Heinz Graalfs   virtio_ring: chan...
265
  static bool vm_notify(struct virtqueue *vq)
edfd52e63   Pawel Moll   virtio: Add platf...
266
267
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
edfd52e63   Pawel Moll   virtio: Add platf...
268
269
270
  
  	/* We write the queue's selector into the notification register to
  	 * signal the other end */
06ca287db   Rusty Russell   virtio: move queu...
271
  	writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
46f9c2b92   Heinz Graalfs   virtio_ring: chan...
272
  	return true;
edfd52e63   Pawel Moll   virtio: Add platf...
273
274
275
276
277
278
279
  }
  
  /* Notify all virtqueues on an interrupt. */
  static irqreturn_t vm_interrupt(int irq, void *opaque)
  {
  	struct virtio_mmio_device *vm_dev = opaque;
  	struct virtio_mmio_vq_info *info;
edfd52e63   Pawel Moll   virtio: Add platf...
280
281
282
283
284
285
286
  	unsigned long status;
  	unsigned long flags;
  	irqreturn_t ret = IRQ_NONE;
  
  	/* Read and acknowledge interrupts */
  	status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS);
  	writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK);
016c98c6f   Michael S. Tsirkin   virtio: unify con...
287
288
  	if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) {
  		virtio_config_changed(&vm_dev->vdev);
edfd52e63   Pawel Moll   virtio: Add platf...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  		ret = IRQ_HANDLED;
  	}
  
  	if (likely(status & VIRTIO_MMIO_INT_VRING)) {
  		spin_lock_irqsave(&vm_dev->lock, flags);
  		list_for_each_entry(info, &vm_dev->virtqueues, node)
  			ret |= vring_interrupt(irq, info->vq);
  		spin_unlock_irqrestore(&vm_dev->lock, flags);
  	}
  
  	return ret;
  }
  
  
  
  static void vm_del_vq(struct virtqueue *vq)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
  	struct virtio_mmio_vq_info *info = vq->priv;
b42111382   Andy Lutomirski   virtio_mmio: Use ...
308
  	unsigned long flags;
06ca287db   Rusty Russell   virtio: move queu...
309
  	unsigned int index = vq->index;
edfd52e63   Pawel Moll   virtio: Add platf...
310
311
312
313
  
  	spin_lock_irqsave(&vm_dev->lock, flags);
  	list_del(&info->node);
  	spin_unlock_irqrestore(&vm_dev->lock, flags);
edfd52e63   Pawel Moll   virtio: Add platf...
314
  	/* Select and deactivate the queue */
17bb6d408   Jason Wang   virtio-ring: move...
315
  	writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
316
317
318
319
320
321
  	if (vm_dev->version == 1) {
  		writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
  	} else {
  		writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY);
  		WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY));
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
322

b42111382   Andy Lutomirski   virtio_mmio: Use ...
323
  	vring_del_virtqueue(vq);
edfd52e63   Pawel Moll   virtio: Add platf...
324
325
326
327
328
329
330
331
332
333
334
335
336
  	kfree(info);
  }
  
  static void vm_del_vqs(struct virtio_device *vdev)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  	struct virtqueue *vq, *n;
  
  	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
  		vm_del_vq(vq);
  
  	free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
  }
edfd52e63   Pawel Moll   virtio: Add platf...
337
338
  static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
  				  void (*callback)(struct virtqueue *vq),
f94682dde   Michael S. Tsirkin   virtio: add conte...
339
  				  const char *name, bool ctx)
edfd52e63   Pawel Moll   virtio: Add platf...
340
341
342
343
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  	struct virtio_mmio_vq_info *info;
  	struct virtqueue *vq;
b42111382   Andy Lutomirski   virtio_mmio: Use ...
344
345
  	unsigned long flags;
  	unsigned int num;
edfd52e63   Pawel Moll   virtio: Add platf...
346
  	int err;
6457f126c   Michael S. Tsirkin   virtio: support r...
347
348
  	if (!name)
  		return NULL;
edfd52e63   Pawel Moll   virtio: Add platf...
349
350
351
352
  	/* Select the queue we're interested in */
  	writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
  
  	/* Queue shouldn't already be set up. */
1862ee22c   Pawel Moll   virtio-mmio: Upda...
353
354
  	if (readl(vm_dev->base + (vm_dev->version == 1 ?
  			VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) {
edfd52e63   Pawel Moll   virtio: Add platf...
355
356
357
358
359
360
361
362
363
364
  		err = -ENOENT;
  		goto error_available;
  	}
  
  	/* Allocate and fill out our active queue description */
  	info = kmalloc(sizeof(*info), GFP_KERNEL);
  	if (!info) {
  		err = -ENOMEM;
  		goto error_kmalloc;
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
365

b42111382   Andy Lutomirski   virtio_mmio: Use ...
366
367
  	num = readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX);
  	if (num == 0) {
d78b519f6   Brian Foley   virtio_mmio: Don'...
368
  		err = -ENOENT;
b42111382   Andy Lutomirski   virtio_mmio: Use ...
369
  		goto error_new_virtqueue;
edfd52e63   Pawel Moll   virtio: Add platf...
370
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
371
  	/* Create the vring */
b42111382   Andy Lutomirski   virtio_mmio: Use ...
372
  	vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, vdev,
f94682dde   Michael S. Tsirkin   virtio: add conte...
373
  				 true, true, ctx, vm_notify, callback, name);
edfd52e63   Pawel Moll   virtio: Add platf...
374
375
376
377
  	if (!vq) {
  		err = -ENOMEM;
  		goto error_new_virtqueue;
  	}
1862ee22c   Pawel Moll   virtio-mmio: Upda...
378
  	/* Activate the queue */
b42111382   Andy Lutomirski   virtio_mmio: Use ...
379
  	writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
380
  	if (vm_dev->version == 1) {
3fc92a96c   Suzuki K Poulose   virtio: mmio-v1: ...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  		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
  ",
  				0x1ULL << (32 + PAGE_SHIFT - 30));
  			err = -E2BIG;
  			goto error_bad_pfn;
  		}
1862ee22c   Pawel Moll   virtio-mmio: Upda...
396
  		writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
3fc92a96c   Suzuki K Poulose   virtio: mmio-v1: ...
397
  		writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
398
399
  	} else {
  		u64 addr;
b42111382   Andy Lutomirski   virtio_mmio: Use ...
400
  		addr = virtqueue_get_desc_addr(vq);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
401
402
403
  		writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
  		writel((u32)(addr >> 32),
  				vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
b42111382   Andy Lutomirski   virtio_mmio: Use ...
404
  		addr = virtqueue_get_avail_addr(vq);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
405
406
407
  		writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW);
  		writel((u32)(addr >> 32),
  				vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH);
b42111382   Andy Lutomirski   virtio_mmio: Use ...
408
  		addr = virtqueue_get_used_addr(vq);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
409
410
411
412
413
414
  		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);
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
415
416
417
418
419
420
421
422
  	vq->priv = info;
  	info->vq = vq;
  
  	spin_lock_irqsave(&vm_dev->lock, flags);
  	list_add(&info->node, &vm_dev->virtqueues);
  	spin_unlock_irqrestore(&vm_dev->lock, flags);
  
  	return vq;
3fc92a96c   Suzuki K Poulose   virtio: mmio-v1: ...
423
424
  error_bad_pfn:
  	vring_del_virtqueue(vq);
edfd52e63   Pawel Moll   virtio: Add platf...
425
  error_new_virtqueue:
1862ee22c   Pawel Moll   virtio-mmio: Upda...
426
427
428
429
430
431
  	if (vm_dev->version == 1) {
  		writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
  	} else {
  		writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY);
  		WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY));
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
432
433
434
435
436
437
438
439
440
  	kfree(info);
  error_kmalloc:
  error_available:
  	return ERR_PTR(err);
  }
  
  static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
  		       struct virtqueue *vqs[],
  		       vq_callback_t *callbacks[],
fb5e31d97   Christoph Hellwig   virtio: allow dri...
441
  		       const char * const names[],
f94682dde   Michael S. Tsirkin   virtio: add conte...
442
  		       const bool *ctx,
fb5e31d97   Christoph Hellwig   virtio: allow dri...
443
  		       struct irq_affinity *desc)
edfd52e63   Pawel Moll   virtio: Add platf...
444
445
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
5e663f041   Ihor Matushchak   virtio-mmio: add ...
446
  	int irq = platform_get_irq(vm_dev->pdev, 0);
a229989d9   Wei Wang   virtio: don't all...
447
  	int i, err, queue_idx = 0;
edfd52e63   Pawel Moll   virtio: Add platf...
448

0c35c6741   Markus Elfring   virtio-mmio: Dele...
449
  	if (irq < 0)
5e663f041   Ihor Matushchak   virtio-mmio: add ...
450
  		return irq;
5e663f041   Ihor Matushchak   virtio-mmio: add ...
451

edfd52e63   Pawel Moll   virtio: Add platf...
452
453
454
455
456
457
  	err = request_irq(irq, vm_interrupt, IRQF_SHARED,
  			dev_name(&vdev->dev), vm_dev);
  	if (err)
  		return err;
  
  	for (i = 0; i < nvqs; ++i) {
a229989d9   Wei Wang   virtio: don't all...
458
459
460
461
462
463
  		if (!names[i]) {
  			vqs[i] = NULL;
  			continue;
  		}
  
  		vqs[i] = vm_setup_vq(vdev, queue_idx++, callbacks[i], names[i],
f94682dde   Michael S. Tsirkin   virtio: add conte...
464
  				     ctx ? ctx[i] : false);
edfd52e63   Pawel Moll   virtio: Add platf...
465
466
467
468
469
470
471
472
  		if (IS_ERR(vqs[i])) {
  			vm_del_vqs(vdev);
  			return PTR_ERR(vqs[i]);
  		}
  	}
  
  	return 0;
  }
66846048f   Rick Jones   enable virtio_net...
473
474
475
  static const char *vm_bus_name(struct virtio_device *vdev)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
edfd52e63   Pawel Moll   virtio: Add platf...
476

66846048f   Rick Jones   enable virtio_net...
477
478
  	return vm_dev->pdev->name;
  }
edfd52e63   Pawel Moll   virtio: Add platf...
479

38e895487   Sebastien Boeuf   virtio: Implement...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
  static bool vm_get_shm_region(struct virtio_device *vdev,
  			      struct virtio_shm_region *region, u8 id)
  {
  	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
  	u64 len, addr;
  
  	/* Select the region we're interested in */
  	writel(id, vm_dev->base + VIRTIO_MMIO_SHM_SEL);
  
  	/* Read the region size */
  	len = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_LOW);
  	len |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_HIGH) << 32;
  
  	region->len = len;
  
  	/* Check if region length is -1. If that's the case, the shared memory
  	 * region does not exist and there is no need to proceed further.
  	 */
  	if (len == ~(u64)0)
  		return false;
  
  	/* Read the region base address */
  	addr = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_LOW);
  	addr |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_HIGH) << 32;
  
  	region->addr = addr;
  
  	return true;
  }
935039323   Stephen Hemminger   virtio: make conf...
509
  static const struct virtio_config_ops virtio_mmio_config_ops = {
edfd52e63   Pawel Moll   virtio: Add platf...
510
511
  	.get		= vm_get,
  	.set		= vm_set,
87e7bf145   Michael S. Tsirkin   virtio_mmio: gene...
512
  	.generation	= vm_generation,
edfd52e63   Pawel Moll   virtio: Add platf...
513
514
515
516
517
518
519
  	.get_status	= vm_get_status,
  	.set_status	= vm_set_status,
  	.reset		= vm_reset,
  	.find_vqs	= vm_find_vqs,
  	.del_vqs	= vm_del_vqs,
  	.get_features	= vm_get_features,
  	.finalize_features = vm_finalize_features,
66846048f   Rick Jones   enable virtio_net...
520
  	.bus_name	= vm_bus_name,
38e895487   Sebastien Boeuf   virtio: Implement...
521
  	.get_shm_region = vm_get_shm_region,
edfd52e63   Pawel Moll   virtio: Add platf...
522
  };
7eb781b1b   weiping zhang   virtio_mmio: add ...
523
524
525
526
527
528
529
530
531
532
  static void virtio_mmio_release_dev(struct device *_d)
  {
  	struct virtio_device *vdev =
  			container_of(_d, struct virtio_device, dev);
  	struct virtio_mmio_device *vm_dev =
  			container_of(vdev, struct virtio_mmio_device, vdev);
  	struct platform_device *pdev = vm_dev->pdev;
  
  	devm_kfree(&pdev->dev, vm_dev);
  }
edfd52e63   Pawel Moll   virtio: Add platf...
533
534
  
  /* Platform device */
8590dbc79   Greg Kroah-Hartman   Drivers: virtio: ...
535
  static int virtio_mmio_probe(struct platform_device *pdev)
edfd52e63   Pawel Moll   virtio: Add platf...
536
537
  {
  	struct virtio_mmio_device *vm_dev;
edfd52e63   Pawel Moll   virtio: Add platf...
538
  	unsigned long magic;
f7f6634d2   Robin Murphy   virtio_mmio: Set ...
539
  	int rc;
edfd52e63   Pawel Moll   virtio: Add platf...
540

edfd52e63   Pawel Moll   virtio: Add platf...
541
  	vm_dev = devm_kzalloc(&pdev->dev, sizeof(*vm_dev), GFP_KERNEL);
c2e90800a   Mark Rutland   virtio_mmio: fix ...
542
543
  	if (!vm_dev)
  		return -ENOMEM;
edfd52e63   Pawel Moll   virtio: Add platf...
544
545
  
  	vm_dev->vdev.dev.parent = &pdev->dev;
7eb781b1b   weiping zhang   virtio_mmio: add ...
546
  	vm_dev->vdev.dev.release = virtio_mmio_release_dev;
edfd52e63   Pawel Moll   virtio: Add platf...
547
548
549
550
  	vm_dev->vdev.config = &virtio_mmio_config_ops;
  	vm_dev->pdev = pdev;
  	INIT_LIST_HEAD(&vm_dev->virtqueues);
  	spin_lock_init(&vm_dev->lock);
c64eb62cf   Yangtao Li   virtio-mmio: conv...
551
552
553
  	vm_dev->base = devm_platform_ioremap_resource(pdev, 0);
  	if (IS_ERR(vm_dev->base))
  		return PTR_ERR(vm_dev->base);
edfd52e63   Pawel Moll   virtio: Add platf...
554
555
556
  
  	/* Check magic value */
  	magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE);
4ae853707   Marc Zyngier   virtio: mmio: fix...
557
  	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
edfd52e63   Pawel Moll   virtio: Add platf...
558
559
  		dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!
  ", magic);
c2e90800a   Mark Rutland   virtio_mmio: fix ...
560
  		return -ENODEV;
edfd52e63   Pawel Moll   virtio: Add platf...
561
562
563
564
  	}
  
  	/* Check device version */
  	vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
565
  	if (vm_dev->version < 1 || vm_dev->version > 2) {
edfd52e63   Pawel Moll   virtio: Add platf...
566
567
568
  		dev_err(&pdev->dev, "Version %ld not supported!
  ",
  				vm_dev->version);
c2e90800a   Mark Rutland   virtio_mmio: fix ...
569
  		return -ENXIO;
edfd52e63   Pawel Moll   virtio: Add platf...
570
571
572
  	}
  
  	vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
1862ee22c   Pawel Moll   virtio-mmio: Upda...
573
574
575
576
577
  	if (vm_dev->vdev.id.device == 0) {
  		/*
  		 * virtio-mmio device with an ID 0 is a (dummy) placeholder
  		 * with no function. End probing now with no error reported.
  		 */
c2e90800a   Mark Rutland   virtio_mmio: fix ...
578
  		return -ENODEV;
1862ee22c   Pawel Moll   virtio-mmio: Upda...
579
  	}
edfd52e63   Pawel Moll   virtio: Add platf...
580
  	vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
f7f6634d2   Robin Murphy   virtio_mmio: Set ...
581
  	if (vm_dev->version == 1) {
1862ee22c   Pawel Moll   virtio-mmio: Upda...
582
  		writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
edfd52e63   Pawel Moll   virtio: Add platf...
583

f7f6634d2   Robin Murphy   virtio_mmio: Set ...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  		rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
  		/*
  		 * In the legacy case, ensure our coherently-allocated virtio
  		 * ring will be at an address expressable as a 32-bit PFN.
  		 */
  		if (!rc)
  			dma_set_coherent_mask(&pdev->dev,
  					      DMA_BIT_MASK(32 + PAGE_SHIFT));
  	} else {
  		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
  	}
  	if (rc)
  		rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
  	if (rc)
  		dev_warn(&pdev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.
  ");
edfd52e63   Pawel Moll   virtio: Add platf...
600
  	platform_set_drvdata(pdev, vm_dev);
7eb781b1b   weiping zhang   virtio_mmio: add ...
601
  	rc = register_virtio_device(&vm_dev->vdev);
c2e90800a   Mark Rutland   virtio_mmio: fix ...
602
  	if (rc)
7eb781b1b   weiping zhang   virtio_mmio: add ...
603
  		put_device(&vm_dev->vdev.dev);
c2e90800a   Mark Rutland   virtio_mmio: fix ...
604

7eb781b1b   weiping zhang   virtio_mmio: add ...
605
  	return rc;
edfd52e63   Pawel Moll   virtio: Add platf...
606
  }
8590dbc79   Greg Kroah-Hartman   Drivers: virtio: ...
607
  static int virtio_mmio_remove(struct platform_device *pdev)
edfd52e63   Pawel Moll   virtio: Add platf...
608
609
  {
  	struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
edfd52e63   Pawel Moll   virtio: Add platf...
610
611
612
613
  	unregister_virtio_device(&vm_dev->vdev);
  
  	return 0;
  }
81a054ce0   Pawel Moll   virtio-mmio: Devi...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
  /* Devices list parameter */
  
  #if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES)
  
  static struct device vm_cmdline_parent = {
  	.init_name = "virtio-mmio-cmdline",
  };
  
  static int vm_cmdline_parent_registered;
  static int vm_cmdline_id;
  
  static int vm_cmdline_set(const char *device,
  		const struct kernel_param *kp)
  {
  	int err;
  	struct resource resources[2] = {};
  	char *str;
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
631
632
  	long long int base, size;
  	unsigned int irq;
81a054ce0   Pawel Moll   virtio-mmio: Devi...
633
634
  	int processed, consumed = 0;
  	struct platform_device *pdev;
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
635
636
  	/* Consume "size" part of the command line parameter */
  	size = memparse(device, &str);
81a054ce0   Pawel Moll   virtio-mmio: Devi...
637

40f9938c4   Pawel Moll   virtio-mmio: Fix ...
638
  	/* Get "@<base>:<irq>[:<id>]" chunks */
81a054ce0   Pawel Moll   virtio-mmio: Devi...
639
  	processed = sscanf(str, "@%lli:%u%n:%d%n",
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
640
  			&base, &irq, &consumed,
81a054ce0   Pawel Moll   virtio-mmio: Devi...
641
  			&vm_cmdline_id, &consumed);
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
642
  	/*
62ca18a08   Bjorn Helgaas   virtio-mmio: Reje...
643
  	 * sscanf() must process at least 2 chunks; also there
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
644
645
646
  	 * must be no extra characters after the last chunk, so
  	 * str[consumed] must be '\0'
  	 */
62ca18a08   Bjorn Helgaas   virtio-mmio: Reje...
647
  	if (processed < 2 || str[consumed] || irq == 0)
81a054ce0   Pawel Moll   virtio-mmio: Devi...
648
  		return -EINVAL;
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
649
  	resources[0].flags = IORESOURCE_MEM;
81a054ce0   Pawel Moll   virtio-mmio: Devi...
650
  	resources[0].start = base;
40f9938c4   Pawel Moll   virtio-mmio: Fix ...
651
652
653
654
  	resources[0].end = base + size - 1;
  
  	resources[1].flags = IORESOURCE_IRQ;
  	resources[1].start = resources[1].end = irq;
81a054ce0   Pawel Moll   virtio-mmio: Devi...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  
  	if (!vm_cmdline_parent_registered) {
  		err = device_register(&vm_cmdline_parent);
  		if (err) {
  			pr_err("Failed to register parent device!
  ");
  			return err;
  		}
  		vm_cmdline_parent_registered = 1;
  	}
  
  	pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.
  ",
  		       vm_cmdline_id,
  		       (unsigned long long)resources[0].start,
  		       (unsigned long long)resources[0].end,
  		       (int)resources[1].start);
  
  	pdev = platform_device_register_resndata(&vm_cmdline_parent,
  			"virtio-mmio", vm_cmdline_id++,
  			resources, ARRAY_SIZE(resources), NULL, 0);
81a054ce0   Pawel Moll   virtio-mmio: Devi...
676

c2c9f9bc5   Vasyl Gomonovych   virtio-mmio: Use ...
677
  	return PTR_ERR_OR_ZERO(pdev);
81a054ce0   Pawel Moll   virtio-mmio: Devi...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  }
  
  static int vm_cmdline_get_device(struct device *dev, void *data)
  {
  	char *buffer = data;
  	unsigned int len = strlen(buffer);
  	struct platform_device *pdev = to_platform_device(dev);
  
  	snprintf(buffer + len, PAGE_SIZE - len, "0x%llx@0x%llx:%llu:%d
  ",
  			pdev->resource[0].end - pdev->resource[0].start + 1ULL,
  			(unsigned long long)pdev->resource[0].start,
  			(unsigned long long)pdev->resource[1].start,
  			pdev->id);
  	return 0;
  }
  
  static int vm_cmdline_get(char *buffer, const struct kernel_param *kp)
  {
  	buffer[0] = '\0';
  	device_for_each_child(&vm_cmdline_parent, buffer,
  			vm_cmdline_get_device);
  	return strlen(buffer) + 1;
  }
9c27847dd   Luis R. Rodriguez   kernel/params: co...
702
  static const struct kernel_param_ops vm_cmdline_param_ops = {
81a054ce0   Pawel Moll   virtio-mmio: Devi...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
  	.set = vm_cmdline_set,
  	.get = vm_cmdline_get,
  };
  
  device_param_cb(device, &vm_cmdline_param_ops, NULL, S_IRUSR);
  
  static int vm_unregister_cmdline_device(struct device *dev,
  		void *data)
  {
  	platform_device_unregister(to_platform_device(dev));
  
  	return 0;
  }
  
  static void vm_unregister_cmdline_devices(void)
  {
  	if (vm_cmdline_parent_registered) {
  		device_for_each_child(&vm_cmdline_parent, NULL,
  				vm_unregister_cmdline_device);
  		device_unregister(&vm_cmdline_parent);
  		vm_cmdline_parent_registered = 0;
  	}
  }
  
  #else
  
  static void vm_unregister_cmdline_devices(void)
  {
  }
  
  #endif
edfd52e63   Pawel Moll   virtio: Add platf...
734
  /* Platform driver */
31919140f   Arvind Yadav   virtio: virtio_mm...
735
  static const struct of_device_id virtio_mmio_match[] = {
edfd52e63   Pawel Moll   virtio: Add platf...
736
737
738
739
  	{ .compatible = "virtio,mmio", },
  	{},
  };
  MODULE_DEVICE_TABLE(of, virtio_mmio_match);
38c4ab8e4   Graeme Gregory   virtio_mmio: add ...
740
741
742
743
744
745
746
  #ifdef CONFIG_ACPI
  static const struct acpi_device_id virtio_mmio_acpi_match[] = {
  	{ "LNRO0005", },
  	{ }
  };
  MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
  #endif
edfd52e63   Pawel Moll   virtio: Add platf...
747
748
  static struct platform_driver virtio_mmio_driver = {
  	.probe		= virtio_mmio_probe,
8590dbc79   Greg Kroah-Hartman   Drivers: virtio: ...
749
  	.remove		= virtio_mmio_remove,
edfd52e63   Pawel Moll   virtio: Add platf...
750
751
  	.driver		= {
  		.name	= "virtio-mmio",
edfd52e63   Pawel Moll   virtio: Add platf...
752
  		.of_match_table	= virtio_mmio_match,
38c4ab8e4   Graeme Gregory   virtio_mmio: add ...
753
  		.acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match),
edfd52e63   Pawel Moll   virtio: Add platf...
754
755
756
757
758
759
760
761
762
763
764
  	},
  };
  
  static int __init virtio_mmio_init(void)
  {
  	return platform_driver_register(&virtio_mmio_driver);
  }
  
  static void __exit virtio_mmio_exit(void)
  {
  	platform_driver_unregister(&virtio_mmio_driver);
81a054ce0   Pawel Moll   virtio-mmio: Devi...
765
  	vm_unregister_cmdline_devices();
edfd52e63   Pawel Moll   virtio: Add platf...
766
767
768
769
770
771
772
773
  }
  
  module_init(virtio_mmio_init);
  module_exit(virtio_mmio_exit);
  
  MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
  MODULE_DESCRIPTION("Platform bus driver for memory mapped virtio devices");
  MODULE_LICENSE("GPL");