qcom_sysmon.c (19569B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2017, Linaro Ltd. 4 */ 5#include <linux/firmware.h> 6#include <linux/module.h> 7#include <linux/notifier.h> 8#include <linux/slab.h> 9#include <linux/interrupt.h> 10#include <linux/io.h> 11#include <linux/of_irq.h> 12#include <linux/of_platform.h> 13#include <linux/platform_device.h> 14#include <linux/remoteproc/qcom_rproc.h> 15#include <linux/rpmsg.h> 16 17#include "qcom_common.h" 18 19static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers); 20 21struct qcom_sysmon { 22 struct rproc_subdev subdev; 23 struct rproc *rproc; 24 25 int state; 26 struct mutex state_lock; 27 28 struct list_head node; 29 30 const char *name; 31 32 int shutdown_irq; 33 int ssctl_version; 34 int ssctl_instance; 35 36 struct notifier_block nb; 37 38 struct device *dev; 39 40 struct rpmsg_endpoint *ept; 41 struct completion comp; 42 struct completion ind_comp; 43 struct completion shutdown_comp; 44 struct mutex lock; 45 46 bool ssr_ack; 47 bool shutdown_acked; 48 49 struct qmi_handle qmi; 50 struct sockaddr_qrtr ssctl; 51}; 52 53enum { 54 SSCTL_SSR_EVENT_BEFORE_POWERUP, 55 SSCTL_SSR_EVENT_AFTER_POWERUP, 56 SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, 57 SSCTL_SSR_EVENT_AFTER_SHUTDOWN, 58}; 59 60static const char * const sysmon_state_string[] = { 61 [SSCTL_SSR_EVENT_BEFORE_POWERUP] = "before_powerup", 62 [SSCTL_SSR_EVENT_AFTER_POWERUP] = "after_powerup", 63 [SSCTL_SSR_EVENT_BEFORE_SHUTDOWN] = "before_shutdown", 64 [SSCTL_SSR_EVENT_AFTER_SHUTDOWN] = "after_shutdown", 65}; 66 67struct sysmon_event { 68 const char *subsys_name; 69 u32 ssr_event; 70}; 71 72static DEFINE_MUTEX(sysmon_lock); 73static LIST_HEAD(sysmon_list); 74 75/** 76 * sysmon_send_event() - send notification of other remote's SSR event 77 * @sysmon: sysmon context 78 * @event: sysmon event context 79 */ 80static void sysmon_send_event(struct qcom_sysmon *sysmon, 81 const struct sysmon_event *event) 82{ 83 char req[50]; 84 int len; 85 int ret; 86 87 len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name, 88 sysmon_state_string[event->ssr_event]); 89 if (len >= sizeof(req)) 90 return; 91 92 mutex_lock(&sysmon->lock); 93 reinit_completion(&sysmon->comp); 94 sysmon->ssr_ack = false; 95 96 ret = rpmsg_send(sysmon->ept, req, len); 97 if (ret < 0) { 98 dev_err(sysmon->dev, "failed to send sysmon event\n"); 99 goto out_unlock; 100 } 101 102 ret = wait_for_completion_timeout(&sysmon->comp, 103 msecs_to_jiffies(5000)); 104 if (!ret) { 105 dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 106 goto out_unlock; 107 } 108 109 if (!sysmon->ssr_ack) 110 dev_err(sysmon->dev, "unexpected response to sysmon event\n"); 111 112out_unlock: 113 mutex_unlock(&sysmon->lock); 114} 115 116/** 117 * sysmon_request_shutdown() - request graceful shutdown of remote 118 * @sysmon: sysmon context 119 * 120 * Return: boolean indicator of the remote processor acking the request 121 */ 122static bool sysmon_request_shutdown(struct qcom_sysmon *sysmon) 123{ 124 char *req = "ssr:shutdown"; 125 bool acked = false; 126 int ret; 127 128 mutex_lock(&sysmon->lock); 129 reinit_completion(&sysmon->comp); 130 sysmon->ssr_ack = false; 131 132 ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1); 133 if (ret < 0) { 134 dev_err(sysmon->dev, "send sysmon shutdown request failed\n"); 135 goto out_unlock; 136 } 137 138 ret = wait_for_completion_timeout(&sysmon->comp, 139 msecs_to_jiffies(5000)); 140 if (!ret) { 141 dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 142 goto out_unlock; 143 } 144 145 if (!sysmon->ssr_ack) 146 dev_err(sysmon->dev, 147 "unexpected response to sysmon shutdown request\n"); 148 else 149 acked = true; 150 151out_unlock: 152 mutex_unlock(&sysmon->lock); 153 154 return acked; 155} 156 157static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, 158 void *priv, u32 addr) 159{ 160 struct qcom_sysmon *sysmon = priv; 161 const char *ssr_ack = "ssr:ack"; 162 const int ssr_ack_len = strlen(ssr_ack) + 1; 163 164 if (!sysmon) 165 return -EINVAL; 166 167 if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len)) 168 sysmon->ssr_ack = true; 169 170 complete(&sysmon->comp); 171 172 return 0; 173} 174 175#define SSCTL_SHUTDOWN_REQ 0x21 176#define SSCTL_SHUTDOWN_READY_IND 0x21 177#define SSCTL_SUBSYS_EVENT_REQ 0x23 178 179#define SSCTL_MAX_MSG_LEN 7 180 181#define SSCTL_SUBSYS_NAME_LENGTH 15 182 183enum { 184 SSCTL_SSR_EVENT_FORCED, 185 SSCTL_SSR_EVENT_GRACEFUL, 186}; 187 188struct ssctl_shutdown_resp { 189 struct qmi_response_type_v01 resp; 190}; 191 192static struct qmi_elem_info ssctl_shutdown_resp_ei[] = { 193 { 194 .data_type = QMI_STRUCT, 195 .elem_len = 1, 196 .elem_size = sizeof(struct qmi_response_type_v01), 197 .array_type = NO_ARRAY, 198 .tlv_type = 0x02, 199 .offset = offsetof(struct ssctl_shutdown_resp, resp), 200 .ei_array = qmi_response_type_v01_ei, 201 }, 202 {} 203}; 204 205struct ssctl_subsys_event_req { 206 u8 subsys_name_len; 207 char subsys_name[SSCTL_SUBSYS_NAME_LENGTH]; 208 u32 event; 209 u8 evt_driven_valid; 210 u32 evt_driven; 211}; 212 213static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { 214 { 215 .data_type = QMI_DATA_LEN, 216 .elem_len = 1, 217 .elem_size = sizeof(uint8_t), 218 .array_type = NO_ARRAY, 219 .tlv_type = 0x01, 220 .offset = offsetof(struct ssctl_subsys_event_req, 221 subsys_name_len), 222 .ei_array = NULL, 223 }, 224 { 225 .data_type = QMI_UNSIGNED_1_BYTE, 226 .elem_len = SSCTL_SUBSYS_NAME_LENGTH, 227 .elem_size = sizeof(char), 228 .array_type = VAR_LEN_ARRAY, 229 .tlv_type = 0x01, 230 .offset = offsetof(struct ssctl_subsys_event_req, 231 subsys_name), 232 .ei_array = NULL, 233 }, 234 { 235 .data_type = QMI_SIGNED_4_BYTE_ENUM, 236 .elem_len = 1, 237 .elem_size = sizeof(uint32_t), 238 .array_type = NO_ARRAY, 239 .tlv_type = 0x02, 240 .offset = offsetof(struct ssctl_subsys_event_req, 241 event), 242 .ei_array = NULL, 243 }, 244 { 245 .data_type = QMI_OPT_FLAG, 246 .elem_len = 1, 247 .elem_size = sizeof(uint8_t), 248 .array_type = NO_ARRAY, 249 .tlv_type = 0x10, 250 .offset = offsetof(struct ssctl_subsys_event_req, 251 evt_driven_valid), 252 .ei_array = NULL, 253 }, 254 { 255 .data_type = QMI_SIGNED_4_BYTE_ENUM, 256 .elem_len = 1, 257 .elem_size = sizeof(uint32_t), 258 .array_type = NO_ARRAY, 259 .tlv_type = 0x10, 260 .offset = offsetof(struct ssctl_subsys_event_req, 261 evt_driven), 262 .ei_array = NULL, 263 }, 264 {} 265}; 266 267struct ssctl_subsys_event_resp { 268 struct qmi_response_type_v01 resp; 269}; 270 271static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { 272 { 273 .data_type = QMI_STRUCT, 274 .elem_len = 1, 275 .elem_size = sizeof(struct qmi_response_type_v01), 276 .array_type = NO_ARRAY, 277 .tlv_type = 0x02, 278 .offset = offsetof(struct ssctl_subsys_event_resp, 279 resp), 280 .ei_array = qmi_response_type_v01_ei, 281 }, 282 {} 283}; 284 285static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { 286 {} 287}; 288 289static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, 290 struct qmi_txn *txn, const void *data) 291{ 292 struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); 293 294 complete(&sysmon->ind_comp); 295} 296 297static const struct qmi_msg_handler qmi_indication_handler[] = { 298 { 299 .type = QMI_INDICATION, 300 .msg_id = SSCTL_SHUTDOWN_READY_IND, 301 .ei = ssctl_shutdown_ind_ei, 302 .decoded_size = 0, 303 .fn = sysmon_ind_cb 304 }, 305 {} 306}; 307 308static bool ssctl_request_shutdown_wait(struct qcom_sysmon *sysmon) 309{ 310 int ret; 311 312 ret = wait_for_completion_timeout(&sysmon->shutdown_comp, 10 * HZ); 313 if (ret) 314 return true; 315 316 ret = try_wait_for_completion(&sysmon->ind_comp); 317 if (ret) 318 return true; 319 320 dev_err(sysmon->dev, "timeout waiting for shutdown ack\n"); 321 return false; 322} 323 324/** 325 * ssctl_request_shutdown() - request shutdown via SSCTL QMI service 326 * @sysmon: sysmon context 327 * 328 * Return: boolean indicator of the remote processor acking the request 329 */ 330static bool ssctl_request_shutdown(struct qcom_sysmon *sysmon) 331{ 332 struct ssctl_shutdown_resp resp; 333 struct qmi_txn txn; 334 bool acked = false; 335 int ret; 336 337 reinit_completion(&sysmon->ind_comp); 338 reinit_completion(&sysmon->shutdown_comp); 339 ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); 340 if (ret < 0) { 341 dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 342 return false; 343 } 344 345 ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 346 SSCTL_SHUTDOWN_REQ, 0, NULL, NULL); 347 if (ret < 0) { 348 dev_err(sysmon->dev, "failed to send shutdown request\n"); 349 qmi_txn_cancel(&txn); 350 return false; 351 } 352 353 ret = qmi_txn_wait(&txn, 5 * HZ); 354 if (ret < 0) { 355 dev_err(sysmon->dev, "timeout waiting for shutdown response\n"); 356 } else if (resp.resp.result) { 357 dev_err(sysmon->dev, "shutdown request rejected\n"); 358 } else { 359 dev_dbg(sysmon->dev, "shutdown request completed\n"); 360 acked = true; 361 } 362 363 if (sysmon->shutdown_irq > 0) 364 return ssctl_request_shutdown_wait(sysmon); 365 366 return acked; 367} 368 369/** 370 * ssctl_send_event() - send notification of other remote's SSR event 371 * @sysmon: sysmon context 372 * @event: sysmon event context 373 */ 374static void ssctl_send_event(struct qcom_sysmon *sysmon, 375 const struct sysmon_event *event) 376{ 377 struct ssctl_subsys_event_resp resp; 378 struct ssctl_subsys_event_req req; 379 struct qmi_txn txn; 380 int ret; 381 382 memset(&resp, 0, sizeof(resp)); 383 ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp); 384 if (ret < 0) { 385 dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 386 return; 387 } 388 389 memset(&req, 0, sizeof(req)); 390 strlcpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name)); 391 req.subsys_name_len = strlen(req.subsys_name); 392 req.event = event->ssr_event; 393 req.evt_driven_valid = true; 394 req.evt_driven = SSCTL_SSR_EVENT_FORCED; 395 396 ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 397 SSCTL_SUBSYS_EVENT_REQ, 40, 398 ssctl_subsys_event_req_ei, &req); 399 if (ret < 0) { 400 dev_err(sysmon->dev, "failed to send subsystem event\n"); 401 qmi_txn_cancel(&txn); 402 return; 403 } 404 405 ret = qmi_txn_wait(&txn, 5 * HZ); 406 if (ret < 0) 407 dev_err(sysmon->dev, "timeout waiting for subsystem event response\n"); 408 else if (resp.resp.result) 409 dev_err(sysmon->dev, "subsystem event rejected\n"); 410 else 411 dev_dbg(sysmon->dev, "subsystem event accepted\n"); 412} 413 414/** 415 * ssctl_new_server() - QMI callback indicating a new service 416 * @qmi: QMI handle 417 * @svc: service information 418 * 419 * Return: 0 if we're interested in this service, -EINVAL otherwise. 420 */ 421static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc) 422{ 423 struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); 424 425 switch (svc->version) { 426 case 1: 427 if (svc->instance != 0) 428 return -EINVAL; 429 if (strcmp(sysmon->name, "modem")) 430 return -EINVAL; 431 break; 432 case 2: 433 if (svc->instance != sysmon->ssctl_instance) 434 return -EINVAL; 435 break; 436 default: 437 return -EINVAL; 438 } 439 440 sysmon->ssctl_version = svc->version; 441 442 sysmon->ssctl.sq_family = AF_QIPCRTR; 443 sysmon->ssctl.sq_node = svc->node; 444 sysmon->ssctl.sq_port = svc->port; 445 446 svc->priv = sysmon; 447 448 return 0; 449} 450 451/** 452 * ssctl_del_server() - QMI callback indicating that @svc is removed 453 * @qmi: QMI handle 454 * @svc: service information 455 */ 456static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc) 457{ 458 struct qcom_sysmon *sysmon = svc->priv; 459 460 sysmon->ssctl_version = 0; 461} 462 463static const struct qmi_ops ssctl_ops = { 464 .new_server = ssctl_new_server, 465 .del_server = ssctl_del_server, 466}; 467 468static int sysmon_prepare(struct rproc_subdev *subdev) 469{ 470 struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 471 subdev); 472 struct sysmon_event event = { 473 .subsys_name = sysmon->name, 474 .ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP 475 }; 476 477 mutex_lock(&sysmon->state_lock); 478 sysmon->state = SSCTL_SSR_EVENT_BEFORE_POWERUP; 479 blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 480 mutex_unlock(&sysmon->state_lock); 481 482 return 0; 483} 484 485/** 486 * sysmon_start() - start callback for the sysmon remoteproc subdevice 487 * @subdev: instance of the sysmon subdevice 488 * 489 * Inform all the listners of sysmon notifications that the rproc associated 490 * to @subdev has booted up. The rproc that booted up also needs to know 491 * which rprocs are already up and running, so send start notifications 492 * on behalf of all the online rprocs. 493 */ 494static int sysmon_start(struct rproc_subdev *subdev) 495{ 496 struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 497 subdev); 498 struct qcom_sysmon *target; 499 struct sysmon_event event = { 500 .subsys_name = sysmon->name, 501 .ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP 502 }; 503 504 mutex_lock(&sysmon->state_lock); 505 sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP; 506 blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 507 mutex_unlock(&sysmon->state_lock); 508 509 mutex_lock(&sysmon_lock); 510 list_for_each_entry(target, &sysmon_list, node) { 511 if (target == sysmon) 512 continue; 513 514 mutex_lock(&target->state_lock); 515 event.subsys_name = target->name; 516 event.ssr_event = target->state; 517 518 if (sysmon->ssctl_version == 2) 519 ssctl_send_event(sysmon, &event); 520 else if (sysmon->ept) 521 sysmon_send_event(sysmon, &event); 522 mutex_unlock(&target->state_lock); 523 } 524 mutex_unlock(&sysmon_lock); 525 526 return 0; 527} 528 529static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) 530{ 531 struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); 532 struct sysmon_event event = { 533 .subsys_name = sysmon->name, 534 .ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN 535 }; 536 537 sysmon->shutdown_acked = false; 538 539 mutex_lock(&sysmon->state_lock); 540 sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; 541 blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 542 mutex_unlock(&sysmon->state_lock); 543 544 /* Don't request graceful shutdown if we've crashed */ 545 if (crashed) 546 return; 547 548 if (sysmon->ssctl_version) 549 sysmon->shutdown_acked = ssctl_request_shutdown(sysmon); 550 else if (sysmon->ept) 551 sysmon->shutdown_acked = sysmon_request_shutdown(sysmon); 552} 553 554static void sysmon_unprepare(struct rproc_subdev *subdev) 555{ 556 struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 557 subdev); 558 struct sysmon_event event = { 559 .subsys_name = sysmon->name, 560 .ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN 561 }; 562 563 mutex_lock(&sysmon->state_lock); 564 sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN; 565 blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 566 mutex_unlock(&sysmon->state_lock); 567} 568 569/** 570 * sysmon_notify() - notify sysmon target of another's SSR 571 * @nb: notifier_block associated with sysmon instance 572 * @event: unused 573 * @data: SSR identifier of the remote that is going down 574 */ 575static int sysmon_notify(struct notifier_block *nb, unsigned long event, 576 void *data) 577{ 578 struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); 579 struct sysmon_event *sysmon_event = data; 580 581 /* Skip non-running rprocs and the originating instance */ 582 if (sysmon->state != SSCTL_SSR_EVENT_AFTER_POWERUP || 583 !strcmp(sysmon_event->subsys_name, sysmon->name)) { 584 dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); 585 return NOTIFY_DONE; 586 } 587 588 /* Only SSCTL version 2 supports SSR events */ 589 if (sysmon->ssctl_version == 2) 590 ssctl_send_event(sysmon, sysmon_event); 591 else if (sysmon->ept) 592 sysmon_send_event(sysmon, sysmon_event); 593 594 return NOTIFY_DONE; 595} 596 597static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) 598{ 599 struct qcom_sysmon *sysmon = data; 600 601 complete(&sysmon->shutdown_comp); 602 603 return IRQ_HANDLED; 604} 605 606/** 607 * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc 608 * @rproc: rproc context to associate the subdev with 609 * @name: name of this subdev, to use in SSR 610 * @ssctl_instance: instance id of the ssctl QMI service 611 * 612 * Return: A new qcom_sysmon object, or NULL on failure 613 */ 614struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, 615 const char *name, 616 int ssctl_instance) 617{ 618 struct qcom_sysmon *sysmon; 619 int ret; 620 621 sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); 622 if (!sysmon) 623 return ERR_PTR(-ENOMEM); 624 625 sysmon->dev = rproc->dev.parent; 626 sysmon->rproc = rproc; 627 628 sysmon->name = name; 629 sysmon->ssctl_instance = ssctl_instance; 630 631 init_completion(&sysmon->comp); 632 init_completion(&sysmon->ind_comp); 633 init_completion(&sysmon->shutdown_comp); 634 mutex_init(&sysmon->lock); 635 mutex_init(&sysmon->state_lock); 636 637 sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, 638 "shutdown-ack"); 639 if (sysmon->shutdown_irq < 0) { 640 if (sysmon->shutdown_irq != -ENODATA) { 641 dev_err(sysmon->dev, 642 "failed to retrieve shutdown-ack IRQ\n"); 643 return ERR_PTR(sysmon->shutdown_irq); 644 } 645 } else { 646 ret = devm_request_threaded_irq(sysmon->dev, 647 sysmon->shutdown_irq, 648 NULL, sysmon_shutdown_interrupt, 649 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 650 "q6v5 shutdown-ack", sysmon); 651 if (ret) { 652 dev_err(sysmon->dev, 653 "failed to acquire shutdown-ack IRQ\n"); 654 return ERR_PTR(ret); 655 } 656 } 657 658 ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, 659 qmi_indication_handler); 660 if (ret < 0) { 661 dev_err(sysmon->dev, "failed to initialize qmi handle\n"); 662 kfree(sysmon); 663 return ERR_PTR(ret); 664 } 665 666 qmi_add_lookup(&sysmon->qmi, 43, 0, 0); 667 668 sysmon->subdev.prepare = sysmon_prepare; 669 sysmon->subdev.start = sysmon_start; 670 sysmon->subdev.stop = sysmon_stop; 671 sysmon->subdev.unprepare = sysmon_unprepare; 672 673 rproc_add_subdev(rproc, &sysmon->subdev); 674 675 sysmon->nb.notifier_call = sysmon_notify; 676 blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); 677 678 mutex_lock(&sysmon_lock); 679 list_add(&sysmon->node, &sysmon_list); 680 mutex_unlock(&sysmon_lock); 681 682 return sysmon; 683} 684EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev); 685 686/** 687 * qcom_remove_sysmon_subdev() - release a qcom_sysmon 688 * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev() 689 */ 690void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) 691{ 692 if (!sysmon) 693 return; 694 695 mutex_lock(&sysmon_lock); 696 list_del(&sysmon->node); 697 mutex_unlock(&sysmon_lock); 698 699 blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb); 700 701 rproc_remove_subdev(sysmon->rproc, &sysmon->subdev); 702 703 qmi_handle_release(&sysmon->qmi); 704 705 kfree(sysmon); 706} 707EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev); 708 709/** 710 * qcom_sysmon_shutdown_acked() - query the success of the last shutdown 711 * @sysmon: sysmon context 712 * 713 * When sysmon is used to request a graceful shutdown of the remote processor 714 * this can be used by the remoteproc driver to query the success, in order to 715 * know if it should fall back to other means of requesting a shutdown. 716 * 717 * Return: boolean indicator of the success of the last shutdown request 718 */ 719bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon) 720{ 721 return sysmon && sysmon->shutdown_acked; 722} 723EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked); 724 725/** 726 * sysmon_probe() - probe sys_mon channel 727 * @rpdev: rpmsg device handle 728 * 729 * Find the sysmon context associated with the ancestor remoteproc and assign 730 * this rpmsg device with said sysmon context. 731 * 732 * Return: 0 on success, negative errno on failure. 733 */ 734static int sysmon_probe(struct rpmsg_device *rpdev) 735{ 736 struct qcom_sysmon *sysmon; 737 struct rproc *rproc; 738 739 rproc = rproc_get_by_child(&rpdev->dev); 740 if (!rproc) { 741 dev_err(&rpdev->dev, "sysmon device not child of rproc\n"); 742 return -EINVAL; 743 } 744 745 mutex_lock(&sysmon_lock); 746 list_for_each_entry(sysmon, &sysmon_list, node) { 747 if (sysmon->rproc == rproc) 748 goto found; 749 } 750 mutex_unlock(&sysmon_lock); 751 752 dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n"); 753 754 return -EINVAL; 755 756found: 757 mutex_unlock(&sysmon_lock); 758 759 rpdev->ept->priv = sysmon; 760 sysmon->ept = rpdev->ept; 761 762 return 0; 763} 764 765/** 766 * sysmon_remove() - sys_mon channel remove handler 767 * @rpdev: rpmsg device handle 768 * 769 * Disassociate the rpmsg device with the sysmon instance. 770 */ 771static void sysmon_remove(struct rpmsg_device *rpdev) 772{ 773 struct qcom_sysmon *sysmon = rpdev->ept->priv; 774 775 sysmon->ept = NULL; 776} 777 778static const struct rpmsg_device_id sysmon_match[] = { 779 { "sys_mon" }, 780 {} 781}; 782 783static struct rpmsg_driver sysmon_driver = { 784 .probe = sysmon_probe, 785 .remove = sysmon_remove, 786 .callback = sysmon_callback, 787 .id_table = sysmon_match, 788 .drv = { 789 .name = "qcom_sysmon", 790 }, 791}; 792 793module_rpmsg_driver(sysmon_driver); 794 795MODULE_DESCRIPTION("Qualcomm sysmon driver"); 796MODULE_LICENSE("GPL v2");