aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBin Meng2018-10-15 04:21:00 -0500
committerSimon Glass2018-11-14 11:16:27 -0600
commit8fb49b4c7a820461db7c11dce767f36fd6395cac (patch)
tree3289c27071a293689680da11b1d9614531d7cce9 /drivers
parentb1893a9e0def4052e56513bfcee0a0eb95841f7f (diff)
downloadu-boot-8fb49b4c7a820461db7c11dce767f36fd6395cac.tar.gz
u-boot-8fb49b4c7a820461db7c11dce767f36fd6395cac.tar.xz
u-boot-8fb49b4c7a820461db7c11dce767f36fd6395cac.zip
dm: Add a new uclass driver for VirtIO transport devices
This adds a new virtio uclass driver for “virtio” [1] family of devices that are are found in virtual environments like QEMU, yet by design they look like physical devices to the guest. The uclass driver provides child_pre_probe() and child_post_probe() methods to do some common operations for virtio device drivers like device and driver supported feature negotiation, etc. [1] http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf 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/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/virtio/Kconfig25
-rw-r--r--drivers/virtio/Makefile6
-rw-r--r--drivers/virtio/virtio-uclass.c369
5 files changed, 403 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 927a2b87f6..4ac823d962 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -112,6 +112,8 @@ source "drivers/usb/Kconfig"
112 112
113source "drivers/video/Kconfig" 113source "drivers/video/Kconfig"
114 114
115source "drivers/virtio/Kconfig"
116
115source "drivers/w1/Kconfig" 117source "drivers/w1/Kconfig"
116 118
117source "drivers/w1-eeprom/Kconfig" 119source "drivers/w1-eeprom/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index fb38b67541..4453c62ad3 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_$(SPL_TPL_)SERIAL_SUPPORT) += serial/
14obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/ 14obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/
15obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/ 15obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/
16obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ 16obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/
17obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/
17obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ 18obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/
18obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/ 19obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
19 20
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
new file mode 100644
index 0000000000..82fc536831
--- /dev/null
+++ b/drivers/virtio/Kconfig
@@ -0,0 +1,25 @@
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 is a virtualization standard for network and disk device drivers
7# where just the guest's device driver "knows" it is running in a virtual
8# environment, and cooperates with the hypervisor. This enables guests to
9# get high performance network and disk operations, and gives most of the
10# performance benefits of paravirtualization. In the U-Boot case, the guest
11# is U-Boot itself, while the virtual environment are normally QEMU targets
12# like ARM, RISC-V and x86.
13#
14# See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
15# the VirtIO specification v1.0.
16
17menu "VirtIO Drivers"
18
19config VIRTIO
20 bool
21 help
22 This option is selected by any driver which implements the virtio
23 transport, such as CONFIG_VIRTIO_MMIO or CONFIG_VIRTIO_PCI.
24
25endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
new file mode 100644
index 0000000000..23e7be7165
--- /dev/null
+++ b/drivers/virtio/Makefile
@@ -0,0 +1,6 @@
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
6obj-y += virtio-uclass.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
new file mode 100644
index 0000000000..34397d7dbb
--- /dev/null
+++ b/drivers/virtio/virtio-uclass.c
@@ -0,0 +1,369 @@
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 is a virtualization standard for network and disk device drivers
7 * where just the guest's device driver "knows" it is running in a virtual
8 * environment, and cooperates with the hypervisor. This enables guests to
9 * get high performance network and disk operations, and gives most of the
10 * performance benefits of paravirtualization. In the U-Boot case, the guest
11 * is U-Boot itself, while the virtual environment are normally QEMU targets
12 * like ARM, RISC-V and x86.
13 *
14 * See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
15 * the VirtIO specification v1.0.
16 */
17
18#include <common.h>
19#include <dm.h>
20#include <virtio_types.h>
21#include <virtio.h>
22#include <dm/lists.h>
23
24static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
25 [VIRTIO_ID_NET] = VIRTIO_NET_DRV_NAME,
26 [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
27};
28
29int virtio_get_config(struct udevice *vdev, unsigned int offset,
30 void *buf, unsigned int len)
31{
32 struct dm_virtio_ops *ops;
33
34 ops = virtio_get_ops(vdev->parent);
35
36 return ops->get_config(vdev->parent, offset, buf, len);
37}
38
39int virtio_set_config(struct udevice *vdev, unsigned int offset,
40 void *buf, unsigned int len)
41{
42 struct dm_virtio_ops *ops;
43
44 ops = virtio_get_ops(vdev->parent);
45
46 return ops->set_config(vdev->parent, offset, buf, len);
47}
48
49int virtio_generation(struct udevice *vdev, u32 *counter)
50{
51 struct dm_virtio_ops *ops;
52
53 ops = virtio_get_ops(vdev->parent);
54 if (!ops->generation)
55 return -ENOSYS;
56
57 return ops->generation(vdev->parent, counter);
58}
59
60int virtio_get_status(struct udevice *vdev, u8 *status)
61{
62 struct dm_virtio_ops *ops;
63
64 ops = virtio_get_ops(vdev->parent);
65
66 return ops->get_status(vdev->parent, status);
67}
68
69int virtio_set_status(struct udevice *vdev, u8 status)
70{
71 struct dm_virtio_ops *ops;
72
73 ops = virtio_get_ops(vdev->parent);
74
75 return ops->set_status(vdev->parent, status);
76}
77
78int virtio_reset(struct udevice *vdev)
79{
80 struct dm_virtio_ops *ops;
81
82 ops = virtio_get_ops(vdev->parent);
83
84 return ops->reset(vdev->parent);
85}
86
87int virtio_get_features(struct udevice *vdev, u64 *features)
88{
89 struct dm_virtio_ops *ops;
90
91 ops = virtio_get_ops(vdev->parent);
92
93 return ops->get_features(vdev->parent, features);
94}
95
96int virtio_set_features(struct udevice *vdev)
97{
98 struct dm_virtio_ops *ops;
99
100 ops = virtio_get_ops(vdev->parent);
101
102 return ops->set_features(vdev->parent);
103}
104
105int virtio_find_vqs(struct udevice *vdev, unsigned int nvqs,
106 struct virtqueue *vqs[])
107{
108 struct dm_virtio_ops *ops;
109
110 ops = virtio_get_ops(vdev->parent);
111
112 return ops->find_vqs(vdev->parent, nvqs, vqs);
113}
114
115int virtio_del_vqs(struct udevice *vdev)
116{
117 struct dm_virtio_ops *ops;
118
119 ops = virtio_get_ops(vdev->parent);
120
121 return ops->del_vqs(vdev->parent);
122}
123
124int virtio_notify(struct udevice *vdev, struct virtqueue *vq)
125{
126 struct dm_virtio_ops *ops;
127
128 ops = virtio_get_ops(vdev->parent);
129
130 return ops->notify(vdev->parent, vq);
131}
132
133void virtio_add_status(struct udevice *vdev, u8 status)
134{
135 u8 old;
136
137 if (!virtio_get_status(vdev, &old))
138 virtio_set_status(vdev, old | status);
139}
140
141int virtio_finalize_features(struct udevice *vdev)
142{
143 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
144 u8 status;
145 int ret;
146
147 ret = virtio_set_features(vdev);
148 if (ret)
149 return ret;
150
151 if (uc_priv->legacy)
152 return 0;
153
154 virtio_add_status(vdev, VIRTIO_CONFIG_S_FEATURES_OK);
155 ret = virtio_get_status(vdev, &status);
156 if (ret)
157 return ret;
158 if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
159 debug("(%s): device refuses features %x\n", vdev->name, status);
160 return -ENODEV;
161 }
162
163 return 0;
164}
165
166void virtio_driver_features_init(struct virtio_dev_priv *priv,
167 const u32 *feature,
168 u32 feature_size,
169 const u32 *feature_legacy,
170 u32 feature_legacy_size)
171{
172 priv->feature_table = feature;
173 priv->feature_table_size = feature_size;
174 priv->feature_table_legacy = feature_legacy;
175 priv->feature_table_size_legacy = feature_legacy_size;
176}
177
178int virtio_init(void)
179{
180 struct udevice *bus;
181 int ret;
182
183 /* Enumerate all known virtio devices */
184 ret = uclass_first_device(UCLASS_VIRTIO, &bus);
185 if (ret)
186 return ret;
187
188 while (bus) {
189 ret = uclass_next_device(&bus);
190 if (ret)
191 break;
192 }
193
194 return ret;
195}
196
197static int virtio_uclass_pre_probe(struct udevice *udev)
198{
199 struct dm_virtio_ops *ops;
200
201 ops = (struct dm_virtio_ops *)(udev->driver->ops);
202
203 /*
204 * Check virtio transport driver ops here so that we don't need
205 * check these ops each time when the virtio_xxx APIs are called.
206 *
207 * Only generation op is optional. All other ops are must-have.
208 */
209 if (!ops->get_config || !ops->set_config ||
210 !ops->get_status || !ops->set_status ||
211 !ops->get_features || !ops->set_features ||
212 !ops->find_vqs || !ops->del_vqs ||
213 !ops->reset || !ops->notify)
214 return -ENOENT;
215
216 return 0;
217}
218
219static int virtio_uclass_post_probe(struct udevice *udev)
220{
221 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
222 char dev_name[30], *str;
223 struct udevice *vdev;
224 int ret;
225
226 if (uc_priv->device > VIRTIO_ID_MAX_NUM) {
227 debug("(%s): virtio device ID %d exceeds maximum num\n",
228 udev->name, uc_priv->device);
229 return 0;
230 }
231
232 if (!virtio_drv_name[uc_priv->device]) {
233 debug("(%s): underlying virtio device driver unavailable\n",
234 udev->name);
235 return 0;
236 }
237
238 snprintf(dev_name, sizeof(dev_name), "%s#%d",
239 virtio_drv_name[uc_priv->device], udev->seq);
240 str = strdup(dev_name);
241 if (!str)
242 return -ENOMEM;
243
244 ret = device_bind_driver(udev, virtio_drv_name[uc_priv->device],
245 str, &vdev);
246 if (ret == -ENOENT) {
247 debug("(%s): no driver configured\n", udev->name);
248 return 0;
249 }
250 if (ret) {
251 free(str);
252 return ret;
253 }
254 device_set_name_alloced(vdev);
255
256 INIT_LIST_HEAD(&uc_priv->vqs);
257
258 return 0;
259}
260
261static int virtio_uclass_child_post_bind(struct udevice *vdev)
262{
263 /* Acknowledge that we've seen the device */
264 virtio_add_status(vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
265
266 return 0;
267}
268
269static int virtio_uclass_child_pre_probe(struct udevice *vdev)
270{
271 struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
272 u64 device_features;
273 u64 driver_features;
274 u64 driver_features_legacy;
275 int i;
276 int ret;
277
278 /*
279 * Save the real virtio device (eg: virtio-net, virtio-blk) to
280 * the transport (parent) device's uclass priv for future use.
281 */
282 uc_priv->vdev = vdev;
283
284 /*
285 * We always start by resetting the device, in case a previous driver
286 * messed it up. This also tests that code path a little.
287 */
288 ret = virtio_reset(vdev);
289 if (ret)
290 goto err;
291
292 /* We have a driver! */
293 virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER);
294
295 /* Figure out what features the device supports */
296 virtio_get_features(vdev, &device_features);
297 debug("(%s) plain device features supported %016llx\n",
298 vdev->name, device_features);
299 if (!(device_features & (1ULL << VIRTIO_F_VERSION_1)))
300 uc_priv->legacy = true;
301
302 /* Figure out what features the driver supports */
303 driver_features = 0;
304 for (i = 0; i < uc_priv->feature_table_size; i++) {
305 unsigned int f = uc_priv->feature_table[i];
306
307 WARN_ON(f >= 64);
308 driver_features |= (1ULL << f);
309 }
310
311 /* Some drivers have a separate feature table for virtio v1.0 */
312 if (uc_priv->feature_table_legacy) {
313 driver_features_legacy = 0;
314 for (i = 0; i < uc_priv->feature_table_size_legacy; i++) {
315 unsigned int f = uc_priv->feature_table_legacy[i];
316
317 WARN_ON(f >= 64);
318 driver_features_legacy |= (1ULL << f);
319 }
320 } else {
321 driver_features_legacy = driver_features;
322 }
323
324 if (uc_priv->legacy) {
325 debug("(%s): legacy virtio device\n", vdev->name);
326 uc_priv->features = driver_features_legacy & device_features;
327 } else {
328 debug("(%s): v1.0 complaint virtio device\n", vdev->name);
329 uc_priv->features = driver_features & device_features;
330 }
331
332 /* Transport features always preserved to pass to finalize_features */
333 for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
334 if ((device_features & (1ULL << i)) &&
335 (i == VIRTIO_F_VERSION_1))
336 __virtio_set_bit(vdev->parent, i);
337
338 debug("(%s) final negotiated features supported %016llx\n",
339 vdev->name, uc_priv->features);
340 ret = virtio_finalize_features(vdev);
341 if (ret)
342 goto err;
343
344 return 0;
345
346err:
347 virtio_add_status(vdev, VIRTIO_CONFIG_S_FAILED);
348 return ret;
349}
350
351static int virtio_uclass_child_post_probe(struct udevice *vdev)
352{
353 /* Indicates that the driver is set up and ready to drive the device */
354 virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER_OK);
355
356 return 0;
357}
358
359UCLASS_DRIVER(virtio) = {
360 .name = "virtio",
361 .id = UCLASS_VIRTIO,
362 .flags = DM_UC_FLAG_SEQ_ALIAS,
363 .pre_probe = virtio_uclass_pre_probe,
364 .post_probe = virtio_uclass_post_probe,
365 .child_post_bind = virtio_uclass_child_post_bind,
366 .child_pre_probe = virtio_uclass_child_pre_probe,
367 .child_post_probe = virtio_uclass_child_post_probe,
368 .per_device_auto_alloc_size = sizeof(struct virtio_dev_priv),
369};