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