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