1 /*
2  * Copyright (c) 2018, NXP
3  * Copyright (c) 2018, Nordic Semiconductor ASA
4  * Copyright (c) 2018-2019, Linaro Limited
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr.h>
10 #include <drivers/ipm.h>
11 #include <sys/printk.h>
12 #include <device.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <init.h>
17 
18 #include <openamp/open_amp.h>
19 #include <metal/device.h>
20 
21 #include "common.h"
22 
23 #define APP_TASK_STACK_SIZE (1024)
24 K_THREAD_STACK_DEFINE(thread_stack, APP_TASK_STACK_SIZE);
25 static struct k_thread thread_data;
26 
27 static const struct device *ipm_handle;
28 
29 static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR };
30 static struct metal_device shm_device = {
31 	.name = SHM_DEVICE_NAME,
32 	.bus = NULL,
33 	.num_regions = 1,
34 	{
35 		{
36 			.virt       = (void *) SHM_START_ADDR,
37 			.physmap    = shm_physmap,
38 			.size       = SHM_SIZE,
39 			.page_shift = 0xffffffff,
40 			.page_mask  = 0xffffffff,
41 			.mem_flags  = 0,
42 			.ops        = { NULL },
43 		},
44 	},
45 	.node = { NULL },
46 	.irq_num = 0,
47 	.irq_info = NULL
48 };
49 
50 static volatile unsigned int received_data;
51 
52 static struct virtio_vring_info rvrings[2] = {
53 	[0] = {
54 		.info.align = VRING_ALIGNMENT,
55 	},
56 	[1] = {
57 		.info.align = VRING_ALIGNMENT,
58 	},
59 };
60 static struct virtio_device vdev;
61 static struct rpmsg_virtio_device rvdev;
62 static struct metal_io_region *io;
63 static struct virtqueue *vq[2];
64 
virtio_get_status(struct virtio_device * vdev)65 static unsigned char virtio_get_status(struct virtio_device *vdev)
66 {
67 	return VIRTIO_CONFIG_STATUS_DRIVER_OK;
68 }
69 
virtio_set_status(struct virtio_device * vdev,unsigned char status)70 static void virtio_set_status(struct virtio_device *vdev, unsigned char status)
71 {
72 	sys_write8(status, VDEV_STATUS_ADDR);
73 }
74 
virtio_get_features(struct virtio_device * vdev)75 static uint32_t virtio_get_features(struct virtio_device *vdev)
76 {
77 	return 1 << VIRTIO_RPMSG_F_NS;
78 }
79 
virtio_set_features(struct virtio_device * vdev,uint32_t features)80 static void virtio_set_features(struct virtio_device *vdev,
81 				uint32_t features)
82 {
83 }
84 
virtio_notify(struct virtqueue * vq)85 static void virtio_notify(struct virtqueue *vq)
86 {
87 #if defined(CONFIG_SOC_MPS2_AN521) || \
88 	defined(CONFIG_SOC_V2M_MUSCA_B1)
89 	uint32_t current_core = sse_200_platform_get_cpu_id();
90 
91 	ipm_send(ipm_handle, 0, current_core ? 0 : 1, 0, 1);
92 #else
93 	uint32_t dummy_data = 0x55005500; /* Some data must be provided */
94 
95 	ipm_send(ipm_handle, 0, 0, &dummy_data, sizeof(dummy_data));
96 #endif /* #if defined(CONFIG_SOC_MPS2_AN521) */
97 }
98 
99 struct virtio_dispatch dispatch = {
100 	.get_status = virtio_get_status,
101 	.set_status = virtio_set_status,
102 	.get_features = virtio_get_features,
103 	.set_features = virtio_set_features,
104 	.notify = virtio_notify,
105 };
106 
107 static K_SEM_DEFINE(data_sem, 0, 1);
108 static K_SEM_DEFINE(data_rx_sem, 0, 1);
109 
platform_ipm_callback(const struct device * dev,void * context,uint32_t id,volatile void * data)110 static void platform_ipm_callback(const struct device *dev, void *context,
111 				  uint32_t id, volatile void *data)
112 {
113 	k_sem_give(&data_sem);
114 }
115 
endpoint_cb(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)116 int endpoint_cb(struct rpmsg_endpoint *ept, void *data,
117 		size_t len, uint32_t src, void *priv)
118 {
119 	received_data = *((unsigned int *) data);
120 
121 	k_sem_give(&data_rx_sem);
122 
123 	return RPMSG_SUCCESS;
124 }
125 
126 static K_SEM_DEFINE(ept_sem, 0, 1);
127 
128 struct rpmsg_endpoint my_ept;
129 struct rpmsg_endpoint *ep = &my_ept;
130 
rpmsg_service_unbind(struct rpmsg_endpoint * ept)131 static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
132 {
133 	(void)ept;
134 	rpmsg_destroy_ept(ep);
135 }
136 
ns_bind_cb(struct rpmsg_device * rdev,const char * name,uint32_t dest)137 void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest)
138 {
139 	(void)rpmsg_create_ept(ep, rdev, name,
140 			RPMSG_ADDR_ANY, dest,
141 			endpoint_cb,
142 			rpmsg_service_unbind);
143 
144 	k_sem_give(&ept_sem);
145 }
146 
receive_message(void)147 static unsigned int receive_message(void)
148 {
149 	while (k_sem_take(&data_rx_sem, K_NO_WAIT) != 0) {
150 		int status = k_sem_take(&data_sem, K_FOREVER);
151 
152 		if (status == 0) {
153 			virtqueue_notification(vq[0]);
154 		}
155 	}
156 	return received_data;
157 }
158 
send_message(unsigned int message)159 static int send_message(unsigned int message)
160 {
161 	return rpmsg_send(ep, &message, sizeof(message));
162 }
163 
164 static struct rpmsg_virtio_shm_pool shpool;
165 
app_task(void * arg1,void * arg2,void * arg3)166 void app_task(void *arg1, void *arg2, void *arg3)
167 {
168 	ARG_UNUSED(arg1);
169 	ARG_UNUSED(arg2);
170 	ARG_UNUSED(arg3);
171 	int status = 0;
172 	unsigned int message = 0U;
173 	struct metal_device *device;
174 	struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
175 
176 	printk("\r\nOpenAMP[master] demo started\r\n");
177 
178 	status = metal_init(&metal_params);
179 	if (status != 0) {
180 		printk("metal_init: failed - error code %d\n", status);
181 		return;
182 	}
183 
184 	status = metal_register_generic_device(&shm_device);
185 	if (status != 0) {
186 		printk("Couldn't register shared memory device: %d\n", status);
187 		return;
188 	}
189 
190 	status = metal_device_open("generic", SHM_DEVICE_NAME, &device);
191 	if (status != 0) {
192 		printk("metal_device_open failed: %d\n", status);
193 		return;
194 	}
195 
196 	io = metal_device_io_region(device, 0);
197 	if (io == NULL) {
198 		printk("metal_device_io_region failed to get region\n");
199 		return;
200 	}
201 
202 	/* setup IPM */
203 	ipm_handle = device_get_binding(CONFIG_OPENAMP_IPC_DEV_NAME);
204 	if (ipm_handle == NULL) {
205 		printk("device_get_binding failed to find device\n");
206 		return;
207 	}
208 
209 	ipm_register_callback(ipm_handle, platform_ipm_callback, NULL);
210 
211 	status = ipm_set_enabled(ipm_handle, 1);
212 	if (status != 0) {
213 		printk("ipm_set_enabled failed\n");
214 		return;
215 	}
216 
217 	/* setup vdev */
218 	vq[0] = virtqueue_allocate(VRING_SIZE);
219 	if (vq[0] == NULL) {
220 		printk("virtqueue_allocate failed to alloc vq[0]\n");
221 		return;
222 	}
223 	vq[1] = virtqueue_allocate(VRING_SIZE);
224 	if (vq[1] == NULL) {
225 		printk("virtqueue_allocate failed to alloc vq[1]\n");
226 		return;
227 	}
228 
229 	vdev.role = RPMSG_MASTER;
230 	vdev.vrings_num = VRING_COUNT;
231 	vdev.func = &dispatch;
232 	rvrings[0].io = io;
233 	rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS;
234 	rvrings[0].info.num_descs = VRING_SIZE;
235 	rvrings[0].info.align = VRING_ALIGNMENT;
236 	rvrings[0].vq = vq[0];
237 
238 	rvrings[1].io = io;
239 	rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS;
240 	rvrings[1].info.num_descs = VRING_SIZE;
241 	rvrings[1].info.align = VRING_ALIGNMENT;
242 	rvrings[1].vq = vq[1];
243 
244 	vdev.vrings_info = &rvrings[0];
245 
246 	/* setup rvdev */
247 	rpmsg_virtio_init_shm_pool(&shpool, (void *)SHM_START_ADDR, SHM_SIZE);
248 	status = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool);
249 	if (status != 0) {
250 		printk("rpmsg_init_vdev failed %d\n", status);
251 		return;
252 	}
253 
254 	/* Since we are using name service, we need to wait for a response
255 	 * from NS setup and than we need to process it
256 	 */
257 	k_sem_take(&data_sem, K_FOREVER);
258 	virtqueue_notification(vq[0]);
259 
260 	/* Wait til nameservice ep is setup */
261 	k_sem_take(&ept_sem, K_FOREVER);
262 
263 	while (message < 100) {
264 		status = send_message(message);
265 		if (status < 0) {
266 			printk("send_message(%d) failed with status %d\n",
267 			       message, status);
268 			goto _cleanup;
269 		}
270 
271 		message = receive_message();
272 		printk("Master core received a message: %d\n", message);
273 
274 		message++;
275 	}
276 
277 _cleanup:
278 	rpmsg_deinit_vdev(&rvdev);
279 	metal_finish();
280 
281 	printk("OpenAMP demo ended.\n");
282 }
283 
main(void)284 void main(void)
285 {
286 	printk("Starting application thread!\n");
287 	k_thread_create(&thread_data, thread_stack, APP_TASK_STACK_SIZE,
288 			(k_thread_entry_t)app_task,
289 			NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
290 
291 #if defined(CONFIG_SOC_MPS2_AN521) || \
292 	defined(CONFIG_SOC_V2M_MUSCA_B1)
293 	wakeup_cpu1();
294 	k_msleep(500);
295 #endif /* #if defined(CONFIG_SOC_MPS2_AN521) */
296 }
297 
298 /* Make sure we clear out the status flag very early (before we bringup the
299  * secondary core) so the secondary core see's the proper status
300  */
init_status_flag(const struct device * arg)301 int init_status_flag(const struct device *arg)
302 {
303 	virtio_set_status(NULL, 0);
304 
305 	return 0;
306 }
307 
308 SYS_INIT(init_status_flag, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
309