diff options
author | Bin Meng | 2018-10-15 04:21:00 -0500 |
---|---|---|
committer | Simon Glass | 2018-11-14 11:16:27 -0600 |
commit | 8fb49b4c7a820461db7c11dce767f36fd6395cac (patch) | |
tree | 3289c27071a293689680da11b1d9614531d7cce9 | |
parent | b1893a9e0def4052e56513bfcee0a0eb95841f7f (diff) | |
download | u-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>
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/virtio/Kconfig | 25 | ||||
-rw-r--r-- | drivers/virtio/Makefile | 6 | ||||
-rw-r--r-- | drivers/virtio/virtio-uclass.c | 369 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 | ||||
-rw-r--r-- | include/virtio.h | 707 | ||||
-rw-r--r-- | include/virtio_types.h | 24 |
8 files changed, 1135 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 | ||
113 | source "drivers/video/Kconfig" | 113 | source "drivers/video/Kconfig" |
114 | 114 | ||
115 | source "drivers/virtio/Kconfig" | ||
116 | |||
115 | source "drivers/w1/Kconfig" | 117 | source "drivers/w1/Kconfig" |
116 | 118 | ||
117 | source "drivers/w1-eeprom/Kconfig" | 119 | source "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/ | |||
14 | obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/ | 14 | obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += mtd/spi/ |
15 | obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/ | 15 | obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/ |
16 | obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ | 16 | obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ |
17 | obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/ | ||
17 | obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ | 18 | obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ |
18 | obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/ | 19 | obj-$(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 | |||
17 | menu "VirtIO Drivers" | ||
18 | |||
19 | config 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 | |||
25 | endmenu | ||
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 | |||
6 | obj-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 | |||
24 | static 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 | |||
29 | int 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 | |||
39 | int 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 | |||
49 | int 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 | |||
60 | int 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 | |||
69 | int 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 | |||
78 | int 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 | |||
87 | int 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 | |||
96 | int 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 | |||
105 | int 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 | |||
115 | int 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 | |||
124 | int 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 | |||
133 | void 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 | |||
141 | int 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 | |||
166 | void 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 | |||
178 | int 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 | |||
197 | static 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 | |||
219 | static 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 | |||
261 | static 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 | |||
269 | static 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 | |||
346 | err: | ||
347 | virtio_add_status(vdev, VIRTIO_CONFIG_S_FAILED); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static 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 | |||
359 | UCLASS_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 | }; | ||
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 269a2c6e72..c91dca1f82 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h | |||
@@ -96,6 +96,7 @@ enum uclass_id { | |||
96 | UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ | 96 | UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ |
97 | UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ | 97 | UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ |
98 | UCLASS_VIDEO_OSD, /* On-screen display */ | 98 | UCLASS_VIDEO_OSD, /* On-screen display */ |
99 | UCLASS_VIRTIO, /* VirtIO transport device */ | ||
99 | UCLASS_W1, /* Dallas 1-Wire bus */ | 100 | UCLASS_W1, /* Dallas 1-Wire bus */ |
100 | UCLASS_W1_EEPROM, /* one-wire EEPROMs */ | 101 | UCLASS_W1_EEPROM, /* one-wire EEPROMs */ |
101 | UCLASS_WDT, /* Watchdot Timer driver */ | 102 | UCLASS_WDT, /* Watchdot Timer driver */ |
diff --git a/include/virtio.h b/include/virtio.h new file mode 100644 index 0000000000..654fdf154b --- /dev/null +++ b/include/virtio.h | |||
@@ -0,0 +1,707 @@ | |||
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 | * This file is largely based on Linux kernel virtio_*.h files | ||
18 | */ | ||
19 | |||
20 | #ifndef __VIRTIO_H__ | ||
21 | #define __VIRTIO_H__ | ||
22 | |||
23 | #define VIRTIO_ID_NET 1 /* virtio net */ | ||
24 | #define VIRTIO_ID_BLOCK 2 /* virtio block */ | ||
25 | #define VIRTIO_ID_MAX_NUM 3 | ||
26 | |||
27 | #define VIRTIO_NET_DRV_NAME "virtio-net" | ||
28 | #define VIRTIO_BLK_DRV_NAME "virtio-blk" | ||
29 | |||
30 | /* Status byte for guest to report progress, and synchronize features */ | ||
31 | |||
32 | /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ | ||
33 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 | ||
34 | /* We have found a driver for the device */ | ||
35 | #define VIRTIO_CONFIG_S_DRIVER 2 | ||
36 | /* Driver has used its parts of the config, and is happy */ | ||
37 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 | ||
38 | /* Driver has finished configuring features */ | ||
39 | #define VIRTIO_CONFIG_S_FEATURES_OK 8 | ||
40 | /* Device entered invalid state, driver must reset it */ | ||
41 | #define VIRTIO_CONFIG_S_NEEDS_RESET 0x40 | ||
42 | /* We've given up on this device */ | ||
43 | #define VIRTIO_CONFIG_S_FAILED 0x80 | ||
44 | |||
45 | /* | ||
46 | * Virtio feature bits VIRTIO_TRANSPORT_F_START through VIRTIO_TRANSPORT_F_END | ||
47 | * are reserved for the transport being used (eg: virtio_ring, virtio_pci etc.), | ||
48 | * the rest are per-device feature bits. | ||
49 | */ | ||
50 | #define VIRTIO_TRANSPORT_F_START 28 | ||
51 | #define VIRTIO_TRANSPORT_F_END 38 | ||
52 | |||
53 | #ifndef VIRTIO_CONFIG_NO_LEGACY | ||
54 | /* | ||
55 | * Do we get callbacks when the ring is completely used, | ||
56 | * even if we've suppressed them? | ||
57 | */ | ||
58 | #define VIRTIO_F_NOTIFY_ON_EMPTY 24 | ||
59 | |||
60 | /* Can the device handle any descriptor layout? */ | ||
61 | #define VIRTIO_F_ANY_LAYOUT 27 | ||
62 | #endif /* VIRTIO_CONFIG_NO_LEGACY */ | ||
63 | |||
64 | /* v1.0 compliant */ | ||
65 | #define VIRTIO_F_VERSION_1 32 | ||
66 | |||
67 | /* | ||
68 | * If clear - device has the IOMMU bypass quirk feature. | ||
69 | * If set - use platform tools to detect the IOMMU. | ||
70 | * | ||
71 | * Note the reverse polarity (compared to most other features), | ||
72 | * this is for compatibility with legacy systems. | ||
73 | */ | ||
74 | #define VIRTIO_F_IOMMU_PLATFORM 33 | ||
75 | |||
76 | /* Does the device support Single Root I/O Virtualization? */ | ||
77 | #define VIRTIO_F_SR_IOV 37 | ||
78 | |||
79 | /** | ||
80 | * virtio scatter-gather struct | ||
81 | * | ||
82 | * @addr: sg buffer address | ||
83 | * @lengh: sg buffer length | ||
84 | */ | ||
85 | struct virtio_sg { | ||
86 | void *addr; | ||
87 | size_t length; | ||
88 | }; | ||
89 | |||
90 | struct virtqueue; | ||
91 | |||
92 | /* virtio bus operations */ | ||
93 | struct dm_virtio_ops { | ||
94 | /** | ||
95 | * get_config() - read the value of a configuration field | ||
96 | * | ||
97 | * @vdev: the real virtio device | ||
98 | * @offset: the offset of the configuration field | ||
99 | * @buf: the buffer to write the field value into | ||
100 | * @len: the length of the buffer | ||
101 | * @return 0 if OK, -ve on error | ||
102 | */ | ||
103 | int (*get_config)(struct udevice *vdev, unsigned int offset, | ||
104 | void *buf, unsigned int len); | ||
105 | /** | ||
106 | * set_config() - write the value of a configuration field | ||
107 | * | ||
108 | * @vdev: the real virtio device | ||
109 | * @offset: the offset of the configuration field | ||
110 | * @buf: the buffer to read the field value from | ||
111 | * @len: the length of the buffer | ||
112 | * @return 0 if OK, -ve on error | ||
113 | */ | ||
114 | int (*set_config)(struct udevice *vdev, unsigned int offset, | ||
115 | const void *buf, unsigned int len); | ||
116 | /** | ||
117 | * generation() - config generation counter | ||
118 | * | ||
119 | * @vdev: the real virtio device | ||
120 | * @counter: the returned config generation counter | ||
121 | * @return 0 if OK, -ve on error | ||
122 | */ | ||
123 | int (*generation)(struct udevice *vdev, u32 *counter); | ||
124 | /** | ||
125 | * get_status() - read the status byte | ||
126 | * | ||
127 | * @vdev: the real virtio device | ||
128 | * @status: the returned status byte | ||
129 | * @return 0 if OK, -ve on error | ||
130 | */ | ||
131 | int (*get_status)(struct udevice *vdev, u8 *status); | ||
132 | /** | ||
133 | * set_status() - write the status byte | ||
134 | * | ||
135 | * @vdev: the real virtio device | ||
136 | * @status: the new status byte | ||
137 | * @return 0 if OK, -ve on error | ||
138 | */ | ||
139 | int (*set_status)(struct udevice *vdev, u8 status); | ||
140 | /** | ||
141 | * reset() - reset the device | ||
142 | * | ||
143 | * @vdev: the real virtio device | ||
144 | * @return 0 if OK, -ve on error | ||
145 | */ | ||
146 | int (*reset)(struct udevice *vdev); | ||
147 | /** | ||
148 | * get_features() - get the array of feature bits for this device | ||
149 | * | ||
150 | * @vdev: the real virtio device | ||
151 | * @features: the first 32 feature bits (all we currently need) | ||
152 | * @return 0 if OK, -ve on error | ||
153 | */ | ||
154 | int (*get_features)(struct udevice *vdev, u64 *features); | ||
155 | /** | ||
156 | * set_features() - confirm what device features we'll be using | ||
157 | * | ||
158 | * @vdev: the real virtio device | ||
159 | * @return 0 if OK, -ve on error | ||
160 | */ | ||
161 | int (*set_features)(struct udevice *vdev); | ||
162 | /** | ||
163 | * find_vqs() - find virtqueues and instantiate them | ||
164 | * | ||
165 | * @vdev: the real virtio device | ||
166 | * @nvqs: the number of virtqueues to find | ||
167 | * @vqs: on success, includes new virtqueues | ||
168 | * @return 0 if OK, -ve on error | ||
169 | */ | ||
170 | int (*find_vqs)(struct udevice *vdev, unsigned int nvqs, | ||
171 | struct virtqueue *vqs[]); | ||
172 | /** | ||
173 | * del_vqs() - free virtqueues found by find_vqs() | ||
174 | * | ||
175 | * @vdev: the real virtio device | ||
176 | * @return 0 if OK, -ve on error | ||
177 | */ | ||
178 | int (*del_vqs)(struct udevice *vdev); | ||
179 | /** | ||
180 | * notify() - notify the device to process the queue | ||
181 | * | ||
182 | * @vdev: the real virtio device | ||
183 | * @vq: virtqueue to process | ||
184 | * @return 0 if OK, -ve on error | ||
185 | */ | ||
186 | int (*notify)(struct udevice *vdev, struct virtqueue *vq); | ||
187 | }; | ||
188 | |||
189 | /* Get access to a virtio bus' operations */ | ||
190 | #define virtio_get_ops(dev) ((struct dm_virtio_ops *)(dev)->driver->ops) | ||
191 | |||
192 | /** | ||
193 | * virtio uclass per device private data | ||
194 | * | ||
195 | * @vqs: virtualqueue for the virtio device | ||
196 | * @vdev: the real virtio device underneath | ||
197 | * @legacy: is it a legacy device? | ||
198 | * @device: virtio device ID | ||
199 | * @vendor: virtio vendor ID | ||
200 | * @features: negotiated supported features | ||
201 | * @feature_table: an array of feature supported by the driver | ||
202 | * @feature_table_size: number of entries in the feature table array | ||
203 | * @feature_table_legacy: same as feature_table but working in legacy mode | ||
204 | * @feature_table_size_legacy: number of entries in feature table legacy array | ||
205 | */ | ||
206 | struct virtio_dev_priv { | ||
207 | struct list_head vqs; | ||
208 | struct udevice *vdev; | ||
209 | bool legacy; | ||
210 | u32 device; | ||
211 | u32 vendor; | ||
212 | u64 features; | ||
213 | const u32 *feature_table; | ||
214 | u32 feature_table_size; | ||
215 | const u32 *feature_table_legacy; | ||
216 | u32 feature_table_size_legacy; | ||
217 | }; | ||
218 | |||
219 | /** | ||
220 | * virtio_get_config() - read the value of a configuration field | ||
221 | * | ||
222 | * @vdev: the real virtio device | ||
223 | * @offset: the offset of the configuration field | ||
224 | * @buf: the buffer to write the field value into | ||
225 | * @len: the length of the buffer | ||
226 | * @return 0 if OK, -ve on error | ||
227 | */ | ||
228 | int virtio_get_config(struct udevice *vdev, unsigned int offset, | ||
229 | void *buf, unsigned int len); | ||
230 | |||
231 | /** | ||
232 | * virtio_set_config() - write the value of a configuration field | ||
233 | * | ||
234 | * @vdev: the real virtio device | ||
235 | * @offset: the offset of the configuration field | ||
236 | * @buf: the buffer to read the field value from | ||
237 | * @len: the length of the buffer | ||
238 | * @return 0 if OK, -ve on error | ||
239 | */ | ||
240 | int virtio_set_config(struct udevice *vdev, unsigned int offset, | ||
241 | void *buf, unsigned int len); | ||
242 | |||
243 | /** | ||
244 | * virtio_generation() - config generation counter | ||
245 | * | ||
246 | * @vdev: the real virtio device | ||
247 | * @counter: the returned config generation counter | ||
248 | * @return 0 if OK, -ve on error | ||
249 | */ | ||
250 | int virtio_generation(struct udevice *vdev, u32 *counter); | ||
251 | |||
252 | /** | ||
253 | * virtio_get_status() - read the status byte | ||
254 | * | ||
255 | * @vdev: the real virtio device | ||
256 | * @status: the returned status byte | ||
257 | * @return 0 if OK, -ve on error | ||
258 | */ | ||
259 | int virtio_get_status(struct udevice *vdev, u8 *status); | ||
260 | |||
261 | /** | ||
262 | * virtio_set_status() - write the status byte | ||
263 | * | ||
264 | * @vdev: the real virtio device | ||
265 | * @status: the new status byte | ||
266 | * @return 0 if OK, -ve on error | ||
267 | */ | ||
268 | int virtio_set_status(struct udevice *vdev, u8 status); | ||
269 | |||
270 | /** | ||
271 | * virtio_reset() - reset the device | ||
272 | * | ||
273 | * @vdev: the real virtio device | ||
274 | * @return 0 if OK, -ve on error | ||
275 | */ | ||
276 | int virtio_reset(struct udevice *vdev); | ||
277 | |||
278 | /** | ||
279 | * virtio_get_features() - get the array of feature bits for this device | ||
280 | * | ||
281 | * @vdev: the real virtio device | ||
282 | * @features: the first 32 feature bits (all we currently need) | ||
283 | * @return 0 if OK, -ve on error | ||
284 | */ | ||
285 | int virtio_get_features(struct udevice *vdev, u64 *features); | ||
286 | |||
287 | /** | ||
288 | * virtio_set_features() - confirm what device features we'll be using | ||
289 | * | ||
290 | * @vdev: the real virtio device | ||
291 | * @return 0 if OK, -ve on error | ||
292 | */ | ||
293 | int virtio_set_features(struct udevice *vdev); | ||
294 | |||
295 | /** | ||
296 | * virtio_find_vqs() - find virtqueues and instantiate them | ||
297 | * | ||
298 | * @vdev: the real virtio device | ||
299 | * @nvqs: the number of virtqueues to find | ||
300 | * @vqs: on success, includes new virtqueues | ||
301 | * @return 0 if OK, -ve on error | ||
302 | */ | ||
303 | int virtio_find_vqs(struct udevice *vdev, unsigned int nvqs, | ||
304 | struct virtqueue *vqs[]); | ||
305 | |||
306 | /** | ||
307 | * virtio_del_vqs() - free virtqueues found by find_vqs() | ||
308 | * | ||
309 | * @vdev: the real virtio device | ||
310 | * @return 0 if OK, -ve on error | ||
311 | */ | ||
312 | int virtio_del_vqs(struct udevice *vdev); | ||
313 | |||
314 | /** | ||
315 | * virtio_notify() - notify the device to process the queue | ||
316 | * | ||
317 | * @vdev: the real virtio device | ||
318 | * @vq: virtqueue to process | ||
319 | * @return 0 if OK, -ve on error | ||
320 | */ | ||
321 | int virtio_notify(struct udevice *vdev, struct virtqueue *vq); | ||
322 | |||
323 | /** | ||
324 | * virtio_add_status() - helper to set a new status code to the device | ||
325 | * | ||
326 | * @vdev: the real virtio device | ||
327 | * @status: new status code to be added | ||
328 | */ | ||
329 | void virtio_add_status(struct udevice *vdev, u8 status); | ||
330 | |||
331 | /** | ||
332 | * virtio_finalize_features() - helper to finalize features | ||
333 | * | ||
334 | * @vdev: the real virtio device | ||
335 | * @return 0 if OK, -ve on error | ||
336 | */ | ||
337 | int virtio_finalize_features(struct udevice *vdev); | ||
338 | |||
339 | /** | ||
340 | * virtio_driver_features_init() - initialize driver supported features | ||
341 | * | ||
342 | * This fills in the virtio device parent per child private data with the given | ||
343 | * information, which contains driver supported features and legacy features. | ||
344 | * | ||
345 | * This API should be called in the virtio device driver's bind method, so that | ||
346 | * later virtio transport uclass driver can utilize the driver supplied features | ||
347 | * to negotiate with the device on the final supported features. | ||
348 | * | ||
349 | * @priv: virtio uclass per device private data | ||
350 | * @feature: an array of feature supported by the driver | ||
351 | * @feature_size: number of entries in the feature table array | ||
352 | * @feature_legacy: same as feature_table but working in legacy mode | ||
353 | * @feature_legacy_size:number of entries in feature table legacy array | ||
354 | */ | ||
355 | void virtio_driver_features_init(struct virtio_dev_priv *priv, | ||
356 | const u32 *feature, | ||
357 | u32 feature_size, | ||
358 | const u32 *feature_legacy, | ||
359 | u32 feature_legacy_size); | ||
360 | |||
361 | /** | ||
362 | * virtio_init() - helper to enumerate all known virtio devices | ||
363 | * | ||
364 | * @return 0 if OK, -ve on error | ||
365 | */ | ||
366 | int virtio_init(void); | ||
367 | |||
368 | static inline u16 __virtio16_to_cpu(bool little_endian, __virtio16 val) | ||
369 | { | ||
370 | if (little_endian) | ||
371 | return le16_to_cpu((__force __le16)val); | ||
372 | else | ||
373 | return be16_to_cpu((__force __be16)val); | ||
374 | } | ||
375 | |||
376 | static inline __virtio16 __cpu_to_virtio16(bool little_endian, u16 val) | ||
377 | { | ||
378 | if (little_endian) | ||
379 | return (__force __virtio16)cpu_to_le16(val); | ||
380 | else | ||
381 | return (__force __virtio16)cpu_to_be16(val); | ||
382 | } | ||
383 | |||
384 | static inline u32 __virtio32_to_cpu(bool little_endian, __virtio32 val) | ||
385 | { | ||
386 | if (little_endian) | ||
387 | return le32_to_cpu((__force __le32)val); | ||
388 | else | ||
389 | return be32_to_cpu((__force __be32)val); | ||
390 | } | ||
391 | |||
392 | static inline __virtio32 __cpu_to_virtio32(bool little_endian, u32 val) | ||
393 | { | ||
394 | if (little_endian) | ||
395 | return (__force __virtio32)cpu_to_le32(val); | ||
396 | else | ||
397 | return (__force __virtio32)cpu_to_be32(val); | ||
398 | } | ||
399 | |||
400 | static inline u64 __virtio64_to_cpu(bool little_endian, __virtio64 val) | ||
401 | { | ||
402 | if (little_endian) | ||
403 | return le64_to_cpu((__force __le64)val); | ||
404 | else | ||
405 | return be64_to_cpu((__force __be64)val); | ||
406 | } | ||
407 | |||
408 | static inline __virtio64 __cpu_to_virtio64(bool little_endian, u64 val) | ||
409 | { | ||
410 | if (little_endian) | ||
411 | return (__force __virtio64)cpu_to_le64(val); | ||
412 | else | ||
413 | return (__force __virtio64)cpu_to_be64(val); | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * __virtio_test_bit - helper to test feature bits | ||
418 | * | ||
419 | * For use by transports. Devices should normally use virtio_has_feature, | ||
420 | * which includes more checks. | ||
421 | * | ||
422 | * @udev: the transport device | ||
423 | * @fbit: the feature bit | ||
424 | */ | ||
425 | static inline bool __virtio_test_bit(struct udevice *udev, unsigned int fbit) | ||
426 | { | ||
427 | struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | ||
428 | |||
429 | /* Did you forget to fix assumptions on max features? */ | ||
430 | if (__builtin_constant_p(fbit)) | ||
431 | BUILD_BUG_ON(fbit >= 64); | ||
432 | else | ||
433 | WARN_ON(fbit >= 64); | ||
434 | |||
435 | return uc_priv->features & BIT_ULL(fbit); | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * __virtio_set_bit - helper to set feature bits | ||
440 | * | ||
441 | * For use by transports. | ||
442 | * | ||
443 | * @udev: the transport device | ||
444 | * @fbit: the feature bit | ||
445 | */ | ||
446 | static inline void __virtio_set_bit(struct udevice *udev, unsigned int fbit) | ||
447 | { | ||
448 | struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | ||
449 | |||
450 | /* Did you forget to fix assumptions on max features? */ | ||
451 | if (__builtin_constant_p(fbit)) | ||
452 | BUILD_BUG_ON(fbit >= 64); | ||
453 | else | ||
454 | WARN_ON(fbit >= 64); | ||
455 | |||
456 | uc_priv->features |= BIT_ULL(fbit); | ||
457 | } | ||
458 | |||
459 | /** | ||
460 | * __virtio_clear_bit - helper to clear feature bits | ||
461 | * | ||
462 | * For use by transports. | ||
463 | * | ||
464 | * @vdev: the transport device | ||
465 | * @fbit: the feature bit | ||
466 | */ | ||
467 | static inline void __virtio_clear_bit(struct udevice *udev, unsigned int fbit) | ||
468 | { | ||
469 | struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | ||
470 | |||
471 | /* Did you forget to fix assumptions on max features? */ | ||
472 | if (__builtin_constant_p(fbit)) | ||
473 | BUILD_BUG_ON(fbit >= 64); | ||
474 | else | ||
475 | WARN_ON(fbit >= 64); | ||
476 | |||
477 | uc_priv->features &= ~BIT_ULL(fbit); | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * virtio_has_feature - helper to determine if this device has this feature | ||
482 | * | ||
483 | * Note this API is only usable after the virtio device driver's bind phase, | ||
484 | * as the feature has been negotiated between the device and the driver. | ||
485 | * | ||
486 | * @vdev: the virtio device | ||
487 | * @fbit: the feature bit | ||
488 | */ | ||
489 | static inline bool virtio_has_feature(struct udevice *vdev, unsigned int fbit) | ||
490 | { | ||
491 | if (!(vdev->flags & DM_FLAG_BOUND)) | ||
492 | WARN_ON(true); | ||
493 | |||
494 | return __virtio_test_bit(vdev->parent, fbit); | ||
495 | } | ||
496 | |||
497 | static inline bool virtio_legacy_is_little_endian(void) | ||
498 | { | ||
499 | #ifdef __LITTLE_ENDIAN | ||
500 | return true; | ||
501 | #else | ||
502 | return false; | ||
503 | #endif | ||
504 | } | ||
505 | |||
506 | static inline bool virtio_is_little_endian(struct udevice *vdev) | ||
507 | { | ||
508 | struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent); | ||
509 | |||
510 | return !uc_priv->legacy || virtio_legacy_is_little_endian(); | ||
511 | } | ||
512 | |||
513 | /* Memory accessors */ | ||
514 | static inline u16 virtio16_to_cpu(struct udevice *vdev, __virtio16 val) | ||
515 | { | ||
516 | return __virtio16_to_cpu(virtio_is_little_endian(vdev), val); | ||
517 | } | ||
518 | |||
519 | static inline __virtio16 cpu_to_virtio16(struct udevice *vdev, u16 val) | ||
520 | { | ||
521 | return __cpu_to_virtio16(virtio_is_little_endian(vdev), val); | ||
522 | } | ||
523 | |||
524 | static inline u32 virtio32_to_cpu(struct udevice *vdev, __virtio32 val) | ||
525 | { | ||
526 | return __virtio32_to_cpu(virtio_is_little_endian(vdev), val); | ||
527 | } | ||
528 | |||
529 | static inline __virtio32 cpu_to_virtio32(struct udevice *vdev, u32 val) | ||
530 | { | ||
531 | return __cpu_to_virtio32(virtio_is_little_endian(vdev), val); | ||
532 | } | ||
533 | |||
534 | static inline u64 virtio64_to_cpu(struct udevice *vdev, __virtio64 val) | ||
535 | { | ||
536 | return __virtio64_to_cpu(virtio_is_little_endian(vdev), val); | ||
537 | } | ||
538 | |||
539 | static inline __virtio64 cpu_to_virtio64(struct udevice *vdev, u64 val) | ||
540 | { | ||
541 | return __cpu_to_virtio64(virtio_is_little_endian(vdev), val); | ||
542 | } | ||
543 | |||
544 | /* Read @count fields, @bytes each */ | ||
545 | static inline void __virtio_cread_many(struct udevice *vdev, | ||
546 | unsigned int offset, | ||
547 | void *buf, size_t count, size_t bytes) | ||
548 | { | ||
549 | u32 old, gen; | ||
550 | int i; | ||
551 | |||
552 | /* no need to check return value as generation can be optional */ | ||
553 | virtio_generation(vdev, &gen); | ||
554 | do { | ||
555 | old = gen; | ||
556 | |||
557 | for (i = 0; i < count; i++) | ||
558 | virtio_get_config(vdev, offset + bytes * i, | ||
559 | buf + i * bytes, bytes); | ||
560 | |||
561 | virtio_generation(vdev, &gen); | ||
562 | } while (gen != old); | ||
563 | } | ||
564 | |||
565 | static inline void virtio_cread_bytes(struct udevice *vdev, | ||
566 | unsigned int offset, | ||
567 | void *buf, size_t len) | ||
568 | { | ||
569 | __virtio_cread_many(vdev, offset, buf, len, 1); | ||
570 | } | ||
571 | |||
572 | static inline u8 virtio_cread8(struct udevice *vdev, unsigned int offset) | ||
573 | { | ||
574 | u8 ret; | ||
575 | |||
576 | virtio_get_config(vdev, offset, &ret, sizeof(ret)); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | static inline void virtio_cwrite8(struct udevice *vdev, | ||
581 | unsigned int offset, u8 val) | ||
582 | { | ||
583 | virtio_set_config(vdev, offset, &val, sizeof(val)); | ||
584 | } | ||
585 | |||
586 | static inline u16 virtio_cread16(struct udevice *vdev, | ||
587 | unsigned int offset) | ||
588 | { | ||
589 | u16 ret; | ||
590 | |||
591 | virtio_get_config(vdev, offset, &ret, sizeof(ret)); | ||
592 | return virtio16_to_cpu(vdev, (__force __virtio16)ret); | ||
593 | } | ||
594 | |||
595 | static inline void virtio_cwrite16(struct udevice *vdev, | ||
596 | unsigned int offset, u16 val) | ||
597 | { | ||
598 | val = (__force u16)cpu_to_virtio16(vdev, val); | ||
599 | virtio_set_config(vdev, offset, &val, sizeof(val)); | ||
600 | } | ||
601 | |||
602 | static inline u32 virtio_cread32(struct udevice *vdev, | ||
603 | unsigned int offset) | ||
604 | { | ||
605 | u32 ret; | ||
606 | |||
607 | virtio_get_config(vdev, offset, &ret, sizeof(ret)); | ||
608 | return virtio32_to_cpu(vdev, (__force __virtio32)ret); | ||
609 | } | ||
610 | |||
611 | static inline void virtio_cwrite32(struct udevice *vdev, | ||
612 | unsigned int offset, u32 val) | ||
613 | { | ||
614 | val = (__force u32)cpu_to_virtio32(vdev, val); | ||
615 | virtio_set_config(vdev, offset, &val, sizeof(val)); | ||
616 | } | ||
617 | |||
618 | static inline u64 virtio_cread64(struct udevice *vdev, | ||
619 | unsigned int offset) | ||
620 | { | ||
621 | u64 ret; | ||
622 | |||
623 | __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret)); | ||
624 | return virtio64_to_cpu(vdev, (__force __virtio64)ret); | ||
625 | } | ||
626 | |||
627 | static inline void virtio_cwrite64(struct udevice *vdev, | ||
628 | unsigned int offset, u64 val) | ||
629 | { | ||
630 | val = (__force u64)cpu_to_virtio64(vdev, val); | ||
631 | virtio_set_config(vdev, offset, &val, sizeof(val)); | ||
632 | } | ||
633 | |||
634 | /* Config space read accessor */ | ||
635 | #define virtio_cread(vdev, structname, member, ptr) \ | ||
636 | do { \ | ||
637 | /* Must match the member's type, and be integer */ \ | ||
638 | if (!typecheck(typeof((((structname *)0)->member)), *(ptr))) \ | ||
639 | (*ptr) = 1; \ | ||
640 | \ | ||
641 | switch (sizeof(*ptr)) { \ | ||
642 | case 1: \ | ||
643 | *(ptr) = virtio_cread8(vdev, \ | ||
644 | offsetof(structname, member)); \ | ||
645 | break; \ | ||
646 | case 2: \ | ||
647 | *(ptr) = virtio_cread16(vdev, \ | ||
648 | offsetof(structname, member)); \ | ||
649 | break; \ | ||
650 | case 4: \ | ||
651 | *(ptr) = virtio_cread32(vdev, \ | ||
652 | offsetof(structname, member)); \ | ||
653 | break; \ | ||
654 | case 8: \ | ||
655 | *(ptr) = virtio_cread64(vdev, \ | ||
656 | offsetof(structname, member)); \ | ||
657 | break; \ | ||
658 | default: \ | ||
659 | WARN_ON(true); \ | ||
660 | } \ | ||
661 | } while (0) | ||
662 | |||
663 | /* Config space write accessor */ | ||
664 | #define virtio_cwrite(vdev, structname, member, ptr) \ | ||
665 | do { \ | ||
666 | /* Must match the member's type, and be integer */ \ | ||
667 | if (!typecheck(typeof((((structname *)0)->member)), *(ptr))) \ | ||
668 | WARN_ON((*ptr) == 1); \ | ||
669 | \ | ||
670 | switch (sizeof(*ptr)) { \ | ||
671 | case 1: \ | ||
672 | virtio_cwrite8(vdev, \ | ||
673 | offsetof(structname, member), \ | ||
674 | *(ptr)); \ | ||
675 | break; \ | ||
676 | case 2: \ | ||
677 | virtio_cwrite16(vdev, \ | ||
678 | offsetof(structname, member), \ | ||
679 | *(ptr)); \ | ||
680 | break; \ | ||
681 | case 4: \ | ||
682 | virtio_cwrite32(vdev, \ | ||
683 | offsetof(structname, member), \ | ||
684 | *(ptr)); \ | ||
685 | break; \ | ||
686 | case 8: \ | ||
687 | virtio_cwrite64(vdev, \ | ||
688 | offsetof(structname, member), \ | ||
689 | *(ptr)); \ | ||
690 | break; \ | ||
691 | default: \ | ||
692 | WARN_ON(true); \ | ||
693 | } \ | ||
694 | } while (0) | ||
695 | |||
696 | /* Conditional config space accessors */ | ||
697 | #define virtio_cread_feature(vdev, fbit, structname, member, ptr) \ | ||
698 | ({ \ | ||
699 | int _r = 0; \ | ||
700 | if (!virtio_has_feature(vdev, fbit)) \ | ||
701 | _r = -ENOENT; \ | ||
702 | else \ | ||
703 | virtio_cread(vdev, structname, member, ptr); \ | ||
704 | _r; \ | ||
705 | }) | ||
706 | |||
707 | #endif /* __VIRTIO_H__ */ | ||
diff --git a/include/virtio_types.h b/include/virtio_types.h new file mode 100644 index 0000000000..d700d1936d --- /dev/null +++ b/include/virtio_types.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* SPDX-License-Identifier: BSD-3-Clause */ | ||
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_types.h | ||
7 | */ | ||
8 | |||
9 | #ifndef _LINUX_VIRTIO_TYPES_H | ||
10 | #define _LINUX_VIRTIO_TYPES_H | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | |||
14 | /* | ||
15 | * __virtio{16,32,64} have the following meaning: | ||
16 | * - __u{16,32,64} for virtio devices in legacy mode, accessed in native endian | ||
17 | * - __le{16,32,64} for standard-compliant virtio devices | ||
18 | */ | ||
19 | |||
20 | typedef __u16 __bitwise __virtio16; | ||
21 | typedef __u32 __bitwise __virtio32; | ||
22 | typedef __u64 __bitwise __virtio64; | ||
23 | |||
24 | #endif /* _LINUX_VIRTIO_TYPES_H */ | ||