| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> | |
| * | |
| * VirtIO Sandbox transport driver, for testing purpose only | |
| */ | |
| #include <dm.h> | |
| #include <virtio_types.h> | |
| #include <virtio.h> | |
| #include <virtio_ring.h> | |
| #include <linux/bug.h> | |
| #include <linux/compat.h> | |
| #include <linux/err.h> | |
| #include <linux/io.h> | |
| struct virtio_sandbox_priv { | |
| u8 id; | |
| u8 status; | |
| u64 device_features; | |
| u64 driver_features; | |
| ulong queue_desc; | |
| ulong queue_available; | |
| ulong queue_used; | |
| }; | |
| static int virtio_sandbox_get_config(struct udevice *udev, unsigned int offset, | |
| void *buf, unsigned int len) | |
| { | |
| return 0; | |
| } | |
| static int virtio_sandbox_set_config(struct udevice *udev, unsigned int offset, | |
| const void *buf, unsigned int len) | |
| { | |
| return 0; | |
| } | |
| static int virtio_sandbox_get_status(struct udevice *udev, u8 *status) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| *status = priv->status; | |
| return 0; | |
| } | |
| static int virtio_sandbox_set_status(struct udevice *udev, u8 status) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| /* We should never be setting status to 0 */ | |
| WARN_ON(status == 0); | |
| priv->status = status; | |
| return 0; | |
| } | |
| static int virtio_sandbox_reset(struct udevice *udev) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| /* 0 status means a reset */ | |
| priv->status = 0; | |
| return 0; | |
| } | |
| static int virtio_sandbox_get_features(struct udevice *udev, u64 *features) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| *features = priv->device_features; | |
| return 0; | |
| } | |
| static int virtio_sandbox_set_features(struct udevice *udev) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | |
| priv->driver_features = uc_priv->features; | |
| return 0; | |
| } | |
| static struct virtqueue *virtio_sandbox_setup_vq(struct udevice *udev, | |
| unsigned int index) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| struct virtqueue *vq; | |
| ulong addr; | |
| int err; | |
| /* Create the vring */ | |
| vq = vring_create_virtqueue(index, 4, 4096, udev); | |
| if (!vq) { | |
| err = -ENOMEM; | |
| goto error_new_virtqueue; | |
| } | |
| addr = virtqueue_get_desc_addr(vq); | |
| priv->queue_desc = addr; | |
| addr = virtqueue_get_avail_addr(vq); | |
| priv->queue_available = addr; | |
| addr = virtqueue_get_used_addr(vq); | |
| priv->queue_used = addr; | |
| return vq; | |
| error_new_virtqueue: | |
| return ERR_PTR(err); | |
| } | |
| static void virtio_sandbox_del_vq(struct virtqueue *vq) | |
| { | |
| vring_del_virtqueue(vq); | |
| } | |
| static int virtio_sandbox_del_vqs(struct udevice *udev) | |
| { | |
| struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | |
| struct virtqueue *vq, *n; | |
| list_for_each_entry_safe(vq, n, &uc_priv->vqs, list) | |
| virtio_sandbox_del_vq(vq); | |
| return 0; | |
| } | |
| static int virtio_sandbox_find_vqs(struct udevice *udev, unsigned int nvqs, | |
| struct virtqueue *vqs[]) | |
| { | |
| int i; | |
| for (i = 0; i < nvqs; ++i) { | |
| vqs[i] = virtio_sandbox_setup_vq(udev, i); | |
| if (IS_ERR(vqs[i])) { | |
| virtio_sandbox_del_vqs(udev); | |
| return PTR_ERR(vqs[i]); | |
| } | |
| } | |
| return 0; | |
| } | |
| static int virtio_sandbox_notify(struct udevice *udev, struct virtqueue *vq) | |
| { | |
| return 0; | |
| } | |
| static int virtio_sandbox_probe(struct udevice *udev) | |
| { | |
| struct virtio_sandbox_priv *priv = dev_get_priv(udev); | |
| struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); | |
| /* fake some information for testing */ | |
| priv->device_features = BIT_ULL(VIRTIO_F_VERSION_1); | |
| uc_priv->device = dev_read_u32_default(udev, "virtio-type", | |
| VIRTIO_ID_RNG); | |
| uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't'; | |
| return 0; | |
| } | |
| static const struct dm_virtio_ops virtio_sandbox1_ops = { | |
| .get_config = virtio_sandbox_get_config, | |
| .set_config = virtio_sandbox_set_config, | |
| .get_status = virtio_sandbox_get_status, | |
| .set_status = virtio_sandbox_set_status, | |
| .reset = virtio_sandbox_reset, | |
| .get_features = virtio_sandbox_get_features, | |
| .set_features = virtio_sandbox_set_features, | |
| .find_vqs = virtio_sandbox_find_vqs, | |
| .del_vqs = virtio_sandbox_del_vqs, | |
| .notify = virtio_sandbox_notify, | |
| }; | |
| static const struct udevice_id virtio_sandbox1_ids[] = { | |
| { .compatible = "sandbox,virtio1" }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(virtio_sandbox1) = { | |
| .name = "virtio-sandbox1", | |
| .id = UCLASS_VIRTIO, | |
| .of_match = virtio_sandbox1_ids, | |
| .ops = &virtio_sandbox1_ops, | |
| .probe = virtio_sandbox_probe, | |
| .priv_auto = sizeof(struct virtio_sandbox_priv), | |
| }; | |
| /* this one without notify op */ | |
| static const struct dm_virtio_ops virtio_sandbox2_ops = { | |
| .get_config = virtio_sandbox_get_config, | |
| .set_config = virtio_sandbox_set_config, | |
| .get_status = virtio_sandbox_get_status, | |
| .set_status = virtio_sandbox_set_status, | |
| .reset = virtio_sandbox_reset, | |
| .get_features = virtio_sandbox_get_features, | |
| .set_features = virtio_sandbox_set_features, | |
| .find_vqs = virtio_sandbox_find_vqs, | |
| .del_vqs = virtio_sandbox_del_vqs, | |
| }; | |
| static const struct udevice_id virtio_sandbox2_ids[] = { | |
| { .compatible = "sandbox,virtio2" }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(virtio_sandbox2) = { | |
| .name = "virtio-sandbox2", | |
| .id = UCLASS_VIRTIO, | |
| .of_match = virtio_sandbox2_ids, | |
| .ops = &virtio_sandbox2_ops, | |
| .probe = virtio_sandbox_probe, | |
| .priv_auto = sizeof(struct virtio_sandbox_priv), | |
| }; |