1 /*
2  * Copyright (c) 2022, 2025 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 
9 #include <zephyr/init.h>
10 #include <zephyr/spinlock.h>
11 
12 #include <intel_adsp_ipc.h>
13 #include <zephyr/ipc/backends/intel_adsp_host_ipc.h>
14 
15 static struct ipc_ept intel_adsp_ipc_ept;
16 static struct intel_adsp_ipc_ept_priv_data intel_adsp_ipc_priv_data;
17 static struct ipc_ept_cfg intel_adsp_ipc_ept_cfg;
18 
intel_adsp_ipc_set_message_handler(const struct device * dev,intel_adsp_ipc_handler_t fn,void * arg)19 void intel_adsp_ipc_set_message_handler(const struct device *dev, intel_adsp_ipc_handler_t fn,
20 					void *arg)
21 {
22 	struct intel_adsp_ipc_data *devdata = dev->data;
23 	k_spinlock_key_t key = k_spin_lock(&devdata->lock);
24 
25 	devdata->handle_message = fn;
26 	devdata->handler_arg = arg;
27 	k_spin_unlock(&devdata->lock, key);
28 }
29 
intel_adsp_ipc_set_done_handler(const struct device * dev,intel_adsp_ipc_done_t fn,void * arg)30 void intel_adsp_ipc_set_done_handler(const struct device *dev, intel_adsp_ipc_done_t fn, void *arg)
31 {
32 	struct intel_adsp_ipc_data *devdata = dev->data;
33 	k_spinlock_key_t key = k_spin_lock(&devdata->lock);
34 
35 	devdata->done_notify = fn;
36 	devdata->done_arg = arg;
37 	k_spin_unlock(&devdata->lock, key);
38 }
39 
intel_adsp_ipc_receive_cb(const void * data,size_t len,void * priv)40 static void intel_adsp_ipc_receive_cb(const void *data, size_t len, void *priv)
41 {
42 	const struct device *dev = INTEL_ADSP_IPC_HOST_DEV;
43 	struct intel_adsp_ipc_data *devdata = dev->data;
44 	struct intel_adsp_ipc_ept_priv_data *priv_data =
45 		(struct intel_adsp_ipc_ept_priv_data *)priv;
46 	bool done = true;
47 
48 	if (len == INTEL_ADSP_IPC_CB_MSG) {
49 		const struct intel_adsp_ipc_msg *msg = (const struct intel_adsp_ipc_msg *)data;
50 
51 		if (devdata->handle_message != NULL) {
52 			done = devdata->handle_message(dev, devdata->handler_arg, msg->data,
53 						       msg->ext_data);
54 		}
55 
56 		if (done) {
57 			priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY;
58 		} else {
59 			priv_data->cb_ret = -EBADMSG;
60 		}
61 	} else if (len == INTEL_ADSP_IPC_CB_DONE) {
62 		bool external_completion = false;
63 
64 		if (devdata->done_notify != NULL) {
65 			external_completion = devdata->done_notify(dev, devdata->done_arg);
66 		}
67 
68 		if (external_completion) {
69 			priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_EXT_COMPLETE;
70 		} else {
71 			priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY;
72 		}
73 	}
74 }
75 
intel_adsp_ipc_complete(const struct device * dev)76 void intel_adsp_ipc_complete(const struct device *dev)
77 {
78 	int ret;
79 
80 	ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_DONE);
81 
82 	ARG_UNUSED(ret);
83 }
84 
intel_adsp_ipc_is_complete(const struct device * dev)85 bool intel_adsp_ipc_is_complete(const struct device *dev)
86 {
87 	int ret;
88 
89 	ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_IS_COMPLETE);
90 
91 	return ret == 0;
92 }
93 
intel_adsp_ipc_send_message(const struct device * dev,uint32_t data,uint32_t ext_data)94 int intel_adsp_ipc_send_message(const struct device *dev, uint32_t data, uint32_t ext_data)
95 {
96 	struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data};
97 	int ret;
98 
99 	ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG);
100 
101 	if (ret < 0) {
102 		return ret;
103 	}
104 
105 	return 0;
106 }
107 
intel_adsp_ipc_send_message_sync(const struct device * dev,uint32_t data,uint32_t ext_data,k_timeout_t timeout)108 int intel_adsp_ipc_send_message_sync(const struct device *dev, uint32_t data, uint32_t ext_data,
109 				     k_timeout_t timeout)
110 {
111 	struct intel_adsp_ipc_msg msg = {
112 		.data = data,
113 		.ext_data = ext_data,
114 		.timeout = timeout,
115 	};
116 	int ret;
117 
118 	ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_SYNC);
119 
120 	if (ret < 0) {
121 		return ret;
122 	}
123 
124 	return 0;
125 }
126 
intel_adsp_ipc_send_message_emergency(const struct device * dev,uint32_t data,uint32_t ext_data)127 void intel_adsp_ipc_send_message_emergency(const struct device *dev, uint32_t data,
128 					   uint32_t ext_data)
129 {
130 	struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data};
131 	int ret;
132 
133 	ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_EMERGENCY);
134 
135 	ARG_UNUSED(ret);
136 }
137 
138 static struct ipc_ept_cfg intel_adsp_ipc_ept_cfg = {
139 	.name = "intel_adsp_ipc_ept",
140 	.cb = {
141 		.received = intel_adsp_ipc_receive_cb,
142 	},
143 	.priv = (void *)&intel_adsp_ipc_priv_data,
144 };
145 
intel_adsp_ipc_old_init(void)146 static int intel_adsp_ipc_old_init(void)
147 {
148 	int ret;
149 	const struct device *ipc_dev = INTEL_ADSP_IPC_HOST_DEV;
150 
151 	ret = ipc_service_register_endpoint(ipc_dev, &intel_adsp_ipc_ept, &intel_adsp_ipc_ept_cfg);
152 
153 	return ret;
154 }
155 
156 /* Backend is at PRE_KERNEL_2:0, so we need to init after that. */
157 SYS_INIT(intel_adsp_ipc_old_init, PRE_KERNEL_2, 1);
158