1 /*
2  * Remoteproc Virtio Framework Implementation
3  *
4  * Copyright(c) 2018 Xilinx Ltd.
5  * Copyright(c) 2011 Texas Instruments, Inc.
6  * Copyright(c) 2011 Google, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  *   notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above copyright
16  *   notice, this list of conditions and the following disclaimer in
17  *   the documentation and/or other materials provided with the
18  *   distribution.
19  * * Neither the name Texas Instruments nor the names of its
20  *   contributors may be used to endorse or promote products derived
21  *   from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <openamp/remoteproc.h>
37 #include <openamp/remoteproc_virtio.h>
38 #include <openamp/virtqueue.h>
39 #include <metal/utilities.h>
40 #include <metal/alloc.h>
41 
rproc_virtio_virtqueue_notify(struct virtqueue * vq)42 static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
43 {
44 	struct remoteproc_virtio *rpvdev;
45 	struct virtio_vring_info *vring_info;
46 	struct virtio_device *vdev;
47 	unsigned int vq_id = vq->vq_queue_index;
48 
49 	vdev = vq->vq_dev;
50 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
51 	metal_assert(vq_id <= vdev->vrings_num);
52 	vring_info = &vdev->vrings_info[vq_id];
53 	rpvdev->notify(rpvdev->priv, vring_info->notifyid);
54 }
55 
rproc_virtio_get_status(struct virtio_device * vdev)56 static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
57 {
58 	struct remoteproc_virtio *rpvdev;
59 	struct fw_rsc_vdev *vdev_rsc;
60 	struct metal_io_region *io;
61 	char status;
62 
63 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
64 	vdev_rsc = rpvdev->vdev_rsc;
65 	io = rpvdev->vdev_rsc_io;
66 	status = metal_io_read8(io,
67 				metal_io_virt_to_offset(io, &vdev_rsc->status));
68 	return status;
69 }
70 
71 #ifndef VIRTIO_SLAVE_ONLY
rproc_virtio_set_status(struct virtio_device * vdev,unsigned char status)72 static void rproc_virtio_set_status(struct virtio_device *vdev,
73 				    unsigned char status)
74 {
75 	struct remoteproc_virtio *rpvdev;
76 	struct fw_rsc_vdev *vdev_rsc;
77 	struct metal_io_region *io;
78 
79 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
80 	vdev_rsc = rpvdev->vdev_rsc;
81 	io = rpvdev->vdev_rsc_io;
82 	metal_io_write8(io,
83 			metal_io_virt_to_offset(io, &vdev_rsc->status),
84 			status);
85 	rpvdev->notify(rpvdev->priv, vdev->index);
86 }
87 #endif
88 
rproc_virtio_get_features(struct virtio_device * vdev)89 static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
90 {
91 	struct remoteproc_virtio *rpvdev;
92 	struct fw_rsc_vdev *vdev_rsc;
93 	struct metal_io_region *io;
94 	uint32_t features;
95 
96 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
97 	vdev_rsc = rpvdev->vdev_rsc;
98 	io = rpvdev->vdev_rsc_io;
99 	/* TODO: shall we get features based on the role ? */
100 	features = metal_io_read32(io,
101 			metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
102 
103 	return features;
104 }
105 
106 #ifndef VIRTIO_SLAVE_ONLY
rproc_virtio_set_features(struct virtio_device * vdev,uint32_t features)107 static void rproc_virtio_set_features(struct virtio_device *vdev,
108 				      uint32_t features)
109 {
110 	struct remoteproc_virtio *rpvdev;
111 	struct fw_rsc_vdev *vdev_rsc;
112 	struct metal_io_region *io;
113 
114 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
115 	vdev_rsc = rpvdev->vdev_rsc;
116 	io = rpvdev->vdev_rsc_io;
117 	/* TODO: shall we set features based on the role ? */
118 	metal_io_write32(io,
119 			 metal_io_virt_to_offset(io, &vdev_rsc->dfeatures),
120 			 features);
121 	rpvdev->notify(rpvdev->priv, vdev->index);
122 }
123 #endif
124 
rproc_virtio_negotiate_features(struct virtio_device * vdev,uint32_t features)125 static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
126 						uint32_t features)
127 {
128 	(void)vdev;
129 	(void)features;
130 
131 	return 0;
132 }
133 
rproc_virtio_read_config(struct virtio_device * vdev,uint32_t offset,void * dst,int length)134 static void rproc_virtio_read_config(struct virtio_device *vdev,
135 				     uint32_t offset, void *dst, int length)
136 {
137 	(void)vdev;
138 	(void)offset;
139 	(void)dst;
140 	(void)length;
141 }
142 
143 #ifndef VIRTIO_SLAVE_ONLY
rproc_virtio_write_config(struct virtio_device * vdev,uint32_t offset,void * src,int length)144 static void rproc_virtio_write_config(struct virtio_device *vdev,
145 				      uint32_t offset, void *src, int length)
146 {
147 	(void)vdev;
148 	(void)offset;
149 	(void)src;
150 	(void)length;
151 }
152 
rproc_virtio_reset_device(struct virtio_device * vdev)153 static void rproc_virtio_reset_device(struct virtio_device *vdev)
154 {
155 	if (vdev->role == VIRTIO_DEV_MASTER)
156 		rproc_virtio_set_status(vdev,
157 					VIRTIO_CONFIG_STATUS_NEEDS_RESET);
158 }
159 #endif
160 
161 const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
162 	.get_status =  rproc_virtio_get_status,
163 	.get_features = rproc_virtio_get_features,
164 	.read_config = rproc_virtio_read_config,
165 	.notify = rproc_virtio_virtqueue_notify,
166 	.negotiate_features = rproc_virtio_negotiate_features,
167 #ifndef VIRTIO_SLAVE_ONLY
168 	/*
169 	 * We suppose here that the vdev is in a shared memory so that can
170 	 * be access only by one core: the master. In this case salve core has
171 	 * only read access right.
172 	 */
173 	.set_status = rproc_virtio_set_status,
174 	.set_features = rproc_virtio_set_features,
175 	.write_config = rproc_virtio_write_config,
176 	.reset_device = rproc_virtio_reset_device,
177 #endif
178 };
179 
180 struct virtio_device *
rproc_virtio_create_vdev(unsigned int role,unsigned int notifyid,void * rsc,struct metal_io_region * rsc_io,void * priv,rpvdev_notify_func notify,virtio_dev_reset_cb rst_cb)181 rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
182 			 void *rsc, struct metal_io_region *rsc_io,
183 			 void *priv,
184 			 rpvdev_notify_func notify,
185 			 virtio_dev_reset_cb rst_cb)
186 {
187 	struct remoteproc_virtio *rpvdev;
188 	struct virtio_vring_info *vrings_info;
189 	struct fw_rsc_vdev *vdev_rsc = rsc;
190 	struct virtio_device *vdev;
191 	unsigned int num_vrings = vdev_rsc->num_of_vrings;
192 	unsigned int i;
193 
194 	rpvdev = metal_allocate_memory(sizeof(*rpvdev));
195 	if (!rpvdev)
196 		return NULL;
197 	vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
198 	if (!vrings_info)
199 		goto err0;
200 	memset(rpvdev, 0, sizeof(*rpvdev));
201 	memset(vrings_info, 0, sizeof(*vrings_info));
202 	vdev = &rpvdev->vdev;
203 
204 	for (i = 0; i < num_vrings; i++) {
205 		struct virtqueue *vq;
206 		struct fw_rsc_vdev_vring *vring_rsc;
207 		unsigned int num_extra_desc = 0;
208 
209 		vring_rsc = &vdev_rsc->vring[i];
210 		if (role == VIRTIO_DEV_MASTER) {
211 			num_extra_desc = vring_rsc->num;
212 		}
213 		vq = virtqueue_allocate(num_extra_desc);
214 		if (!vq)
215 			goto err1;
216 		vrings_info[i].vq = vq;
217 	}
218 
219 	/* FIXME commended as seems not nedded, already stored in vdev */
220 	//rpvdev->notifyid = notifyid;
221 	rpvdev->notify = notify;
222 	rpvdev->priv = priv;
223 	vdev->vrings_info = vrings_info;
224 	/* Assuming the shared memory has been mapped and registered if
225 	 * necessary
226 	 */
227 	rpvdev->vdev_rsc = vdev_rsc;
228 	rpvdev->vdev_rsc_io = rsc_io;
229 
230 	vdev->index = notifyid;
231 	vdev->role = role;
232 	vdev->reset_cb = rst_cb;
233 	vdev->vrings_num = num_vrings;
234 	vdev->func = &remoteproc_virtio_dispatch_funcs;
235 	/* TODO: Shall we set features here ? */
236 
237 	return &rpvdev->vdev;
238 
239 err1:
240 	for (i = 0; i < num_vrings; i++) {
241 		if (vrings_info[i].vq)
242 			metal_free_memory(vrings_info[i].vq);
243 	}
244 	metal_free_memory(vrings_info);
245 err0:
246 	metal_free_memory(rpvdev);
247 	return NULL;
248 }
249 
rproc_virtio_remove_vdev(struct virtio_device * vdev)250 void rproc_virtio_remove_vdev(struct virtio_device *vdev)
251 {
252 	struct remoteproc_virtio *rpvdev;
253 	unsigned int i;
254 
255 	if (!vdev)
256 		return;
257 	rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
258 	for (i = 0; i < vdev->vrings_num; i++) {
259 		struct virtqueue *vq;
260 
261 		vq = vdev->vrings_info[i].vq;
262 		if (vq)
263 			metal_free_memory(vq);
264 	}
265 	metal_free_memory(vdev->vrings_info);
266 	metal_free_memory(rpvdev);
267 }
268 
rproc_virtio_init_vring(struct virtio_device * vdev,unsigned int index,unsigned int notifyid,void * va,struct metal_io_region * io,unsigned int num_descs,unsigned int align)269 int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
270 			    unsigned int notifyid, void *va,
271 			    struct metal_io_region *io,
272 			    unsigned int num_descs, unsigned int align)
273 {
274 	struct virtio_vring_info *vring_info;
275 	unsigned int num_vrings;
276 
277 	num_vrings = vdev->vrings_num;
278 	if (index >= num_vrings)
279 		return -RPROC_EINVAL;
280 	vring_info = &vdev->vrings_info[index];
281 	vring_info->io = io;
282 	vring_info->notifyid = notifyid;
283 	vring_info->info.vaddr = va;
284 	vring_info->info.num_descs = num_descs;
285 	vring_info->info.align = align;
286 
287 	return 0;
288 }
289 
rproc_virtio_notified(struct virtio_device * vdev,uint32_t notifyid)290 int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
291 {
292 	unsigned int num_vrings, i;
293 	struct virtio_vring_info *vring_info;
294 	struct virtqueue *vq;
295 
296 	if (!vdev)
297 		return -EINVAL;
298 	/* We do nothing for vdev notification in this implementation */
299 	if (vdev->index == notifyid)
300 		return 0;
301 	num_vrings = vdev->vrings_num;
302 	for (i = 0; i < num_vrings; i++) {
303 		vring_info = &vdev->vrings_info[i];
304 		if (vring_info->notifyid == notifyid ||
305 		    notifyid == RSC_NOTIFY_ID_ANY) {
306 			vq = vring_info->vq;
307 			virtqueue_notification(vq);
308 		}
309 	}
310 	return 0;
311 }
312 
rproc_virtio_wait_remote_ready(struct virtio_device * vdev)313 void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
314 {
315 	uint8_t status;
316 
317 	/*
318 	 * No status available for slave. As Master has not to wait
319 	 * slave action, we can return. Behavior should be updated
320 	 * in future if a slave status is added.
321 	 */
322 	if (vdev->role == VIRTIO_DEV_MASTER)
323 		return;
324 
325 	while (1) {
326 		status = rproc_virtio_get_status(vdev);
327 		if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
328 			return;
329 	}
330 }
331