1 /*
2 * Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ipc/ipc_static_vrings.h>
8 #include <zephyr/cache.h>
9
10 #define RPMSG_VQ_0 (0) /* TX virtqueue queue index */
11 #define RPMSG_VQ_1 (1) /* RX virtqueue queue index */
12
ipc_virtio_notify(struct virtqueue * vq)13 static void ipc_virtio_notify(struct virtqueue *vq)
14 {
15 struct ipc_static_vrings *vr;
16
17 vr = CONTAINER_OF(vq->vq_dev, struct ipc_static_vrings, vdev);
18
19 if (vr->notify_cb) {
20 vr->notify_cb(vq, vr->priv);
21 }
22 }
23
ipc_virtio_set_features(struct virtio_device * vdev,uint32_t features)24 static void ipc_virtio_set_features(struct virtio_device *vdev, uint32_t features)
25 {
26 /* No need for implementation */
27 }
28
ipc_virtio_set_status(struct virtio_device * p_vdev,unsigned char status)29 static void ipc_virtio_set_status(struct virtio_device *p_vdev, unsigned char status)
30 {
31 struct ipc_static_vrings *vr;
32
33 if (p_vdev->role != VIRTIO_DEV_DRIVER) {
34 return;
35 }
36
37 vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
38
39 sys_write8(status, vr->status_reg_addr);
40 sys_cache_data_flush_range((void *) vr->status_reg_addr, sizeof(status));
41 }
42
ipc_virtio_get_features(struct virtio_device * vdev)43 static uint32_t ipc_virtio_get_features(struct virtio_device *vdev)
44 {
45 return BIT(VIRTIO_RPMSG_F_NS);
46 }
47
ipc_virtio_get_status(struct virtio_device * p_vdev)48 static unsigned char ipc_virtio_get_status(struct virtio_device *p_vdev)
49 {
50 struct ipc_static_vrings *vr;
51 uint8_t ret;
52
53 vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
54
55 ret = VIRTIO_CONFIG_STATUS_DRIVER_OK;
56
57 if (p_vdev->role == VIRTIO_DEV_DEVICE) {
58 sys_cache_data_invd_range((void *) vr->status_reg_addr, sizeof(ret));
59 ret = sys_read8(vr->status_reg_addr);
60 }
61
62 return ret;
63 }
64
65 const static struct virtio_dispatch dispatch = {
66 .get_status = ipc_virtio_get_status,
67 .get_features = ipc_virtio_get_features,
68 .set_status = ipc_virtio_set_status,
69 .set_features = ipc_virtio_set_features,
70 .notify = ipc_virtio_notify,
71 };
72
vq_setup(struct ipc_static_vrings * vr,unsigned int role)73 static int vq_setup(struct ipc_static_vrings *vr, unsigned int role)
74 {
75 vr->vq[RPMSG_VQ_0] = virtqueue_allocate(vr->vring_size);
76 if (vr->vq[RPMSG_VQ_0] == NULL) {
77 return -ENOMEM;
78 }
79
80 vr->vq[RPMSG_VQ_1] = virtqueue_allocate(vr->vring_size);
81 if (vr->vq[RPMSG_VQ_1] == NULL) {
82 return -ENOMEM;
83 }
84
85 vr->rvrings[RPMSG_VQ_0].io = &vr->shm_io;
86 vr->rvrings[RPMSG_VQ_0].info.vaddr = (void *) vr->tx_addr;
87 vr->rvrings[RPMSG_VQ_0].info.num_descs = vr->vring_size;
88 vr->rvrings[RPMSG_VQ_0].info.align = MEM_ALIGNMENT;
89 vr->rvrings[RPMSG_VQ_0].vq = vr->vq[RPMSG_VQ_0];
90
91 vr->rvrings[RPMSG_VQ_1].io = &vr->shm_io;
92 vr->rvrings[RPMSG_VQ_1].info.vaddr = (void *) vr->rx_addr;
93 vr->rvrings[RPMSG_VQ_1].info.num_descs = vr->vring_size;
94 vr->rvrings[RPMSG_VQ_1].info.align = MEM_ALIGNMENT;
95 vr->rvrings[RPMSG_VQ_1].vq = vr->vq[RPMSG_VQ_1];
96
97 vr->vdev.role = role;
98
99 vr->vdev.vrings_num = VRING_COUNT;
100 vr->vdev.func = &dispatch;
101 vr->vdev.vrings_info = &vr->rvrings[0];
102
103 return 0;
104 }
105
vq_teardown(struct ipc_static_vrings * vr,unsigned int role)106 static int vq_teardown(struct ipc_static_vrings *vr, unsigned int role)
107 {
108 memset(&vr->vdev, 0, sizeof(struct virtio_device));
109
110 memset(&(vr->rvrings[RPMSG_VQ_1]), 0, sizeof(struct virtio_vring_info));
111 memset(&(vr->rvrings[RPMSG_VQ_0]), 0, sizeof(struct virtio_vring_info));
112
113 virtqueue_free(vr->vq[RPMSG_VQ_1]);
114 virtqueue_free(vr->vq[RPMSG_VQ_0]);
115
116 return 0;
117 }
118
ipc_static_vrings_init(struct ipc_static_vrings * vr,unsigned int role)119 int ipc_static_vrings_init(struct ipc_static_vrings *vr, unsigned int role)
120 {
121 struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
122 int err = 0;
123
124 if (!vr) {
125 return -EINVAL;
126 }
127
128 err = metal_init(&metal_params);
129 if (err != 0) {
130 return err;
131 }
132
133 vr->shm_physmap[0] = vr->shm_addr;
134
135 metal_io_init(&vr->shm_io, (void *)vr->shm_addr,
136 vr->shm_physmap, vr->shm_size, -1, 0, NULL);
137
138 return vq_setup(vr, role);
139 }
140
ipc_static_vrings_deinit(struct ipc_static_vrings * vr,unsigned int role)141 int ipc_static_vrings_deinit(struct ipc_static_vrings *vr, unsigned int role)
142 {
143 int err;
144
145 err = vq_teardown(vr, role);
146 if (err != 0) {
147 return err;
148 }
149
150 metal_io_finish(&vr->shm_io);
151
152 metal_finish();
153
154 return 0;
155 }
156