1 /*
2  * Copyright (c) 2023 Linaro.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT linaro_ivshmem_ipm
8 
9 #include <stdint.h>
10 #include <string.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/ipm.h>
13 #include <zephyr/drivers/virtualization/ivshmem.h>
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(ipm_ivshmem, CONFIG_IPM_LOG_LEVEL);
16 
17 K_THREAD_STACK_DEFINE(ivshmem_ev_loop_stack, CONFIG_IPM_IVSHMEM_EVENT_LOOP_STACK_SIZE);
18 static struct k_thread ivshmem_ev_loop_thread;
19 
20 struct ivshmem_ipm_data {
21 	ipm_callback_t cb;
22 	void *user_data;
23 };
24 
25 struct ivshmem_ipm_config {
26 	const struct device *ivshmem_dev;
27 };
28 
ivshmem_ipm_event_loop_thread(void * arg,void * p2,void * p3)29 static void ivshmem_ipm_event_loop_thread(void *arg, void *p2, void *p3)
30 {
31 	ARG_UNUSED(p2);
32 	ARG_UNUSED(p3);
33 
34 	unsigned int poll_signaled;
35 	int ivshmem_vector_rx;
36 	struct k_poll_signal sig;
37 	struct k_poll_event events[] = {
38 		K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL,
39 					 K_POLL_MODE_NOTIFY_ONLY,
40 					 &sig),
41 	};
42 
43 	const struct device *dev = (const struct device *)arg;
44 	struct ivshmem_ipm_data *dev_data = (struct ivshmem_ipm_data *)dev->data;
45 	struct ivshmem_ipm_config *dev_cfg = (struct ivshmem_ipm_config *)dev->config;
46 
47 	k_poll_signal_init(&sig);
48 	int ret = ivshmem_register_handler(dev_cfg->ivshmem_dev, &sig, 0);
49 
50 	if (ret < 0) {
51 		LOG_ERR("registering handlers must be supported: %d\n", ret);
52 		k_panic();
53 	}
54 
55 	while (1) {
56 		LOG_DBG("%s: waiting interrupt from client...\n", __func__);
57 		ret = k_poll(events, ARRAY_SIZE(events), K_FOREVER);
58 
59 		k_poll_signal_check(&sig, &poll_signaled, &ivshmem_vector_rx);
60 		/* get ready for next signal */
61 		k_poll_signal_reset(&sig);
62 
63 		if (dev_data->cb)
64 			dev_data->cb(dev, dev_data->user_data, 0, NULL);
65 	}
66 }
67 
ivshmem_ipm_send(const struct device * dev,int wait,uint32_t id,const void * data,int size)68 static int ivshmem_ipm_send(const struct device *dev, int wait, uint32_t id,
69 			 const void *data, int size)
70 {
71 	ARG_UNUSED(wait);
72 	ARG_UNUSED(data);
73 	ARG_UNUSED(size);
74 
75 	struct ivshmem_ipm_config *dev_cfg = (struct ivshmem_ipm_config *)dev->config;
76 
77 	LOG_DBG("sending notification to the peer id 0x%x\n", id);
78 	return ivshmem_int_peer(dev_cfg->ivshmem_dev, id, 0);
79 }
80 
ivshmem_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)81 static void ivshmem_ipm_register_callback(const struct device *dev,
82 					ipm_callback_t cb,
83 					void *user_data)
84 {
85 	struct ivshmem_ipm_data *dev_data = (struct ivshmem_ipm_data *)dev->data;
86 
87 	dev_data->cb = cb;
88 	dev_data->user_data = user_data;
89 }
90 
ivshmem_ipm_set_enabled(const struct device * dev,int enable)91 static int ivshmem_ipm_set_enabled(const struct device *dev, int enable)
92 {
93 	/* some subsystems needs this minimal function just return success here*/
94 	ARG_UNUSED(dev);
95 	ARG_UNUSED(enable);
96 
97 	return 0;
98 }
99 
ivshmem_ipm_init(const struct device * dev)100 static int ivshmem_ipm_init(const struct device *dev)
101 {
102 	k_thread_create(&ivshmem_ev_loop_thread,
103 		ivshmem_ev_loop_stack,
104 		CONFIG_IPM_IVSHMEM_EVENT_LOOP_STACK_SIZE,
105 		(k_thread_entry_t)ivshmem_ipm_event_loop_thread,
106 		(void *)dev, NULL, NULL,
107 		CONFIG_IPM_IVSHMEM_EVENT_LOOP_PRIO,
108 		0, K_NO_WAIT);
109 
110 	return 0;
111 }
112 
113 static const struct ipm_driver_api ivshmem_ipm_driver_api = {
114 	.send = ivshmem_ipm_send,
115 	.register_callback = ivshmem_ipm_register_callback,
116 	.set_enabled = ivshmem_ipm_set_enabled
117 };
118 
119 #define IPM_IVSHMEM_INIT(inst)								    \
120 	static const struct ivshmem_ipm_config ivshmem_ipm_cfg_##inst = {	\
121 		.ivshmem_dev =	\
122 			DEVICE_DT_GET(DT_INST_PHANDLE(inst, ivshmem))\
123 	};	\
124 	static struct ivshmem_ipm_data ivshmem_ipm_data_##inst = {	\
125 		.cb = NULL,	\
126 		.user_data = NULL,	\
127 	};	\
128 	DEVICE_DT_INST_DEFINE(inst,	\
129 				ivshmem_ipm_init,	\
130 				NULL,	\
131 				&ivshmem_ipm_data_##inst, &ivshmem_ipm_cfg_##inst,  \
132 				POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY,      \
133 				&ivshmem_ipm_driver_api);                           \
134 
135 DT_INST_FOREACH_STATUS_OKAY(IPM_IVSHMEM_INIT);
136