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 SHM_DEVICE_NAME		"sram0.shm"
11 
12 #define RPMSG_VQ_0		(0) /* TX virtqueue queue index */
13 #define RPMSG_VQ_1		(1) /* RX virtqueue queue index */
14 
ipc_virtio_notify(struct virtqueue * vq)15 static void ipc_virtio_notify(struct virtqueue *vq)
16 {
17 	struct ipc_static_vrings *vr;
18 
19 	vr = CONTAINER_OF(vq->vq_dev, struct ipc_static_vrings, vdev);
20 
21 	if (vr->notify_cb) {
22 		vr->notify_cb(vq, vr->priv);
23 	}
24 }
25 
ipc_virtio_set_features(struct virtio_device * vdev,uint32_t features)26 static void ipc_virtio_set_features(struct virtio_device *vdev, uint32_t features)
27 {
28 	/* No need for implementation */
29 }
30 
ipc_virtio_set_status(struct virtio_device * p_vdev,unsigned char status)31 static void ipc_virtio_set_status(struct virtio_device *p_vdev, unsigned char status)
32 {
33 	struct ipc_static_vrings *vr;
34 
35 	if (p_vdev->role != VIRTIO_DEV_DRIVER) {
36 		return;
37 	}
38 
39 	vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
40 
41 	sys_write8(status, vr->status_reg_addr);
42 	sys_cache_data_flush_range((void *) vr->status_reg_addr, sizeof(status));
43 }
44 
ipc_virtio_get_features(struct virtio_device * vdev)45 static uint32_t ipc_virtio_get_features(struct virtio_device *vdev)
46 {
47 	return BIT(VIRTIO_RPMSG_F_NS);
48 }
49 
ipc_virtio_get_status(struct virtio_device * p_vdev)50 static unsigned char ipc_virtio_get_status(struct virtio_device *p_vdev)
51 {
52 	struct ipc_static_vrings *vr;
53 	uint8_t ret;
54 
55 	vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
56 
57 	ret = VIRTIO_CONFIG_STATUS_DRIVER_OK;
58 
59 	if (p_vdev->role == VIRTIO_DEV_DEVICE) {
60 		sys_cache_data_invd_range((void *) vr->status_reg_addr, sizeof(ret));
61 		ret = sys_read8(vr->status_reg_addr);
62 	}
63 
64 	return ret;
65 }
66 
67 const static struct virtio_dispatch dispatch = {
68 	.get_status = ipc_virtio_get_status,
69 	.get_features = ipc_virtio_get_features,
70 	.set_status = ipc_virtio_set_status,
71 	.set_features = ipc_virtio_set_features,
72 	.notify = ipc_virtio_notify,
73 };
74 
libmetal_setup(struct ipc_static_vrings * vr)75 static int libmetal_setup(struct ipc_static_vrings *vr)
76 {
77 	struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
78 	struct metal_device *device;
79 	int err;
80 
81 	err = metal_init(&metal_params);
82 	if (err != 0) {
83 		return err;
84 	}
85 
86 	err = metal_register_generic_device(&vr->shm_device);
87 	if (err != 0) {
88 		return err;
89 	}
90 
91 	err = metal_device_open("generic", SHM_DEVICE_NAME, &device);
92 	if (err != 0) {
93 		return err;
94 	}
95 
96 	vr->shm_io = metal_device_io_region(device, 0);
97 	if (vr->shm_io == NULL) {
98 		return err;
99 	}
100 
101 	return 0;
102 }
103 
libmetal_teardown(struct ipc_static_vrings * vr)104 static int libmetal_teardown(struct ipc_static_vrings *vr)
105 {
106 	vr->shm_io = 0;
107 
108 	metal_device_close(&vr->shm_device);
109 
110 	metal_finish();
111 
112 	return 0;
113 }
114 
vq_setup(struct ipc_static_vrings * vr,unsigned int role)115 static int vq_setup(struct ipc_static_vrings *vr, unsigned int role)
116 {
117 	vr->vq[RPMSG_VQ_0] = virtqueue_allocate(vr->vring_size);
118 	if (vr->vq[RPMSG_VQ_0] == NULL) {
119 		return -ENOMEM;
120 	}
121 
122 	vr->vq[RPMSG_VQ_1] = virtqueue_allocate(vr->vring_size);
123 	if (vr->vq[RPMSG_VQ_1] == NULL) {
124 		return -ENOMEM;
125 	}
126 
127 	vr->rvrings[RPMSG_VQ_0].io = vr->shm_io;
128 	vr->rvrings[RPMSG_VQ_0].info.vaddr = (void *) vr->tx_addr;
129 	vr->rvrings[RPMSG_VQ_0].info.num_descs = vr->vring_size;
130 	vr->rvrings[RPMSG_VQ_0].info.align = MEM_ALIGNMENT;
131 	vr->rvrings[RPMSG_VQ_0].vq = vr->vq[RPMSG_VQ_0];
132 
133 	vr->rvrings[RPMSG_VQ_1].io = vr->shm_io;
134 	vr->rvrings[RPMSG_VQ_1].info.vaddr = (void *) vr->rx_addr;
135 	vr->rvrings[RPMSG_VQ_1].info.num_descs = vr->vring_size;
136 	vr->rvrings[RPMSG_VQ_1].info.align = MEM_ALIGNMENT;
137 	vr->rvrings[RPMSG_VQ_1].vq = vr->vq[RPMSG_VQ_1];
138 
139 	vr->vdev.role = role;
140 
141 	vr->vdev.vrings_num = VRING_COUNT;
142 	vr->vdev.func = &dispatch;
143 	vr->vdev.vrings_info = &vr->rvrings[0];
144 
145 	return 0;
146 }
147 
vq_teardown(struct ipc_static_vrings * vr,unsigned int role)148 static int vq_teardown(struct ipc_static_vrings *vr, unsigned int role)
149 {
150 	memset(&vr->vdev, 0, sizeof(struct virtio_device));
151 
152 	memset(&(vr->rvrings[RPMSG_VQ_1]), 0, sizeof(struct virtio_vring_info));
153 	memset(&(vr->rvrings[RPMSG_VQ_0]), 0, sizeof(struct virtio_vring_info));
154 
155 	virtqueue_free(vr->vq[RPMSG_VQ_1]);
156 	virtqueue_free(vr->vq[RPMSG_VQ_0]);
157 
158 	return 0;
159 }
160 
ipc_static_vrings_init(struct ipc_static_vrings * vr,unsigned int role)161 int ipc_static_vrings_init(struct ipc_static_vrings *vr, unsigned int role)
162 {
163 	int err = 0;
164 
165 	if (!vr) {
166 		return -EINVAL;
167 	}
168 
169 	vr->shm_device.name = SHM_DEVICE_NAME;
170 	vr->shm_device.num_regions = 1;
171 	vr->shm_physmap[0] = vr->shm_addr;
172 
173 	metal_io_init(vr->shm_device.regions, (void *) vr->shm_addr,
174 		      vr->shm_physmap, vr->shm_size, -1, 0, NULL);
175 
176 	err = libmetal_setup(vr);
177 	if (err != 0) {
178 		return err;
179 	}
180 
181 	return vq_setup(vr, role);
182 }
183 
ipc_static_vrings_deinit(struct ipc_static_vrings * vr,unsigned int role)184 int ipc_static_vrings_deinit(struct ipc_static_vrings *vr, unsigned int role)
185 {
186 	int err;
187 
188 	err = vq_teardown(vr, role);
189 	if (err != 0) {
190 		return err;
191 	}
192 
193 	err = libmetal_teardown(vr);
194 	if (err != 0) {
195 		return err;
196 	}
197 
198 	metal_io_finish(vr->shm_device.regions);
199 
200 	return 0;
201 }
202