diff options
Diffstat (limited to 'drivers/remoteproc/qcom_sysmon.c')
| -rw-r--r-- | drivers/remoteproc/qcom_sysmon.c | 82 |
1 files changed, 78 insertions, 4 deletions
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index e976a602b015..c231314eab66 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c | |||
| @@ -6,8 +6,9 @@ | |||
| 6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
| 7 | #include <linux/notifier.h> | 7 | #include <linux/notifier.h> |
| 8 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
| 9 | #include <linux/interrupt.h> | ||
| 9 | #include <linux/io.h> | 10 | #include <linux/io.h> |
| 10 | #include <linux/notifier.h> | 11 | #include <linux/of_irq.h> |
| 11 | #include <linux/of_platform.h> | 12 | #include <linux/of_platform.h> |
| 12 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 13 | #include <linux/remoteproc/qcom_rproc.h> | 14 | #include <linux/remoteproc/qcom_rproc.h> |
| @@ -25,6 +26,7 @@ struct qcom_sysmon { | |||
| 25 | 26 | ||
| 26 | const char *name; | 27 | const char *name; |
| 27 | 28 | ||
| 29 | int shutdown_irq; | ||
| 28 | int ssctl_version; | 30 | int ssctl_version; |
| 29 | int ssctl_instance; | 31 | int ssctl_instance; |
| 30 | 32 | ||
| @@ -34,6 +36,8 @@ struct qcom_sysmon { | |||
| 34 | 36 | ||
| 35 | struct rpmsg_endpoint *ept; | 37 | struct rpmsg_endpoint *ept; |
| 36 | struct completion comp; | 38 | struct completion comp; |
| 39 | struct completion ind_comp; | ||
| 40 | struct completion shutdown_comp; | ||
| 37 | struct mutex lock; | 41 | struct mutex lock; |
| 38 | 42 | ||
| 39 | bool ssr_ack; | 43 | bool ssr_ack; |
| @@ -137,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, | |||
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | #define SSCTL_SHUTDOWN_REQ 0x21 | 143 | #define SSCTL_SHUTDOWN_REQ 0x21 |
| 144 | #define SSCTL_SHUTDOWN_READY_IND 0x21 | ||
| 140 | #define SSCTL_SUBSYS_EVENT_REQ 0x23 | 145 | #define SSCTL_SUBSYS_EVENT_REQ 0x23 |
| 141 | 146 | ||
| 142 | #define SSCTL_MAX_MSG_LEN 7 | 147 | #define SSCTL_MAX_MSG_LEN 7 |
| @@ -252,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { | |||
| 252 | {} | 257 | {} |
| 253 | }; | 258 | }; |
| 254 | 259 | ||
| 260 | static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { | ||
| 261 | {} | ||
| 262 | }; | ||
| 263 | |||
| 264 | static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, | ||
| 265 | struct qmi_txn *txn, const void *data) | ||
| 266 | { | ||
| 267 | struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); | ||
| 268 | |||
| 269 | complete(&sysmon->ind_comp); | ||
| 270 | } | ||
| 271 | |||
| 272 | static struct qmi_msg_handler qmi_indication_handler[] = { | ||
| 273 | { | ||
| 274 | .type = QMI_INDICATION, | ||
| 275 | .msg_id = SSCTL_SHUTDOWN_READY_IND, | ||
| 276 | .ei = ssctl_shutdown_ind_ei, | ||
| 277 | .decoded_size = 0, | ||
| 278 | .fn = sysmon_ind_cb | ||
| 279 | }, | ||
| 280 | {} | ||
| 281 | }; | ||
| 282 | |||
| 255 | /** | 283 | /** |
| 256 | * ssctl_request_shutdown() - request shutdown via SSCTL QMI service | 284 | * ssctl_request_shutdown() - request shutdown via SSCTL QMI service |
| 257 | * @sysmon: sysmon context | 285 | * @sysmon: sysmon context |
| @@ -262,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) | |||
| 262 | struct qmi_txn txn; | 290 | struct qmi_txn txn; |
| 263 | int ret; | 291 | int ret; |
| 264 | 292 | ||
| 293 | reinit_completion(&sysmon->ind_comp); | ||
| 294 | reinit_completion(&sysmon->shutdown_comp); | ||
| 265 | ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); | 295 | ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); |
| 266 | if (ret < 0) { | 296 | if (ret < 0) { |
| 267 | dev_err(sysmon->dev, "failed to allocate QMI txn\n"); | 297 | dev_err(sysmon->dev, "failed to allocate QMI txn\n"); |
| @@ -283,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) | |||
| 283 | dev_err(sysmon->dev, "shutdown request failed\n"); | 313 | dev_err(sysmon->dev, "shutdown request failed\n"); |
| 284 | else | 314 | else |
| 285 | dev_dbg(sysmon->dev, "shutdown request completed\n"); | 315 | dev_dbg(sysmon->dev, "shutdown request completed\n"); |
| 316 | |||
| 317 | if (sysmon->shutdown_irq > 0) { | ||
| 318 | ret = wait_for_completion_timeout(&sysmon->shutdown_comp, | ||
| 319 | 10 * HZ); | ||
| 320 | if (!ret) { | ||
| 321 | ret = try_wait_for_completion(&sysmon->ind_comp); | ||
| 322 | if (!ret) | ||
| 323 | dev_err(sysmon->dev, | ||
| 324 | "timeout waiting for shutdown ack\n"); | ||
| 325 | } | ||
| 326 | } | ||
| 286 | } | 327 | } |
| 287 | 328 | ||
| 288 | /** | 329 | /** |
| @@ -432,6 +473,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, | |||
| 432 | return NOTIFY_DONE; | 473 | return NOTIFY_DONE; |
| 433 | } | 474 | } |
| 434 | 475 | ||
| 476 | static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) | ||
| 477 | { | ||
| 478 | struct qcom_sysmon *sysmon = data; | ||
| 479 | |||
| 480 | complete(&sysmon->shutdown_comp); | ||
| 481 | |||
| 482 | return IRQ_HANDLED; | ||
| 483 | } | ||
| 484 | |||
| 435 | /** | 485 | /** |
| 436 | * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc | 486 | * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc |
| 437 | * @rproc: rproc context to associate the subdev with | 487 | * @rproc: rproc context to associate the subdev with |
| @@ -449,7 +499,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, | |||
| 449 | 499 | ||
| 450 | sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); | 500 | sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); |
| 451 | if (!sysmon) | 501 | if (!sysmon) |
| 452 | return NULL; | 502 | return ERR_PTR(-ENOMEM); |
| 453 | 503 | ||
| 454 | sysmon->dev = rproc->dev.parent; | 504 | sysmon->dev = rproc->dev.parent; |
| 455 | sysmon->rproc = rproc; | 505 | sysmon->rproc = rproc; |
| @@ -458,13 +508,37 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, | |||
| 458 | sysmon->ssctl_instance = ssctl_instance; | 508 | sysmon->ssctl_instance = ssctl_instance; |
| 459 | 509 | ||
| 460 | init_completion(&sysmon->comp); | 510 | init_completion(&sysmon->comp); |
| 511 | init_completion(&sysmon->ind_comp); | ||
| 512 | init_completion(&sysmon->shutdown_comp); | ||
| 461 | mutex_init(&sysmon->lock); | 513 | mutex_init(&sysmon->lock); |
| 462 | 514 | ||
| 463 | ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); | 515 | sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, |
| 516 | "shutdown-ack"); | ||
| 517 | if (sysmon->shutdown_irq < 0) { | ||
| 518 | if (sysmon->shutdown_irq != -ENODATA) { | ||
| 519 | dev_err(sysmon->dev, | ||
| 520 | "failed to retrieve shutdown-ack IRQ\n"); | ||
| 521 | return ERR_PTR(sysmon->shutdown_irq); | ||
| 522 | } | ||
| 523 | } else { | ||
| 524 | ret = devm_request_threaded_irq(sysmon->dev, | ||
| 525 | sysmon->shutdown_irq, | ||
| 526 | NULL, sysmon_shutdown_interrupt, | ||
| 527 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
| 528 | "q6v5 shutdown-ack", sysmon); | ||
| 529 | if (ret) { | ||
| 530 | dev_err(sysmon->dev, | ||
| 531 | "failed to acquire shutdown-ack IRQ\n"); | ||
| 532 | return ERR_PTR(ret); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, | ||
| 537 | qmi_indication_handler); | ||
| 464 | if (ret < 0) { | 538 | if (ret < 0) { |
| 465 | dev_err(sysmon->dev, "failed to initialize qmi handle\n"); | 539 | dev_err(sysmon->dev, "failed to initialize qmi handle\n"); |
| 466 | kfree(sysmon); | 540 | kfree(sysmon); |
| 467 | return NULL; | 541 | return ERR_PTR(ret); |
| 468 | } | 542 | } |
| 469 | 543 | ||
| 470 | qmi_add_lookup(&sysmon->qmi, 43, 0, 0); | 544 | qmi_add_lookup(&sysmon->qmi, 43, 0, 0); |
