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(sizeof(*service), GFP_KERNEL); 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(sizeof(*domain), GFP_KERNEL); 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(sizeof(*rsp), GFP_KERNEL); 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 *msm8996_domains[] = { 364 &msm8996_adsp_audio_pd, 365 &msm8996_adsp_root_pd, 366 &msm8996_mpss_root_pd, 367 NULL, 368 }; 369 370 static const struct qcom_pdm_domain_data *msm8998_domains[] = { 371 &mpss_root_pd, 372 &mpss_wlan_pd, 373 NULL, 374 }; 375 376 static const struct qcom_pdm_domain_data *qcm2290_domains[] = { 377 &adsp_audio_pd, 378 &adsp_root_pd, 379 &adsp_sensor_pd, 380 &mpss_root_pd_gps, 381 &mpss_wlan_pd, 382 NULL, 383 }; 384 385 static const struct qcom_pdm_domain_data *qcs404_domains[] = { 386 &adsp_audio_pd, 387 &adsp_root_pd, 388 &adsp_sensor_pd, 389 &cdsp_root_pd, 390 &mpss_root_pd, 391 &mpss_wlan_pd, 392 NULL, 393 }; 394 395 static const struct qcom_pdm_domain_data *sc7180_domains[] = { 396 &adsp_audio_pd, 397 &adsp_root_pd_pdr, 398 &adsp_sensor_pd, 399 &mpss_root_pd_gps_pdr, 400 &mpss_wlan_pd, 401 NULL, 402 }; 403 404 static const struct qcom_pdm_domain_data *sc7280_domains[] = { 405 &adsp_audio_pd, 406 &adsp_root_pd_pdr, 407 &adsp_charger_pd, 408 &adsp_sensor_pd, 409 &cdsp_root_pd, 410 &mpss_root_pd_gps_pdr, 411 NULL, 412 }; 413 414 static const struct qcom_pdm_domain_data *sc8180x_domains[] = { 415 &adsp_audio_pd, 416 &adsp_root_pd, 417 &adsp_charger_pd, 418 &cdsp_root_pd, 419 &mpss_root_pd_gps, 420 &mpss_wlan_pd, 421 NULL, 422 }; 423 424 static const struct qcom_pdm_domain_data *sc8280xp_domains[] = { 425 &adsp_audio_pd, 426 &adsp_root_pd_pdr, 427 &adsp_charger_pd, 428 &cdsp_root_pd, 429 NULL, 430 }; 431 432 static const struct qcom_pdm_domain_data *sdm660_domains[] = { 433 &adsp_audio_pd, 434 &adsp_root_pd, 435 &adsp_sensor_pd, 436 &cdsp_root_pd, 437 &mpss_root_pd, 438 &mpss_wlan_pd, 439 NULL, 440 }; 441 442 static const struct qcom_pdm_domain_data *sdm670_domains[] = { 443 &adsp_audio_pd, 444 &adsp_root_pd, 445 &cdsp_root_pd, 446 &mpss_root_pd, 447 &mpss_wlan_pd, 448 NULL, 449 }; 450 451 static const struct qcom_pdm_domain_data *sdm845_domains[] = { 452 &adsp_audio_pd, 453 &adsp_root_pd, 454 &cdsp_root_pd, 455 &mpss_root_pd, 456 &mpss_wlan_pd, 457 &slpi_root_pd, 458 &slpi_sensor_pd, 459 NULL, 460 }; 461 462 static const struct qcom_pdm_domain_data *sm6115_domains[] = { 463 &adsp_audio_pd, 464 &adsp_root_pd, 465 &adsp_sensor_pd, 466 &cdsp_root_pd, 467 &mpss_root_pd_gps, 468 &mpss_wlan_pd, 469 NULL, 470 }; 471 472 static const struct qcom_pdm_domain_data *sm6350_domains[] = { 473 &adsp_audio_pd, 474 &adsp_root_pd, 475 &adsp_sensor_pd, 476 &cdsp_root_pd, 477 &mpss_wlan_pd, 478 NULL, 479 }; 480 481 static const struct qcom_pdm_domain_data *sm8150_domains[] = { 482 &adsp_audio_pd, 483 &adsp_root_pd, 484 &cdsp_root_pd, 485 &mpss_root_pd_gps, 486 &mpss_wlan_pd, 487 NULL, 488 }; 489 490 static const struct qcom_pdm_domain_data *sm8250_domains[] = { 491 &adsp_audio_pd, 492 &adsp_root_pd, 493 &cdsp_root_pd, 494 &slpi_root_pd, 495 &slpi_sensor_pd, 496 NULL, 497 }; 498 499 static const struct qcom_pdm_domain_data *sm8350_domains[] = { 500 &adsp_audio_pd, 501 &adsp_root_pd_pdr, 502 &adsp_charger_pd, 503 &cdsp_root_pd, 504 &mpss_root_pd_gps, 505 &slpi_root_pd, 506 &slpi_sensor_pd, 507 NULL, 508 }; 509 510 static const struct qcom_pdm_domain_data *sm8550_domains[] = { 511 &adsp_audio_pd, 512 &adsp_root_pd, 513 &adsp_charger_pd, 514 &adsp_sensor_pd, 515 &cdsp_root_pd, 516 &mpss_root_pd_gps, 517 NULL, 518 }; 519 520 static const struct qcom_pdm_domain_data *x1e80100_domains[] = { 521 &adsp_audio_pd, 522 &adsp_root_pd, 523 &adsp_charger_pd, 524 &adsp_sensor_pd, 525 &cdsp_root_pd, 526 NULL, 527 }; 528 529 static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { 530 { .compatible = "qcom,apq8016", .data = NULL, }, 531 { .compatible = "qcom,apq8064", .data = NULL, }, 532 { .compatible = "qcom,apq8074", .data = NULL, }, 533 { .compatible = "qcom,apq8084", .data = NULL, }, 534 { .compatible = "qcom,apq8096", .data = msm8996_domains, }, 535 { .compatible = "qcom,msm8226", .data = NULL, }, 536 { .compatible = "qcom,msm8909", .data = NULL, }, 537 { .compatible = "qcom,msm8916", .data = NULL, }, 538 { .compatible = "qcom,msm8939", .data = NULL, }, 539 { .compatible = "qcom,msm8974", .data = NULL, }, 540 { .compatible = "qcom,msm8996", .data = msm8996_domains, }, 541 { .compatible = "qcom,msm8998", .data = msm8998_domains, }, 542 { .compatible = "qcom,qcm2290", .data = qcm2290_domains, }, 543 { .compatible = "qcom,qcs404", .data = qcs404_domains, }, 544 { .compatible = "qcom,sc7180", .data = sc7180_domains, }, 545 { .compatible = "qcom,sc7280", .data = sc7280_domains, }, 546 { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, 547 { .compatible = "qcom,sc8280xp", .data = sc8280xp_domains, }, 548 { .compatible = "qcom,sda660", .data = sdm660_domains, }, 549 { .compatible = "qcom,sdm660", .data = sdm660_domains, }, 550 { .compatible = "qcom,sdm670", .data = sdm670_domains, }, 551 { .compatible = "qcom,sdm845", .data = sdm845_domains, }, 552 { .compatible = "qcom,sm4250", .data = sm6115_domains, }, 553 { .compatible = "qcom,sm6115", .data = sm6115_domains, }, 554 { .compatible = "qcom,sm6350", .data = sm6350_domains, }, 555 { .compatible = "qcom,sm7325", .data = sc7280_domains, }, 556 { .compatible = "qcom,sm8150", .data = sm8150_domains, }, 557 { .compatible = "qcom,sm8250", .data = sm8250_domains, }, 558 { .compatible = "qcom,sm8350", .data = sm8350_domains, }, 559 { .compatible = "qcom,sm8450", .data = sm8350_domains, }, 560 { .compatible = "qcom,sm8550", .data = sm8550_domains, }, 561 { .compatible = "qcom,sm8650", .data = sm8550_domains, }, 562 { .compatible = "qcom,x1e80100", .data = x1e80100_domains, }, 563 {}, 564 }; 565 566 static void qcom_pdm_stop(struct qcom_pdm_data *data) 567 { 568 qcom_pdm_free_domains(data); 569 570 /* The server is removed automatically */ 571 qmi_handle_release(&data->handle); 572 573 kfree(data); 574 } 575 576 static struct qcom_pdm_data *qcom_pdm_start(void) 577 { 578 const struct qcom_pdm_domain_data * const *domains; 579 const struct of_device_id *match; 580 struct qcom_pdm_data *data; 581 struct device_node *root; 582 int ret, i; 583 584 root = of_find_node_by_path("/"); 585 if (!root) 586 return ERR_PTR(-ENODEV); 587 588 match = of_match_node(qcom_pdm_domains, root); 589 of_node_put(root); 590 if (!match) { 591 pr_notice("PDM: no support for the platform, userspace daemon might be required.\n"); 592 return ERR_PTR(-ENODEV); 593 } 594 595 domains = match->data; 596 if (!domains) { 597 pr_debug("PDM: no domains\n"); 598 return ERR_PTR(-ENODEV); 599 } 600 601 data = kzalloc(sizeof(*data), GFP_KERNEL); 602 if (!data) 603 return ERR_PTR(-ENOMEM); 604 605 INIT_LIST_HEAD(&data->services); 606 607 ret = qmi_handle_init(&data->handle, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, 608 NULL, qcom_pdm_msg_handlers); 609 if (ret) { 610 kfree(data); 611 return ERR_PTR(ret); 612 } 613 614 refcount_set(&data->refcnt, 1); 615 616 for (i = 0; domains[i]; i++) { 617 ret = qcom_pdm_add_domain(data, domains[i]); 618 if (ret) 619 goto err_stop; 620 } 621 622 ret = qmi_add_server(&data->handle, SERVREG_LOCATOR_SERVICE, 623 SERVREG_QMI_VERSION, SERVREG_QMI_INSTANCE); 624 if (ret) { 625 pr_err("PDM: error adding server %d\n", ret); 626 goto err_stop; 627 } 628 629 return data; 630 631 err_stop: 632 qcom_pdm_stop(data); 633 634 return ERR_PTR(ret); 635 } 636 637 static int qcom_pdm_probe(struct auxiliary_device *auxdev, 638 const struct auxiliary_device_id *id) 639 640 { 641 struct qcom_pdm_data *data; 642 int ret = 0; 643 644 mutex_lock(&qcom_pdm_mutex); 645 646 if (!__qcom_pdm_data) { 647 data = qcom_pdm_start(); 648 649 if (IS_ERR(data)) 650 ret = PTR_ERR(data); 651 else 652 __qcom_pdm_data = data; 653 } else { 654 refcount_inc(&__qcom_pdm_data->refcnt); 655 } 656 657 auxiliary_set_drvdata(auxdev, __qcom_pdm_data); 658 659 mutex_unlock(&qcom_pdm_mutex); 660 661 return ret; 662 } 663 664 static void qcom_pdm_remove(struct auxiliary_device *auxdev) 665 { 666 struct qcom_pdm_data *data; 667 668 data = auxiliary_get_drvdata(auxdev); 669 if (!data) 670 return; 671 672 if (refcount_dec_and_mutex_lock(&data->refcnt, &qcom_pdm_mutex)) { 673 __qcom_pdm_data = NULL; 674 qcom_pdm_stop(data); 675 mutex_unlock(&qcom_pdm_mutex); 676 } 677 } 678 679 static const struct auxiliary_device_id qcom_pdm_table[] = { 680 { .name = "qcom_common.pd-mapper" }, 681 {}, 682 }; 683 MODULE_DEVICE_TABLE(auxiliary, qcom_pdm_table); 684 685 static struct auxiliary_driver qcom_pdm_drv = { 686 .name = "qcom-pdm-mapper", 687 .id_table = qcom_pdm_table, 688 .probe = qcom_pdm_probe, 689 .remove = qcom_pdm_remove, 690 }; 691 module_auxiliary_driver(qcom_pdm_drv); 692 693 MODULE_DESCRIPTION("Qualcomm Protection Domain Mapper"); 694 MODULE_LICENSE("GPL"); 695