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 }
68 
ivshmem_ipm_send(const struct device * dev,int wait,uint32_t id,const void * data,int size)69 static int ivshmem_ipm_send(const struct device *dev, int wait, uint32_t id,
70 			 const void *data, int size)
71 {
72 	ARG_UNUSED(wait);
73 	ARG_UNUSED(data);
74 	ARG_UNUSED(size);
75 
76 	struct ivshmem_ipm_config *dev_cfg = (struct ivshmem_ipm_config *)dev->config;
77 
78 	LOG_DBG("sending notification to the peer id 0x%x\n", id);
79 	return ivshmem_int_peer(dev_cfg->ivshmem_dev, id, 0);
80 }
81 
ivshmem_ipm_register_callback(const struct device * dev,ipm_callback_t cb,void * user_data)82 static void ivshmem_ipm_register_callback(const struct device *dev,
83 					ipm_callback_t cb,
84 					void *user_data)
85 {
86 	struct ivshmem_ipm_data *dev_data = (struct ivshmem_ipm_data *)dev->data;
87 
88 	dev_data->cb = cb;
89 	dev_data->user_data = user_data;
90 }
91 
ivshmem_ipm_set_enabled(const struct device * dev,int enable)92 static int ivshmem_ipm_set_enabled(const struct device *dev, int enable)
93 {
94 	/* some subsystems needs this minimal function just return success here*/
95 	ARG_UNUSED(dev);
96 	ARG_UNUSED(enable);
97 
98 	return 0;
99 }
100 
ivshmem_ipm_init(const struct device * dev)101 static int ivshmem_ipm_init(const struct device *dev)
102 {
103 	k_thread_create(&ivshmem_ev_loop_thread,
104 		ivshmem_ev_loop_stack,
105 		CONFIG_IPM_IVSHMEM_EVENT_LOOP_STACK_SIZE,
106 		ivshmem_ipm_event_loop_thread,
107 		(void *)dev, NULL, NULL,
108 		CONFIG_IPM_IVSHMEM_EVENT_LOOP_PRIO,
109 		0, K_NO_WAIT);
110 
111 	return 0;
112 }
113 
114 static DEVICE_API(ipm, ivshmem_ipm_driver_api) = {
115 	.send = ivshmem_ipm_send,
116 	.register_callback = ivshmem_ipm_register_callback,
117 	.set_enabled = ivshmem_ipm_set_enabled
118 };
119 
120 #define IPM_IVSHMEM_INIT(inst)								    \
121 	static const struct ivshmem_ipm_config ivshmem_ipm_cfg_##inst = {	\
122 		.ivshmem_dev =	\
123 			DEVICE_DT_GET(DT_INST_PHANDLE(inst, ivshmem))\
124 	};	\
125 	static struct ivshmem_ipm_data ivshmem_ipm_data_##inst = {	\
126 		.cb = NULL,	\
127 		.user_data = NULL,	\
128 	};	\
129 	DEVICE_DT_INST_DEFINE(inst,	\
130 				ivshmem_ipm_init,	\
131 				NULL,	\
132 				&ivshmem_ipm_data_##inst, &ivshmem_ipm_cfg_##inst,  \
133 				POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY,      \
134 				&ivshmem_ipm_driver_api);                           \
135 
136 DT_INST_FOREACH_STATUS_OKAY(IPM_IVSHMEM_INIT);
137