1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Qualcomm Protection Domain mapper 4 * 5 * Copyright (c) 2023 Linaro Ltd. 6 */ 7 8 #include <linux/auxiliary_bus.h> 9 #include <linux/kernel.h> 10 #include <linux/mod_devicetable.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/refcount.h> 14 #include <linux/slab.h> 15 #include <linux/soc/qcom/qmi.h> 16 17 #include "pdr_internal.h" 18 19 #define SERVREG_QMI_VERSION 0x101 20 #define SERVREG_QMI_INSTANCE 0 21 22 #define TMS_SERVREG_SERVICE "tms/servreg" 23 24 struct qcom_pdm_domain_data { 25 const char *domain; 26 u32 instance_id; 27 /* NULL-terminated array */ 28 const char * services[]; 29 }; 30 31 struct qcom_pdm_domain { 32 struct list_head list; 33 const char *name; 34 u32 instance_id; 35 }; 36 37 struct qcom_pdm_service { 38 struct list_head list; 39 struct list_head domains; 40 const char *name; 41 }; 42 43 struct qcom_pdm_data { 44 refcount_t refcnt; 45 struct qmi_handle handle; 46 struct list_head services; 47 }; 48 49 static DEFINE_MUTEX(qcom_pdm_mutex); /* protects __qcom_pdm_data */ 50 static struct qcom_pdm_data *__qcom_pdm_data; 51 52 static struct qcom_pdm_service *qcom_pdm_find(struct qcom_pdm_data *data, 53 const char *name) 54 { 55 struct qcom_pdm_service *service; 56 57 list_for_each_entry(service, &data->services, list) { 58 if (!strcmp(service->name, name)) 59 return service; 60 } 61 62 return NULL; 63 } 64 65 static int qcom_pdm_add_service_domain(struct qcom_pdm_data *data, 66 const char *service_name, 67 const char *domain_name, 68 u32 instance_id) 69 { 70 struct qcom_pdm_service *service; 71 struct qcom_pdm_domain *domain; 72 73 service = qcom_pdm_find(data, service_name); 74 if (service) { 75 list_for_each_entry(domain, &service->domains, list) { 76 if (!strcmp(domain->name, domain_name)) 77 return -EBUSY; 78 } 79 } else { 80 service = kzalloc_obj(*service); 81 if (!service) 82 return -ENOMEM; 83 84 INIT_LIST_HEAD(&service->domains); 85 service->name = service_name; 86 87 list_add_tail(&service->list, &data->services); 88 } 89 90 domain = kzalloc_obj(*domain); 91 if (!domain) { 92 if (list_empty(&service->domains)) { 93 list_del(&service->list); 94 kfree(service); 95 } 96 97 return -ENOMEM; 98 } 99 100 domain->name = domain_name; 101 domain->instance_id = instance_id; 102 list_add_tail(&domain->list, &service->domains); 103 104 return 0; 105 } 106 107 static int qcom_pdm_add_domain(struct qcom_pdm_data *data, 108 const struct qcom_pdm_domain_data *domain) 109 { 110 int ret; 111 int i; 112 113 ret = qcom_pdm_add_service_domain(data, 114 TMS_SERVREG_SERVICE, 115 domain->domain, 116 domain->instance_id); 117 if (ret) 118 return ret; 119 120 for (i = 0; domain->services[i]; i++) { 121 ret = qcom_pdm_add_service_domain(data, 122 domain->services[i], 123 domain->domain, 124 domain->instance_id); 125 if (ret) 126 return ret; 127 } 128 129 return 0; 130 131 } 132 133 static void qcom_pdm_free_domains(struct qcom_pdm_data *data) 134 { 135 struct qcom_pdm_service *service, *tservice; 136 struct qcom_pdm_domain *domain, *tdomain; 137 138 list_for_each_entry_safe(service, tservice, &data->services, list) { 139 list_for_each_entry_safe(domain, tdomain, &service->domains, list) { 140 list_del(&domain->list); 141 kfree(domain); 142 } 143 144 list_del(&service->list); 145 kfree(service); 146 } 147 } 148 149 static void qcom_pdm_get_domain_list(struct qmi_handle *qmi, 150 struct sockaddr_qrtr *sq, 151 struct qmi_txn *txn, 152 const void *decoded) 153 { 154 struct qcom_pdm_data *data = container_of(qmi, struct qcom_pdm_data, handle); 155 const struct servreg_get_domain_list_req *req = decoded; 156 struct servreg_get_domain_list_resp *rsp; 157 struct qcom_pdm_service *service; 158 u32 offset; 159 int ret; 160 161 rsp = kzalloc_obj(*rsp); 162 if (!rsp) 163 return; 164 165 offset = req->domain_offset_valid ? req->domain_offset : 0; 166 167 rsp->resp.result = QMI_RESULT_SUCCESS_V01; 168 rsp->resp.error = QMI_ERR_NONE_V01; 169 170 rsp->db_rev_count_valid = true; 171 rsp->db_rev_count = 1; 172 173 rsp->total_domains_valid = true; 174 rsp->total_domains = 0; 175 176 mutex_lock(&qcom_pdm_mutex); 177 178 service = qcom_pdm_find(data, req->service_name); 179 if (service) { 180 struct qcom_pdm_domain *domain; 181 182 rsp->domain_list_valid = true; 183 rsp->domain_list_len = 0; 184 185 list_for_each_entry(domain, &service->domains, list) { 186 u32 i = rsp->total_domains++; 187 188 if (i >= offset && i < SERVREG_DOMAIN_LIST_LENGTH) { 189 u32 j = rsp->domain_list_len++; 190 191 strscpy(rsp->domain_list[j].name, domain->name, 192 sizeof(rsp->domain_list[i].name)); 193 rsp->domain_list[j].instance = domain->instance_id; 194 195 pr_debug("PDM: found %s / %d\n", domain->name, 196 domain->instance_id); 197 } 198 } 199 } 200 201 pr_debug("PDM: service '%s' offset %d returning %d domains (of %d)\n", req->service_name, 202 req->domain_offset_valid ? req->domain_offset : -1, rsp->domain_list_len, rsp->total_domains); 203 204 ret = qmi_send_response(qmi, sq, txn, SERVREG_GET_DOMAIN_LIST_REQ, 205 SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN, 206 servreg_get_domain_list_resp_ei, rsp); 207 if (ret) 208 pr_err("Error sending servreg response: %d\n", ret); 209 210 mutex_unlock(&qcom_pdm_mutex); 211 212 kfree(rsp); 213 } 214 215 static void qcom_pdm_pfr(struct qmi_handle *qmi, 216 struct sockaddr_qrtr *sq, 217 struct qmi_txn *txn, 218 const void *decoded) 219 { 220 const struct servreg_loc_pfr_req *req = decoded; 221 struct servreg_loc_pfr_resp rsp = {}; 222 int ret; 223 224 pr_warn_ratelimited("PDM: service '%s' crash: '%s'\n", req->service, req->reason); 225 226 rsp.rsp.result = QMI_RESULT_SUCCESS_V01; 227 rsp.rsp.error = QMI_ERR_NONE_V01; 228 229 ret = qmi_send_response(qmi, sq, txn, SERVREG_LOC_PFR_REQ, 230 SERVREG_LOC_PFR_RESP_MAX_LEN, 231 servreg_loc_pfr_resp_ei, &rsp); 232 if (ret) 233 pr_err("Error sending servreg response: %d\n", ret); 234 } 235 236 static const struct qmi_msg_handler qcom_pdm_msg_handlers[] = { 237 { 238 .type = QMI_REQUEST, 239 .msg_id = SERVREG_GET_DOMAIN_LIST_REQ, 240 .ei = servreg_get_domain_list_req_ei, 241 .decoded_size = sizeof(struct servreg_get_domain_list_req), 242 .fn = qcom_pdm_get_domain_list, 243 }, 244 { 245 .type = QMI_REQUEST, 246 .msg_id = SERVREG_LOC_PFR_REQ, 247 .ei = servreg_loc_pfr_req_ei, 248 .decoded_size = sizeof(struct servreg_loc_pfr_req), 249 .fn = qcom_pdm_pfr, 250 }, 251 { }, 252 }; 253 254 static const struct qcom_pdm_domain_data adsp_audio_pd = { 255 .domain = "msm/adsp/audio_pd", 256 .instance_id = 74, 257 .services = { 258 "avs/audio", 259 NULL, 260 }, 261 }; 262 263 static const struct qcom_pdm_domain_data adsp_charger_pd = { 264 .domain = "msm/adsp/charger_pd", 265 .instance_id = 74, 266 .services = { NULL }, 267 }; 268 269 static const struct qcom_pdm_domain_data adsp_root_pd = { 270 .domain = "msm/adsp/root_pd", 271 .instance_id = 74, 272 .services = { NULL }, 273 }; 274 275 static const struct qcom_pdm_domain_data adsp_root_pd_pdr = { 276 .domain = "msm/adsp/root_pd", 277 .instance_id = 74, 278 .services = { 279 "tms/pdr_enabled", 280 NULL, 281 }, 282 }; 283 284 static const struct qcom_pdm_domain_data adsp_sensor_pd = { 285 .domain = "msm/adsp/sensor_pd", 286 .instance_id = 74, 287 .services = { NULL }, 288 }; 289 290 static const struct qcom_pdm_domain_data msm8996_adsp_audio_pd = { 291 .domain = "msm/adsp/audio_pd", 292 .instance_id = 4, 293 .services = { NULL }, 294 }; 295 296 static const struct qcom_pdm_domain_data msm8996_adsp_root_pd = { 297 .domain = "msm/adsp/root_pd", 298 .instance_id = 4, 299 .services = { NULL }, 300 }; 301 302 static const struct qcom_pdm_domain_data cdsp_root_pd = { 303 .domain = "msm/cdsp/root_pd", 304 .instance_id = 76, 305 .services = { NULL }, 306 }; 307 308 static const struct qcom_pdm_domain_data slpi_root_pd = { 309 .domain = "msm/slpi/root_pd", 310 .instance_id = 90, 311 .services = { NULL }, 312 }; 313 314 static const struct qcom_pdm_domain_data slpi_sensor_pd = { 315 .domain = "msm/slpi/sensor_pd", 316 .instance_id = 90, 317 .services = { NULL }, 318 }; 319 320 static const struct qcom_pdm_domain_data mpss_root_pd = { 321 .domain = "msm/modem/root_pd", 322 .instance_id = 180, 323 .services = { 324 NULL, 325 }, 326 }; 327 328 static const struct qcom_pdm_domain_data mpss_root_pd_gps = { 329 .domain = "msm/modem/root_pd", 330 .instance_id = 180, 331 .services = { 332 "gps/gps_service", 333 NULL, 334 }, 335 }; 336 337 static const struct qcom_pdm_domain_data mpss_root_pd_gps_pdr = { 338 .domain = "msm/modem/root_pd", 339 .instance_id = 180, 340 .services = { 341 "gps/gps_service", 342 "tms/pdr_enabled", 343 NULL, 344 }, 345 }; 346 347 static const struct qcom_pdm_domain_data msm8996_mpss_root_pd = { 348 .domain = "msm/modem/root_pd", 349 .instance_id = 100, 350 .services = { NULL }, 351 }; 352 353 static const struct qcom_pdm_domain_data mpss_wlan_pd = { 354 .domain = "msm/modem/wlan_pd", 355 .instance_id = 180, 356 .services = { 357 "kernel/elf_loader", 358 "wlan/fw", 359 NULL, 360 }, 361 }; 362 363 static const struct qcom_pdm_domain_data *glymur_domains[] = { 364 &adsp_audio_pd, 365 &adsp_root_pd, 366 &adsp_sensor_pd, 367 &cdsp_root_pd, 368 NULL, 369 }; 370 371 static const struct qcom_pdm_domain_data *kaanapali_domains[] = { 372 &adsp_audio_pd, 373 &adsp_root_pd, 374 &adsp_sensor_pd, 375 &cdsp_root_pd, 376 &mpss_root_pd_gps, 377 NULL, 378 }; 379 380 static const struct qcom_pdm_domain_data *msm8996_domains[] = { 381 &msm8996_adsp_audio_pd, 382 &msm8996_adsp_root_pd, 383 &msm8996_mpss_root_pd, 384 NULL, 385 }; 386 387 static const struct qcom_pdm_domain_data *msm8998_domains[] = { 388 &mpss_root_pd, 389 &mpss_wlan_pd, 390 NULL, 391 }; 392 393 static const struct qcom_pdm_domain_data *qcm2290_domains[] = { 394 &adsp_audio_pd, 395 &adsp_root_pd, 396 &adsp_sensor_pd, 397 &mpss_root_pd_gps, 398 &mpss_wlan_pd, 399 NULL, 400 }; 401 402 static const struct qcom_pdm_domain_data *qcs404_domains[] = { 403 &adsp_audio_pd, 404 &adsp_root_pd, 405 &adsp_sensor_pd, 406 &cdsp_root_pd, 407 &mpss_root_pd, 408 &mpss_wlan_pd, 409 NULL, 410 }; 411 412 static const struct qcom_pdm_domain_data *qcs615_domains[] = { 413 &adsp_audio_pd, 414 &adsp_root_pd, 415 &adsp_sensor_pd, 416 &cdsp_root_pd, 417 &mpss_root_pd, 418 &mpss_wlan_pd, 419 NULL, 420 }; 421 422 static const struct qcom_pdm_domain_data *sc7180_domains[] = { 423 &adsp_audio_pd, 424 &adsp_root_pd_pdr, 425 &adsp_sensor_pd, 426 &mpss_root_pd_gps_pdr, 427 &mpss_wlan_pd, 428 NULL, 429 }; 430 431 static const struct qcom_pdm_domain_data *sc7280_domains[] = { 432 &adsp_audio_pd, 433 &adsp_root_pd_pdr, 434 &adsp_charger_pd, 435 &adsp_sensor_pd, 436 &cdsp_root_pd, 437 &mpss_root_pd_gps_pdr, 438 NULL, 439 }; 440 441 static const struct qcom_pdm_domain_data *sc8180x_domains[] = { 442 &adsp_audio_pd, 443 &adsp_root_pd, 444 &adsp_charger_pd, 445 &cdsp_root_pd, 446 &mpss_root_pd_gps, 447 &mpss_wlan_pd, 448 NULL, 449 }; 450 451 static const struct qcom_pdm_domain_data *sc8280xp_domains[] = { 452 &adsp_audio_pd, 453 &adsp_root_pd_pdr, 454 &adsp_charger_pd, 455 &cdsp_root_pd, 456 NULL, 457 }; 458 459 /* Unlike SDM660, SDM630/636 lack CDSP */ 460 static const struct qcom_pdm_domain_data *sdm630_domains[] = { 461 &adsp_audio_pd, 462 &adsp_root_pd, 463 &adsp_sensor_pd, 464 &mpss_root_pd, 465 &mpss_wlan_pd, 466 NULL, 467 }; 468 469 static const struct qcom_pdm_domain_data *sdm660_domains[] = { 470 &adsp_audio_pd, 471 &adsp_root_pd, 472 &adsp_sensor_pd, 473 &cdsp_root_pd, 474 &mpss_root_pd, 475 &mpss_wlan_pd, 476 NULL, 477 }; 478 479 static const struct qcom_pdm_domain_data *sdm670_domains[] = { 480 &adsp_audio_pd, 481 &adsp_root_pd, 482 &cdsp_root_pd, 483 &mpss_root_pd, 484 &mpss_wlan_pd, 485 NULL, 486 }; 487 488 static const struct qcom_pdm_domain_data *sdm845_domains[] = { 489 &adsp_audio_pd, 490 &adsp_root_pd, 491 &cdsp_root_pd, 492 &mpss_root_pd, 493 &mpss_wlan_pd, 494 &slpi_root_pd, 495 &slpi_sensor_pd, 496 NULL, 497 }; 498 499 static const struct qcom_pdm_domain_data *sm6115_domains[] = { 500 &adsp_audio_pd, 501 &adsp_root_pd, 502 &adsp_sensor_pd, 503 &cdsp_root_pd, 504 &mpss_root_pd_gps, 505 &mpss_wlan_pd, 506 NULL, 507 }; 508 509 static const struct qcom_pdm_domain_data *sm6350_domains[] = { 510 &adsp_audio_pd, 511 &adsp_root_pd, 512 &adsp_sensor_pd, 513 &cdsp_root_pd, 514 &mpss_wlan_pd, 515 NULL, 516 }; 517 518 static const struct qcom_pdm_domain_data *sm7150_domains[] = { 519 &adsp_audio_pd, 520 &adsp_root_pd, 521 &adsp_sensor_pd, 522 &cdsp_root_pd, 523 &mpss_root_pd_gps, 524 &mpss_wlan_pd, 525 NULL, 526 }; 527 528 static const struct qcom_pdm_domain_data *sm8150_domains[] = { 529 &adsp_audio_pd, 530 &adsp_root_pd, 531 &cdsp_root_pd, 532 &mpss_root_pd_gps, 533 &mpss_wlan_pd, 534 NULL, 535 }; 536 537 static const struct qcom_pdm_domain_data *sm8250_domains[] = { 538 &adsp_audio_pd, 539 &adsp_root_pd, 540 &cdsp_root_pd, 541 &slpi_root_pd, 542 &slpi_sensor_pd, 543 NULL, 544 }; 545 546 static const struct qcom_pdm_domain_data *sm8350_domains[] = { 547 &adsp_audio_pd, 548 &adsp_root_pd_pdr, 549 &adsp_charger_pd, 550 &cdsp_root_pd, 551 &mpss_root_pd_gps, 552 &slpi_root_pd, 553 &slpi_sensor_pd, 554 NULL, 555 }; 556 557 static const struct qcom_pdm_domain_data *sm8550_domains[] = { 558 &adsp_audio_pd, 559 &adsp_root_pd, 560 &adsp_charger_pd, 561 &adsp_sensor_pd, 562 &cdsp_root_pd, 563 &mpss_root_pd_gps, 564 NULL, 565 }; 566 567 static const struct qcom_pdm_domain_data *x1e80100_domains[] = { 568 &adsp_audio_pd, 569 &adsp_root_pd, 570 &adsp_charger_pd, 571 &adsp_sensor_pd, 572 &cdsp_root_pd, 573 NULL, 574 }; 575 576 static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { 577 { .compatible = "qcom,apq8016", .data = NULL, }, 578 { .compatible = "qcom,apq8064", .data = NULL, }, 579 { .compatible = "qcom,apq8074", .data = NULL, }, 580 { .compatible = "qcom,apq8084", .data = NULL, }, 581 { .compatible = "qcom,eliza", .data = sm8550_domains, }, 582 { .compatible = "qcom,apq8096", .data = msm8996_domains, }, 583 { .compatible = "qcom,glymur", .data = glymur_domains, }, 584 { .compatible = "qcom,kaanapali", .data = kaanapali_domains, }, 585 { .compatible = "qcom,mahua", .data = glymur_domains, }, 586 { .compatible = "qcom,milos", .data = sm8550_domains, }, 587 { .compatible = "qcom,msm8226", .data = NULL, }, 588 { .compatible = "qcom,msm8909", .data = NULL, }, 589 { .compatible = "qcom,msm8916", .data = NULL, }, 590 { .compatible = "qcom,msm8939", .data = NULL, }, 591 { .compatible = "qcom,msm8974", .data = NULL, }, 592 { .compatible = "qcom,msm8996", .data = msm8996_domains, }, 593 { .compatible = "qcom,msm8998", .data = msm8998_domains, }, 594 { .compatible = "qcom,qcm2290", .data = qcm2290_domains, }, 595 { .compatible = "qcom,qcm6490", .data = sc7280_domains, }, 596 { .compatible = "qcom,qcs404", .data = qcs404_domains, }, 597 { .compatible = "qcom,qcs615", .data = qcs615_domains, }, 598 { .compatible = "qcom,sc7180", .data = sc7180_domains, }, 599 { .compatible = "qcom,sc7280", .data = sc7280_domains, }, 600 { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, 601 { .compatible = "qcom,sc8280xp", .data = sc8280xp_domains, }, 602 { .compatible = "qcom,sdm630", .data = sdm630_domains, }, 603 { .compatible = "qcom,sdm636", .data = sdm630_domains, }, 604 { .compatible = "qcom,sda660", .data = sdm660_domains, }, 605 { .compatible = "qcom,sdm660", .data = sdm660_domains, }, 606 { .compatible = "qcom,sdm670", .data = sdm670_domains, }, 607 { .compatible = "qcom,sdm845", .data = sdm845_domains, }, 608 { .compatible = "qcom,sm4250", .data = sm6115_domains, }, 609 { .compatible = "qcom,sm6115", .data = sm6115_domains, }, 610 { .compatible = "qcom,sm6350", .data = sm6350_domains, }, 611 { .compatible = "qcom,sm7150", .data = sm7150_domains, }, 612 { .compatible = "qcom,sm7225", .data = sm6350_domains, }, 613 { .compatible = "qcom,sm7325", .data = sc7280_domains, }, 614 { .compatible = "qcom,sm8150", .data = sm8150_domains, }, 615 { .compatible = "qcom,sm8250", .data = sm8250_domains, }, 616 { .compatible = "qcom,sm8350", .data = sm8350_domains, }, 617 { .compatible = "qcom,sm8450", .data = sm8350_domains, }, 618 { .compatible = "qcom,sm8550", .data = sm8550_domains, }, 619 { .compatible = "qcom,sm8650", .data = sm8550_domains, }, 620 { .compatible = "qcom,sm8750", .data = sm8550_domains, }, 621 { .compatible = "qcom,x1e80100", .data = x1e80100_domains, }, 622 { .compatible = "qcom,x1p42100", .data = x1e80100_domains, }, 623 {}, 624 }; 625 626 static void qcom_pdm_stop(struct qcom_pdm_data *data) 627 { 628 qcom_pdm_free_domains(data); 629 630 /* The server is removed automatically */ 631 qmi_handle_release(&data->handle); 632 633 kfree(data); 634 } 635 636 static struct qcom_pdm_data *qcom_pdm_start(void) 637 { 638 const struct qcom_pdm_domain_data * const *domains; 639 const struct of_device_id *match; 640 struct qcom_pdm_data *data; 641 int ret, i; 642 643 match = of_machine_get_match(qcom_pdm_domains); 644 if (!match) { 645 pr_notice("PDM: no support for the platform, userspace daemon might be required.\n"); 646 return ERR_PTR(-ENODEV); 647 } 648 649 domains = match->data; 650 if (!domains) { 651 pr_debug("PDM: no domains\n"); 652 return ERR_PTR(-ENODEV); 653 } 654 655 data = kzalloc_obj(*data); 656 if (!data) 657 return ERR_PTR(-ENOMEM); 658 659 INIT_LIST_HEAD(&data->services); 660 661 ret = qmi_handle_init(&data->handle, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, 662 NULL, qcom_pdm_msg_handlers); 663 if (ret) { 664 kfree(data); 665 return ERR_PTR(ret); 666 } 667 668 refcount_set(&data->refcnt, 1); 669 670 for (i = 0; domains[i]; i++) { 671 ret = qcom_pdm_add_domain(data, domains[i]); 672 if (ret) 673 goto err_stop; 674 } 675 676 ret = qmi_add_server(&data->handle, QMI_SERVICE_ID_SERVREG_LOC, 677 SERVREG_QMI_VERSION, SERVREG_QMI_INSTANCE); 678 if (ret) { 679 pr_err("PDM: error adding server %d\n", ret); 680 goto err_stop; 681 } 682 683 return data; 684 685 err_stop: 686 qcom_pdm_stop(data); 687 688 return ERR_PTR(ret); 689 } 690 691 static int qcom_pdm_probe(struct auxiliary_device *auxdev, 692 const struct auxiliary_device_id *id) 693 694 { 695 struct qcom_pdm_data *data; 696 int ret = 0; 697 698 mutex_lock(&qcom_pdm_mutex); 699 700 if (!__qcom_pdm_data) { 701 data = qcom_pdm_start(); 702 703 if (IS_ERR(data)) 704 ret = PTR_ERR(data); 705 else 706 __qcom_pdm_data = data; 707 } else { 708 refcount_inc(&__qcom_pdm_data->refcnt); 709 } 710 711 auxiliary_set_drvdata(auxdev, __qcom_pdm_data); 712 713 mutex_unlock(&qcom_pdm_mutex); 714 715 return ret; 716 } 717 718 static void qcom_pdm_remove(struct auxiliary_device *auxdev) 719 { 720 struct qcom_pdm_data *data; 721 722 data = auxiliary_get_drvdata(auxdev); 723 if (!data) 724 return; 725 726 if (refcount_dec_and_mutex_lock(&data->refcnt, &qcom_pdm_mutex)) { 727 __qcom_pdm_data = NULL; 728 qcom_pdm_stop(data); 729 mutex_unlock(&qcom_pdm_mutex); 730 } 731 } 732 733 static const struct auxiliary_device_id qcom_pdm_table[] = { 734 { .name = "qcom_common.pd-mapper" }, 735 {}, 736 }; 737 MODULE_DEVICE_TABLE(auxiliary, qcom_pdm_table); 738 739 static struct auxiliary_driver qcom_pdm_drv = { 740 .name = "qcom-pdm-mapper", 741 .id_table = qcom_pdm_table, 742 .probe = qcom_pdm_probe, 743 .remove = qcom_pdm_remove, 744 }; 745 module_auxiliary_driver(qcom_pdm_drv); 746 747 MODULE_DESCRIPTION("Qualcomm Protection Domain Mapper"); 748 MODULE_LICENSE("GPL"); 749