1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2018, Linaro Limited
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>
11 #include <linux/of_platform.h>
12 #include <linux/jiffies.h>
13 #include <linux/soc/qcom/apr.h>
15 #include "q6dsp-errno.h"
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
26 struct avcs_svc_info {
31 struct avcs_cmdrsp_get_version {
33 uint32_t num_services;
34 struct avcs_svc_info svc_api_info[];
37 /* for ADSP2.8 and above */
38 struct avcs_svc_api_info {
41 uint32_t api_branch_version;
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[];
54 struct apr_device *adev;
55 wait_queue_head_t wait;
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;
68 static struct q6core *g_core;
70 static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
72 struct q6core *core = dev_get_drvdata(&adev->dev);
73 struct aprv2_ibasic_rsp_result_t *result;
74 struct apr_hdr *hdr = &data->hdr;
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;
86 case AVCS_CMD_GET_FWK_VERSION:
87 if (result->status == ADSP_EUNSUPPORTED)
88 core->fwk_version_supported = false;
89 core->resp_received = true;
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;
99 case AVCS_CMDRSP_GET_FWK_VERSION: {
100 struct avcs_cmdrsp_get_fwk_version *fwk;
104 core->fwk_version = kmemdup(data->payload,
105 struct_size(fwk, svc_api_info,
108 if (!core->fwk_version)
111 core->fwk_version_supported = true;
112 core->resp_received = true;
116 case AVCS_GET_VERSIONS_RSP: {
117 struct avcs_cmdrsp_get_version *v;
121 core->svc_version = kmemdup(data->payload,
122 struct_size(v, svc_api_info,
125 if (!core->svc_version)
128 core->get_version_supported = true;
129 core->resp_received = true;
133 case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
134 core->get_state_supported = true;
135 core->avcs_state = result->opcode;
137 core->resp_received = true;
140 dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
145 if (core->resp_received)
146 wake_up(&core->wait);
151 static int q6core_get_fwk_versions(struct q6core *core)
153 struct apr_device *adev = core->adev;
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;
162 rc = apr_send_pkt(adev, &pkt);
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;
171 if (!core->fwk_version_supported)
181 static int q6core_get_svc_versions(struct q6core *core)
183 struct apr_device *adev = core->adev;
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;
192 rc = apr_send_pkt(adev, &pkt);
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;
206 static bool __q6core_is_adsp_ready(struct q6core *core)
208 struct apr_device *adev = core->adev;
212 core->get_state_supported = false;
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;
219 rc = apr_send_pkt(adev, &pkt);
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;
228 if (core->avcs_state)
232 /* assume that the adsp is up if we not support this command */
233 if (!core->get_state_supported)
240 * q6core_get_svc_api_info() - Get version number of a service.
242 * @svc_id: service id of the service.
243 * @ainfo: Valid struct pointer to fill svc api information.
245 * Return: zero on success and error code on failure or unsupported
247 int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
252 if (!g_core || !ainfo)
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;
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;
266 info = &g_core->fwk_version->svc_api_info[i];
267 if (svc_id != info->service_id)
270 ainfo->api_version = info->api_version;
271 ainfo->api_branch_version = info->api_branch_version;
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;
279 info = &g_core->svc_version->svc_api_info[i];
280 if (svc_id != info->service_id)
283 ainfo->api_version = info->version;
284 ainfo->api_branch_version = 0;
290 mutex_unlock(&g_core->lock);
294 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
297 * q6core_is_adsp_ready() - Get status of adsp
299 * Return: Will be an true if adsp is ready and false if not.
301 bool q6core_is_adsp_ready(void)
303 unsigned long timeout;
309 mutex_lock(&g_core->lock);
310 timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
312 if (__q6core_is_adsp_ready(g_core)) {
317 if (!time_after(timeout, jiffies)) {
323 mutex_unlock(&g_core->lock);
326 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
328 static int q6core_probe(struct apr_device *adev)
330 g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
334 dev_set_drvdata(&adev->dev, g_core);
336 mutex_init(&g_core->lock);
338 init_waitqueue_head(&g_core->wait);
342 static int q6core_exit(struct apr_device *adev)
344 struct q6core *core = dev_get_drvdata(&adev->dev);
346 if (core->fwk_version_supported)
347 kfree(core->fwk_version);
348 if (core->get_version_supported)
349 kfree(core->svc_version);
358 static const struct of_device_id q6core_device_id[] = {
359 { .compatible = "qcom,q6core" },
362 MODULE_DEVICE_TABLE(of, q6core_device_id);
365 static struct apr_driver qcom_q6core_driver = {
366 .probe = q6core_probe,
367 .remove = q6core_exit,
368 .callback = q6core_callback,
370 .name = "qcom-q6core",
371 .of_match_table = of_match_ptr(q6core_device_id),
375 module_apr_driver(qcom_q6core_driver);
376 MODULE_DESCRIPTION("q6 core");
377 MODULE_LICENSE("GPL v2");