q6core.c (8604B)
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 26struct avcs_svc_info { 27 uint32_t service_id; 28 uint32_t version; 29} __packed; 30 31struct 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 */ 38struct avcs_svc_api_info { 39 uint32_t service_id; 40 uint32_t api_version; 41 uint32_t api_branch_version; 42} __packed; 43 44struct 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 53struct 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 68static struct q6core *g_core; 69 70static 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 151static 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 181static 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 206static 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 */ 247int 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} 294EXPORT_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 */ 301bool 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} 326EXPORT_SYMBOL_GPL(q6core_is_adsp_ready); 327 328static 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 342static 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 358static const struct of_device_id q6core_device_id[] = { 359 { .compatible = "qcom,q6core" }, 360 {}, 361}; 362MODULE_DEVICE_TABLE(of, q6core_device_id); 363#endif 364 365static 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 375module_apr_driver(qcom_q6core_driver); 376MODULE_DESCRIPTION("q6 core"); 377MODULE_LICENSE("GPL v2");