1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
4  *
5  * VirtIO Sandbox transport driver, for testing purpose only
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <virtio_types.h>
11 #include <virtio.h>
12 #include <virtio_ring.h>
13 #include <linux/compat.h>
14 #include <linux/io.h>
15 
16 struct virtio_sandbox_priv {
17 	u8 id;
18 	u8 status;
19 	u64 device_features;
20 	u64 driver_features;
21 	ulong queue_desc;
22 	ulong queue_available;
23 	ulong queue_used;
24 };
25 
26 static int virtio_sandbox_get_config(struct udevice *udev, unsigned int offset,
27 				     void *buf, unsigned int len)
28 {
29 	return 0;
30 }
31 
32 static int virtio_sandbox_set_config(struct udevice *udev, unsigned int offset,
33 				     const void *buf, unsigned int len)
34 {
35 	return 0;
36 }
37 
38 static int virtio_sandbox_get_status(struct udevice *udev, u8 *status)
39 {
40 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
41 
42 	*status = priv->status;
43 
44 	return 0;
45 }
46 
47 static int virtio_sandbox_set_status(struct udevice *udev, u8 status)
48 {
49 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
50 
51 	/* We should never be setting status to 0 */
52 	WARN_ON(status == 0);
53 
54 	priv->status = status;
55 
56 	return 0;
57 }
58 
59 static int virtio_sandbox_reset(struct udevice *udev)
60 {
61 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
62 
63 	/* 0 status means a reset */
64 	priv->status = 0;
65 
66 	return 0;
67 }
68 
69 static int virtio_sandbox_get_features(struct udevice *udev, u64 *features)
70 {
71 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
72 
73 	*features = priv->device_features;
74 
75 	return 0;
76 }
77 
78 static int virtio_sandbox_set_features(struct udevice *udev)
79 {
80 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
81 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
82 
83 	priv->driver_features = uc_priv->features;
84 
85 	return 0;
86 }
87 
88 static struct virtqueue *virtio_sandbox_setup_vq(struct udevice *udev,
89 						 unsigned int index)
90 {
91 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
92 	struct virtqueue *vq;
93 	ulong addr;
94 	int err;
95 
96 	/* Create the vring */
97 	vq = vring_create_virtqueue(index, 4, 4096, udev);
98 	if (!vq) {
99 		err = -ENOMEM;
100 		goto error_new_virtqueue;
101 	}
102 
103 	addr = virtqueue_get_desc_addr(vq);
104 	priv->queue_desc = addr;
105 
106 	addr = virtqueue_get_avail_addr(vq);
107 	priv->queue_available = addr;
108 
109 	addr = virtqueue_get_used_addr(vq);
110 	priv->queue_used = addr;
111 
112 	return vq;
113 
114 error_new_virtqueue:
115 	return ERR_PTR(err);
116 }
117 
118 static void virtio_sandbox_del_vq(struct virtqueue *vq)
119 {
120 	vring_del_virtqueue(vq);
121 }
122 
123 static int virtio_sandbox_del_vqs(struct udevice *udev)
124 {
125 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
126 	struct virtqueue *vq, *n;
127 
128 	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
129 		virtio_sandbox_del_vq(vq);
130 
131 	return 0;
132 }
133 
134 static int virtio_sandbox_find_vqs(struct udevice *udev, unsigned int nvqs,
135 				   struct virtqueue *vqs[])
136 {
137 	int i;
138 
139 	for (i = 0; i < nvqs; ++i) {
140 		vqs[i] = virtio_sandbox_setup_vq(udev, i);
141 		if (IS_ERR(vqs[i])) {
142 			virtio_sandbox_del_vqs(udev);
143 			return PTR_ERR(vqs[i]);
144 		}
145 	}
146 
147 	return 0;
148 }
149 
150 static int virtio_sandbox_notify(struct udevice *udev, struct virtqueue *vq)
151 {
152 	return 0;
153 }
154 
155 static int virtio_sandbox_probe(struct udevice *udev)
156 {
157 	struct virtio_sandbox_priv *priv = dev_get_priv(udev);
158 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
159 
160 	/* fake some information for testing */
161 	priv->device_features = VIRTIO_F_VERSION_1;
162 	uc_priv->device = VIRTIO_ID_BLOCK;
163 	uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't';
164 
165 	return 0;
166 }
167 
168 /* check virtio device driver's remove routine was called to reset the device */
169 static int virtio_sandbox_child_post_remove(struct udevice *vdev)
170 {
171 	u8 status;
172 
173 	virtio_get_status(vdev, &status);
174 	if (status)
175 		panic("virtio device was not reset\n");
176 
177 	return 0;
178 }
179 
180 static const struct dm_virtio_ops virtio_sandbox1_ops = {
181 	.get_config	= virtio_sandbox_get_config,
182 	.set_config	= virtio_sandbox_set_config,
183 	.get_status	= virtio_sandbox_get_status,
184 	.set_status	= virtio_sandbox_set_status,
185 	.reset		= virtio_sandbox_reset,
186 	.get_features	= virtio_sandbox_get_features,
187 	.set_features	= virtio_sandbox_set_features,
188 	.find_vqs	= virtio_sandbox_find_vqs,
189 	.del_vqs	= virtio_sandbox_del_vqs,
190 	.notify		= virtio_sandbox_notify,
191 };
192 
193 static const struct udevice_id virtio_sandbox1_ids[] = {
194 	{ .compatible = "sandbox,virtio1" },
195 	{ }
196 };
197 
198 U_BOOT_DRIVER(virtio_sandbox1) = {
199 	.name	= "virtio-sandbox1",
200 	.id	= UCLASS_VIRTIO,
201 	.of_match = virtio_sandbox1_ids,
202 	.ops	= &virtio_sandbox1_ops,
203 	.probe	= virtio_sandbox_probe,
204 	.child_post_remove = virtio_sandbox_child_post_remove,
205 	.priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
206 };
207 
208 /* this one without notify op */
209 static const struct dm_virtio_ops virtio_sandbox2_ops = {
210 	.get_config	= virtio_sandbox_get_config,
211 	.set_config	= virtio_sandbox_set_config,
212 	.get_status	= virtio_sandbox_get_status,
213 	.set_status	= virtio_sandbox_set_status,
214 	.reset		= virtio_sandbox_reset,
215 	.get_features	= virtio_sandbox_get_features,
216 	.set_features	= virtio_sandbox_set_features,
217 	.find_vqs	= virtio_sandbox_find_vqs,
218 	.del_vqs	= virtio_sandbox_del_vqs,
219 };
220 
221 static const struct udevice_id virtio_sandbox2_ids[] = {
222 	{ .compatible = "sandbox,virtio2" },
223 	{ }
224 };
225 
226 U_BOOT_DRIVER(virtio_sandbox2) = {
227 	.name	= "virtio-sandbox2",
228 	.id	= UCLASS_VIRTIO,
229 	.of_match = virtio_sandbox2_ids,
230 	.ops	= &virtio_sandbox2_ops,
231 	.probe	= virtio_sandbox_probe,
232 	.priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
233 };
234