aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBin Meng2018-10-15 04:21:02 -0500
committerSimon Glass2018-11-14 11:16:27 -0600
commitfdc4aca89ecb928d893c8bca4d0de08ebd07686a (patch)
tree3853eb8e49af4ca8f18eac28ff2a60bd01ec1fdd /drivers
parentc011641ec4fcb61d1335f61b413117c1b7d83e5e (diff)
downloadu-boot-fdc4aca89ecb928d893c8bca4d0de08ebd07686a.tar.gz
u-boot-fdc4aca89ecb928d893c8bca4d0de08ebd07686a.tar.xz
u-boot-fdc4aca89ecb928d893c8bca4d0de08ebd07686a.zip
virtio: Add virtio over mmio transport driver
VirtIO can use various different buses and virtio devices are commonly implemented as PCI devices. But virtual environments without PCI support (a common situation in embedded devices models) might use simple memory mapped device (“virtio-mmio”) instead of the PCI device. This adds a transport driver that implements UCLASS_VIRTIO for virtio over mmio. Signed-off-by: Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/virtio/Kconfig7
-rw-r--r--drivers/virtio/Makefile1
-rw-r--r--drivers/virtio/virtio_mmio.c413
-rw-r--r--drivers/virtio/virtio_mmio.h129
4 files changed, 550 insertions, 0 deletions
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 82fc536831..4f9a11b6ef 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -22,4 +22,11 @@ config VIRTIO
22 This option is selected by any driver which implements the virtio 22 This option is selected by any driver which implements the virtio
23 transport, such as CONFIG_VIRTIO_MMIO or CONFIG_VIRTIO_PCI. 23 transport, such as CONFIG_VIRTIO_MMIO or CONFIG_VIRTIO_PCI.
24 24
25config VIRTIO_MMIO
26 bool "Platform bus driver for memory mapped virtio devices"
27 select VIRTIO
28 help
29 This driver provides support for memory mapped virtio
30 platform device driver.
31
25endmenu 32endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 17d264a771..2e487854a2 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -4,3 +4,4 @@
4# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> 4# Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5 5
6obj-y += virtio-uclass.o virtio_ring.o 6obj-y += virtio-uclass.o virtio_ring.o
7obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
new file mode 100644
index 0000000000..7b738703b8
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.c
@@ -0,0 +1,413 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
4 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5 *
6 * VirtIO memory-maped I/O transport driver
7 * Ported from Linux drivers/virtio/virtio_mmio.c
8 */
9
10#include <common.h>
11#include <dm.h>
12#include <virtio_types.h>
13#include <virtio.h>
14#include <virtio_ring.h>
15#include <linux/compat.h>
16#include <linux/io.h>
17#include "virtio_mmio.h"
18
19static int virtio_mmio_get_config(struct udevice *udev, unsigned int offset,
20 void *buf, unsigned int len)
21{
22 struct virtio_mmio_priv *priv = dev_get_priv(udev);
23 void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
24 u8 b;
25 __le16 w;
26 __le32 l;
27
28 if (priv->version == 1) {
29 u8 *ptr = buf;
30 int i;
31
32 for (i = 0; i < len; i++)
33 ptr[i] = readb(base + offset + i);
34
35 return 0;
36 }
37
38 switch (len) {
39 case 1:
40 b = readb(base + offset);
41 memcpy(buf, &b, sizeof(b));
42 break;
43 case 2:
44 w = cpu_to_le16(readw(base + offset));
45 memcpy(buf, &w, sizeof(w));
46 break;
47 case 4:
48 l = cpu_to_le32(readl(base + offset));
49 memcpy(buf, &l, sizeof(l));
50 break;
51 case 8:
52 l = cpu_to_le32(readl(base + offset));
53 memcpy(buf, &l, sizeof(l));
54 l = cpu_to_le32(readl(base + offset + sizeof(l)));
55 memcpy(buf + sizeof(l), &l, sizeof(l));
56 break;
57 default:
58 WARN_ON(true);
59 }
60
61 return 0;
62}
63
64static int virtio_mmio_set_config(struct udevice *udev, unsigned int offset,
65 const void *buf, unsigned int len)
66{
67 struct virtio_mmio_priv *priv = dev_get_priv(udev);
68 void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
69 u8 b;
70 __le16 w;
71 __le32 l;
72
73 if (priv->version == 1) {
74 const u8 *ptr = buf;
75 int i;
76
77 for (i = 0; i < len; i++)
78 writeb(ptr[i], base + offset + i);
79
80 return 0;
81 }
82
83 switch (len) {
84 case 1:
85 memcpy(&b, buf, sizeof(b));
86 writeb(b, base + offset);
87 break;
88 case 2:
89 memcpy(&w, buf, sizeof(w));
90 writew(le16_to_cpu(w), base + offset);
91 break;
92 case 4:
93 memcpy(&l, buf, sizeof(l));
94 writel(le32_to_cpu(l), base + offset);
95 break;
96 case 8:
97 memcpy(&l, buf, sizeof(l));
98 writel(le32_to_cpu(l), base + offset);
99 memcpy(&l, buf + sizeof(l), sizeof(l));
100 writel(le32_to_cpu(l), base + offset + sizeof(l));
101 break;
102 default:
103 WARN_ON(true);
104 }
105
106 return 0;
107}
108
109static int virtio_mmio_generation(struct udevice *udev, u32 *counter)
110{
111 struct virtio_mmio_priv *priv = dev_get_priv(udev);
112
113 if (priv->version == 1)
114 *counter = 0;
115 else
116 *counter = readl(priv->base + VIRTIO_MMIO_CONFIG_GENERATION);
117
118 return 0;
119}
120
121static int virtio_mmio_get_status(struct udevice *udev, u8 *status)
122{
123 struct virtio_mmio_priv *priv = dev_get_priv(udev);
124
125 *status = readl(priv->base + VIRTIO_MMIO_STATUS) & 0xff;
126
127 return 0;
128}
129
130static int virtio_mmio_set_status(struct udevice *udev, u8 status)
131{
132 struct virtio_mmio_priv *priv = dev_get_priv(udev);
133
134 /* We should never be setting status to 0 */
135 WARN_ON(status == 0);
136
137 writel(status, priv->base + VIRTIO_MMIO_STATUS);
138
139 return 0;
140}
141
142static int virtio_mmio_reset(struct udevice *udev)
143{
144 struct virtio_mmio_priv *priv = dev_get_priv(udev);
145
146 /* 0 status means a reset */
147 writel(0, priv->base + VIRTIO_MMIO_STATUS);
148
149 return 0;
150}
151
152static int virtio_mmio_get_features(struct udevice *udev, u64 *features)
153{
154 struct virtio_mmio_priv *priv = dev_get_priv(udev);
155
156 writel(1, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
157 *features = readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
158 *features <<= 32;
159
160 writel(0, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
161 *features |= readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
162
163 return 0;
164}
165
166static int virtio_mmio_set_features(struct udevice *udev)
167{
168 struct virtio_mmio_priv *priv = dev_get_priv(udev);
169 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
170
171 /* Make sure there is are no mixed devices */
172 if (priv->version == 2 && uc_priv->legacy) {
173 debug("New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n");
174 return -EINVAL;
175 }
176
177 writel(1, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
178 writel((u32)(uc_priv->features >> 32),
179 priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
180
181 writel(0, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
182 writel((u32)uc_priv->features,
183 priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
184
185 return 0;
186}
187
188static struct virtqueue *virtio_mmio_setup_vq(struct udevice *udev,
189 unsigned int index)
190{
191 struct virtio_mmio_priv *priv = dev_get_priv(udev);
192 struct virtqueue *vq;
193 unsigned int num;
194 int err;
195
196 /* Select the queue we're interested in */
197 writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
198
199 /* Queue shouldn't already be set up */
200 if (readl(priv->base + (priv->version == 1 ?
201 VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) {
202 err = -ENOENT;
203 goto error_available;
204 }
205
206 num = readl(priv->base + VIRTIO_MMIO_QUEUE_NUM_MAX);
207 if (num == 0) {
208 err = -ENOENT;
209 goto error_new_virtqueue;
210 }
211
212 /* Create the vring */
213 vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, udev);
214 if (!vq) {
215 err = -ENOMEM;
216 goto error_new_virtqueue;
217 }
218
219 /* Activate the queue */
220 writel(virtqueue_get_vring_size(vq),
221 priv->base + VIRTIO_MMIO_QUEUE_NUM);
222 if (priv->version == 1) {
223 u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT;
224
225 /*
226 * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something
227 * that doesn't fit in 32bit, fail the setup rather than
228 * pretending to be successful.
229 */
230 if (q_pfn >> 32) {
231 debug("platform bug: legacy virtio-mmio must not be used with RAM above 0x%llxGB\n",
232 0x1ULL << (32 + PAGE_SHIFT - 30));
233 err = -E2BIG;
234 goto error_bad_pfn;
235 }
236
237 writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_QUEUE_ALIGN);
238 writel(q_pfn, priv->base + VIRTIO_MMIO_QUEUE_PFN);
239 } else {
240 u64 addr;
241
242 addr = virtqueue_get_desc_addr(vq);
243 writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
244 writel((u32)(addr >> 32),
245 priv->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
246
247 addr = virtqueue_get_avail_addr(vq);
248 writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW);
249 writel((u32)(addr >> 32),
250 priv->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH);
251
252 addr = virtqueue_get_used_addr(vq);
253 writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_USED_LOW);
254 writel((u32)(addr >> 32),
255 priv->base + VIRTIO_MMIO_QUEUE_USED_HIGH);
256
257 writel(1, priv->base + VIRTIO_MMIO_QUEUE_READY);
258 }
259
260 return vq;
261
262error_bad_pfn:
263 vring_del_virtqueue(vq);
264
265error_new_virtqueue:
266 if (priv->version == 1) {
267 writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
268 } else {
269 writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
270 WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
271 }
272
273error_available:
274 return ERR_PTR(err);
275}
276
277static void virtio_mmio_del_vq(struct virtqueue *vq)
278{
279 struct virtio_mmio_priv *priv = dev_get_priv(vq->vdev);
280 unsigned int index = vq->index;
281
282 /* Select and deactivate the queue */
283 writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
284 if (priv->version == 1) {
285 writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
286 } else {
287 writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
288 WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
289 }
290
291 vring_del_virtqueue(vq);
292}
293
294static int virtio_mmio_del_vqs(struct udevice *udev)
295{
296 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
297 struct virtqueue *vq, *n;
298
299 list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
300 virtio_mmio_del_vq(vq);
301
302 return 0;
303}
304
305static int virtio_mmio_find_vqs(struct udevice *udev, unsigned int nvqs,
306 struct virtqueue *vqs[])
307{
308 int i;
309
310 for (i = 0; i < nvqs; ++i) {
311 vqs[i] = virtio_mmio_setup_vq(udev, i);
312 if (IS_ERR(vqs[i])) {
313 virtio_mmio_del_vqs(udev);
314 return PTR_ERR(vqs[i]);
315 }
316 }
317
318 return 0;
319}
320
321static int virtio_mmio_notify(struct udevice *udev, struct virtqueue *vq)
322{
323 struct virtio_mmio_priv *priv = dev_get_priv(udev);
324
325 /*
326 * We write the queue's selector into the notification register
327 * to signal the other end
328 */
329 writel(vq->index, priv->base + VIRTIO_MMIO_QUEUE_NOTIFY);
330
331 return 0;
332}
333
334static int virtio_mmio_ofdata_to_platdata(struct udevice *udev)
335{
336 struct virtio_mmio_priv *priv = dev_get_priv(udev);
337
338 priv->base = (void __iomem *)(ulong)dev_read_addr(udev);
339 if (priv->base == (void __iomem *)FDT_ADDR_T_NONE)
340 return -EINVAL;
341
342 return 0;
343}
344
345static int virtio_mmio_probe(struct udevice *udev)
346{
347 struct virtio_mmio_priv *priv = dev_get_priv(udev);
348 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
349 u32 magic;
350
351 /* Check magic value */
352 magic = readl(priv->base + VIRTIO_MMIO_MAGIC_VALUE);
353 if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
354 debug("(%s): wrong magic value 0x%08x!\n", udev->name, magic);
355 return 0;
356 }
357
358 /* Check device version */
359 priv->version = readl(priv->base + VIRTIO_MMIO_VERSION);
360 if (priv->version < 1 || priv->version > 2) {
361 debug("(%s): version %d not supported!\n",
362 udev->name, priv->version);
363 return 0;
364 }
365
366 /* Check devicd ID */
367 uc_priv->device = readl(priv->base + VIRTIO_MMIO_DEVICE_ID);
368 if (uc_priv->device == 0) {
369 /*
370 * virtio-mmio device with an ID 0 is a (dummy) placeholder
371 * with no function. End probing now with no error reported.
372 */
373 return 0;
374 }
375 uc_priv->vendor = readl(priv->base + VIRTIO_MMIO_VENDOR_ID);
376
377 if (priv->version == 1)
378 writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
379
380 debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name,
381 uc_priv->device, uc_priv->vendor, priv->version);
382
383 return 0;
384}
385
386static const struct dm_virtio_ops virtio_mmio_ops = {
387 .get_config = virtio_mmio_get_config,
388 .set_config = virtio_mmio_set_config,
389 .generation = virtio_mmio_generation,
390 .get_status = virtio_mmio_get_status,
391 .set_status = virtio_mmio_set_status,
392 .reset = virtio_mmio_reset,
393 .get_features = virtio_mmio_get_features,
394 .set_features = virtio_mmio_set_features,
395 .find_vqs = virtio_mmio_find_vqs,
396 .del_vqs = virtio_mmio_del_vqs,
397 .notify = virtio_mmio_notify,
398};
399
400static const struct udevice_id virtio_mmio_ids[] = {
401 { .compatible = "virtio,mmio" },
402 { }
403};
404
405U_BOOT_DRIVER(virtio_mmio) = {
406 .name = "virtio-mmio",
407 .id = UCLASS_VIRTIO,
408 .of_match = virtio_mmio_ids,
409 .ops = &virtio_mmio_ops,
410 .probe = virtio_mmio_probe,
411 .ofdata_to_platdata = virtio_mmio_ofdata_to_platdata,
412 .priv_auto_alloc_size = sizeof(struct virtio_mmio_priv),
413};
diff --git a/drivers/virtio/virtio_mmio.h b/drivers/virtio/virtio_mmio.h
new file mode 100644
index 0000000000..b3408828a5
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.h
@@ -0,0 +1,129 @@
1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
4 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5 *
6 * From Linux kernel include/uapi/linux/virtio_mmio.h
7 */
8
9#ifndef _LINUX_VIRTIO_MMIO_H
10#define _LINUX_VIRTIO_MMIO_H
11
12/* Control registers */
13
14/* Magic value ("virt" string) - Read Only */
15#define VIRTIO_MMIO_MAGIC_VALUE 0x000
16
17/* Virtio device version - Read Only */
18#define VIRTIO_MMIO_VERSION 0x004
19
20/* Virtio device ID - Read Only */
21#define VIRTIO_MMIO_DEVICE_ID 0x008
22
23/* Virtio vendor ID - Read Only */
24#define VIRTIO_MMIO_VENDOR_ID 0x00c
25
26/*
27 * Bitmask of the features supported by the device (host)
28 * (32 bits per set) - Read Only
29 */
30#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
31
32/* Device (host) features set selector - Write Only */
33#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014
34
35/*
36 * Bitmask of features activated by the driver (guest)
37 * (32 bits per set) - Write Only
38 */
39#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
40
41/* Activated features set selector - Write Only */
42#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024
43
44#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */
45
46/* Guest's memory page size in bytes - Write Only */
47#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028
48
49#endif
50
51/* Queue selector - Write Only */
52#define VIRTIO_MMIO_QUEUE_SEL 0x030
53
54/* Maximum size of the currently selected queue - Read Only */
55#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034
56
57/* Queue size for the currently selected queue - Write Only */
58#define VIRTIO_MMIO_QUEUE_NUM 0x038
59
60#ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */
61
62/* Used Ring alignment for the currently selected queue - Write Only */
63#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c
64
65/* Guest's PFN for the currently selected queue - Read Write */
66#define VIRTIO_MMIO_QUEUE_PFN 0x040
67
68#endif
69
70/* Ready bit for the currently selected queue - Read Write */
71#define VIRTIO_MMIO_QUEUE_READY 0x044
72
73/* Queue notifier - Write Only */
74#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
75
76/* Interrupt status - Read Only */
77#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
78
79/* Interrupt acknowledge - Write Only */
80#define VIRTIO_MMIO_INTERRUPT_ACK 0x064
81
82/* Device status register - Read Write */
83#define VIRTIO_MMIO_STATUS 0x070
84
85/* Selected queue's Descriptor Table address, 64 bits in two halves */
86#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080
87#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
88
89/* Selected queue's Available Ring address, 64 bits in two halves */
90#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090
91#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094
92
93/* Selected queue's Used Ring address, 64 bits in two halves */
94#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0
95#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4
96
97/* Configuration atomicity value */
98#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc
99
100/*
101 * The config space is defined by each driver as
102 * the per-driver configuration space - Read Write
103 */
104#define VIRTIO_MMIO_CONFIG 0x100
105
106/* Interrupt flags (re: interrupt status & acknowledge registers) */
107
108#define VIRTIO_MMIO_INT_VRING BIT(0)
109#define VIRTIO_MMIO_INT_CONFIG BIT(1)
110
111/*
112 * The alignment to use between consumer and producer parts of vring.
113 * Currently hardcoded to the page size.
114 */
115#define PAGE_SHIFT 12
116#define VIRTIO_MMIO_VRING_ALIGN PAGE_SIZE
117
118/**
119 * virtio mmio transport driver private data
120 *
121 * @base: mmio transport device register base
122 * @version: mmio transport device version
123 */
124struct virtio_mmio_priv {
125 void __iomem *base;
126 u32 version;
127};
128
129#endif /* _LINUX_VIRTIO_MMIO_H */