1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2018, Linaro Limited
4 
5 #include <linux/slab.h>
6 #include <linux/wait.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/sched.h>
10 #include <linux/of.h>
11 #include <linux/of_platform.h>
12 #include <linux/jiffies.h>
13 #include <linux/soc/qcom/apr.h>
14 #include "q6core.h"
15 #include "q6dsp-errno.h"
16 
17 #define ADSP_STATE_READY_TIMEOUT_MS    3000
18 #define Q6_READY_TIMEOUT_MS 100
19 #define AVCS_CMD_ADSP_EVENT_GET_STATE		0x0001290C
20 #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE	0x0001290D
21 #define AVCS_GET_VERSIONS       0x00012905
22 #define AVCS_GET_VERSIONS_RSP   0x00012906
23 #define AVCS_CMD_GET_FWK_VERSION	0x001292c
24 #define AVCS_CMDRSP_GET_FWK_VERSION	0x001292d
25 
26 struct avcs_svc_info {
27 	uint32_t service_id;
28 	uint32_t version;
29 } __packed;
30 
31 struct avcs_cmdrsp_get_version {
32 	uint32_t build_id;
33 	uint32_t num_services;
34 	struct avcs_svc_info svc_api_info[];
35 } __packed;
36 
37 /* for ADSP2.8 and above */
38 struct avcs_svc_api_info {
39 	uint32_t service_id;
40 	uint32_t api_version;
41 	uint32_t api_branch_version;
42 } __packed;
43 
44 struct avcs_cmdrsp_get_fwk_version {
45 	uint32_t build_major_version;
46 	uint32_t build_minor_version;
47 	uint32_t build_branch_version;
48 	uint32_t build_subbranch_version;
49 	uint32_t num_services;
50 	struct avcs_svc_api_info svc_api_info[];
51 } __packed;
52 
53 struct q6core {
54 	struct apr_device *adev;
55 	wait_queue_head_t wait;
56 	uint32_t avcs_state;
57 	struct mutex lock;
58 	bool resp_received;
59 	uint32_t num_services;
60 	struct avcs_cmdrsp_get_fwk_version *fwk_version;
61 	struct avcs_cmdrsp_get_version *svc_version;
62 	bool fwk_version_supported;
63 	bool get_state_supported;
64 	bool get_version_supported;
65 	bool is_version_requested;
66 };
67 
68 static struct q6core *g_core;
69 
q6core_callback(struct apr_device * adev,struct apr_resp_pkt * data)70 static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
71 {
72 	struct q6core *core = dev_get_drvdata(&adev->dev);
73 	struct aprv2_ibasic_rsp_result_t *result;
74 	struct apr_hdr *hdr = &data->hdr;
75 
76 	result = data->payload;
77 	switch (hdr->opcode) {
78 	case APR_BASIC_RSP_RESULT:{
79 		result = data->payload;
80 		switch (result->opcode) {
81 		case AVCS_GET_VERSIONS:
82 			if (result->status == ADSP_EUNSUPPORTED)
83 				core->get_version_supported = false;
84 			core->resp_received = true;
85 			break;
86 		case AVCS_CMD_GET_FWK_VERSION:
87 			if (result->status == ADSP_EUNSUPPORTED)
88 				core->fwk_version_supported = false;
89 			core->resp_received = true;
90 			break;
91 		case AVCS_CMD_ADSP_EVENT_GET_STATE:
92 			if (result->status == ADSP_EUNSUPPORTED)
93 				core->get_state_supported = false;
94 			core->resp_received = true;
95 			break;
96 		}
97 		break;
98 	}
99 	case AVCS_CMDRSP_GET_FWK_VERSION: {
100 		struct avcs_cmdrsp_get_fwk_version *fwk;
101 
102 		fwk = data->payload;
103 
104 		core->fwk_version = kmemdup(data->payload,
105 					    struct_size(fwk, svc_api_info,
106 							fwk->num_services),
107 					    GFP_ATOMIC);
108 		if (!core->fwk_version)
109 			return -ENOMEM;
110 
111 		core->fwk_version_supported = true;
112 		core->resp_received = true;
113 
114 		break;
115 	}
116 	case AVCS_GET_VERSIONS_RSP: {
117 		struct avcs_cmdrsp_get_version *v;
118 
119 		v = data->payload;
120 
121 		core->svc_version = kmemdup(data->payload,
122 					    struct_size(v, svc_api_info,
123 							v->num_services),
124 					    GFP_ATOMIC);
125 		if (!core->svc_version)
126 			return -ENOMEM;
127 
128 		core->get_version_supported = true;
129 		core->resp_received = true;
130 
131 		break;
132 	}
133 	case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
134 		core->get_state_supported = true;
135 		core->avcs_state = result->opcode;
136 
137 		core->resp_received = true;
138 		break;
139 	default:
140 		dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
141 			hdr->opcode);
142 		break;
143 	}
144 
145 	if (core->resp_received)
146 		wake_up(&core->wait);
147 
148 	return 0;
149 }
150 
q6core_get_fwk_versions(struct q6core * core)151 static int q6core_get_fwk_versions(struct q6core *core)
152 {
153 	struct apr_device *adev = core->adev;
154 	struct apr_pkt pkt;
155 	int rc;
156 
157 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
158 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
159 	pkt.hdr.pkt_size = APR_HDR_SIZE;
160 	pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION;
161 
162 	rc = apr_send_pkt(adev, &pkt);
163 	if (rc < 0)
164 		return rc;
165 
166 	rc = wait_event_timeout(core->wait, (core->resp_received),
167 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
168 	if (rc > 0 && core->resp_received) {
169 		core->resp_received = false;
170 
171 		if (!core->fwk_version_supported)
172 			return -ENOTSUPP;
173 		else
174 			return 0;
175 	}
176 
177 
178 	return rc;
179 }
180 
q6core_get_svc_versions(struct q6core * core)181 static int q6core_get_svc_versions(struct q6core *core)
182 {
183 	struct apr_device *adev = core->adev;
184 	struct apr_pkt pkt;
185 	int rc;
186 
187 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
188 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
189 	pkt.hdr.pkt_size = APR_HDR_SIZE;
190 	pkt.hdr.opcode = AVCS_GET_VERSIONS;
191 
192 	rc = apr_send_pkt(adev, &pkt);
193 	if (rc < 0)
194 		return rc;
195 
196 	rc = wait_event_timeout(core->wait, (core->resp_received),
197 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
198 	if (rc > 0 && core->resp_received) {
199 		core->resp_received = false;
200 		return 0;
201 	}
202 
203 	return rc;
204 }
205 
__q6core_is_adsp_ready(struct q6core * core)206 static bool __q6core_is_adsp_ready(struct q6core *core)
207 {
208 	struct apr_device *adev = core->adev;
209 	struct apr_pkt pkt;
210 	int rc;
211 
212 	core->get_state_supported = false;
213 
214 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
215 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
216 	pkt.hdr.pkt_size = APR_HDR_SIZE;
217 	pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
218 
219 	rc = apr_send_pkt(adev, &pkt);
220 	if (rc < 0)
221 		return false;
222 
223 	rc = wait_event_timeout(core->wait, (core->resp_received),
224 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
225 	if (rc > 0 && core->resp_received) {
226 		core->resp_received = false;
227 
228 		if (core->avcs_state)
229 			return true;
230 	}
231 
232 	/* assume that the adsp is up if we not support this command */
233 	if (!core->get_state_supported)
234 		return true;
235 
236 	return false;
237 }
238 
239 /**
240  * q6core_get_svc_api_info() - Get version number of a service.
241  *
242  * @svc_id: service id of the service.
243  * @ainfo: Valid struct pointer to fill svc api information.
244  *
245  * Return: zero on success and error code on failure or unsupported
246  */
q6core_get_svc_api_info(int svc_id,struct q6core_svc_api_info * ainfo)247 int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
248 {
249 	int i;
250 	int ret = -ENOTSUPP;
251 
252 	if (!g_core || !ainfo)
253 		return 0;
254 
255 	mutex_lock(&g_core->lock);
256 	if (!g_core->is_version_requested) {
257 		if (q6core_get_fwk_versions(g_core) == -ENOTSUPP)
258 			q6core_get_svc_versions(g_core);
259 		g_core->is_version_requested = true;
260 	}
261 
262 	if (g_core->fwk_version_supported) {
263 		for (i = 0; i < g_core->fwk_version->num_services; i++) {
264 			struct avcs_svc_api_info *info;
265 
266 			info = &g_core->fwk_version->svc_api_info[i];
267 			if (svc_id != info->service_id)
268 				continue;
269 
270 			ainfo->api_version = info->api_version;
271 			ainfo->api_branch_version = info->api_branch_version;
272 			ret = 0;
273 			break;
274 		}
275 	} else if (g_core->get_version_supported) {
276 		for (i = 0; i < g_core->svc_version->num_services; i++) {
277 			struct avcs_svc_info *info;
278 
279 			info = &g_core->svc_version->svc_api_info[i];
280 			if (svc_id != info->service_id)
281 				continue;
282 
283 			ainfo->api_version = info->version;
284 			ainfo->api_branch_version = 0;
285 			ret = 0;
286 			break;
287 		}
288 	}
289 
290 	mutex_unlock(&g_core->lock);
291 
292 	return ret;
293 }
294 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
295 
296 /**
297  * q6core_is_adsp_ready() - Get status of adsp
298  *
299  * Return: Will be an true if adsp is ready and false if not.
300  */
q6core_is_adsp_ready(void)301 bool q6core_is_adsp_ready(void)
302 {
303 	unsigned long  timeout;
304 	bool ret = false;
305 
306 	if (!g_core)
307 		return false;
308 
309 	mutex_lock(&g_core->lock);
310 	timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
311 	for (;;) {
312 		if (__q6core_is_adsp_ready(g_core)) {
313 			ret = true;
314 			break;
315 		}
316 
317 		if (!time_after(timeout, jiffies)) {
318 			ret = false;
319 			break;
320 		}
321 	}
322 
323 	mutex_unlock(&g_core->lock);
324 	return ret;
325 }
326 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
327 
q6core_probe(struct apr_device * adev)328 static int q6core_probe(struct apr_device *adev)
329 {
330 	g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
331 	if (!g_core)
332 		return -ENOMEM;
333 
334 	dev_set_drvdata(&adev->dev, g_core);
335 
336 	mutex_init(&g_core->lock);
337 	g_core->adev = adev;
338 	init_waitqueue_head(&g_core->wait);
339 	return 0;
340 }
341 
q6core_exit(struct apr_device * adev)342 static int q6core_exit(struct apr_device *adev)
343 {
344 	struct q6core *core = dev_get_drvdata(&adev->dev);
345 
346 	if (core->fwk_version_supported)
347 		kfree(core->fwk_version);
348 	if (core->get_version_supported)
349 		kfree(core->svc_version);
350 
351 	g_core = NULL;
352 	kfree(core);
353 
354 	return 0;
355 }
356 
357 #ifdef CONFIG_OF
358 static const struct of_device_id q6core_device_id[]  = {
359 	{ .compatible = "qcom,q6core" },
360 	{},
361 };
362 MODULE_DEVICE_TABLE(of, q6core_device_id);
363 #endif
364 
365 static struct apr_driver qcom_q6core_driver = {
366 	.probe = q6core_probe,
367 	.remove = q6core_exit,
368 	.callback = q6core_callback,
369 	.driver = {
370 		.name = "qcom-q6core",
371 		.of_match_table = of_match_ptr(q6core_device_id),
372 	},
373 };
374 
375 module_apr_driver(qcom_q6core_driver);
376 MODULE_DESCRIPTION("q6 core");
377 MODULE_LICENSE("GPL v2");
378