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