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