linux/drivers/remoteproc/qcom_sysmon.c
<<
>>
Prefs
   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        struct list_head node;
  26
  27        const char *name;
  28
  29        int shutdown_irq;
  30        int ssctl_version;
  31        int ssctl_instance;
  32
  33        struct notifier_block nb;
  34
  35        struct device *dev;
  36
  37        struct rpmsg_endpoint *ept;
  38        struct completion comp;
  39        struct completion ind_comp;
  40        struct completion shutdown_comp;
  41        struct mutex lock;
  42
  43        bool ssr_ack;
  44
  45        struct qmi_handle qmi;
  46        struct sockaddr_qrtr ssctl;
  47};
  48
  49static DEFINE_MUTEX(sysmon_lock);
  50static LIST_HEAD(sysmon_list);
  51
  52/**
  53 * sysmon_send_event() - send notification of other remote's SSR event
  54 * @sysmon:     sysmon context
  55 * @name:       other remote's name
  56 */
  57static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name)
  58{
  59        char req[50];
  60        int len;
  61        int ret;
  62
  63        len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name);
  64        if (len >= sizeof(req))
  65                return;
  66
  67        mutex_lock(&sysmon->lock);
  68        reinit_completion(&sysmon->comp);
  69        sysmon->ssr_ack = false;
  70
  71        ret = rpmsg_send(sysmon->ept, req, len);
  72        if (ret < 0) {
  73                dev_err(sysmon->dev, "failed to send sysmon event\n");
  74                goto out_unlock;
  75        }
  76
  77        ret = wait_for_completion_timeout(&sysmon->comp,
  78                                          msecs_to_jiffies(5000));
  79        if (!ret) {
  80                dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
  81                goto out_unlock;
  82        }
  83
  84        if (!sysmon->ssr_ack)
  85                dev_err(sysmon->dev, "unexpected response to sysmon event\n");
  86
  87out_unlock:
  88        mutex_unlock(&sysmon->lock);
  89}
  90
  91/**
  92 * sysmon_request_shutdown() - request graceful shutdown of remote
  93 * @sysmon:     sysmon context
  94 */
  95static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
  96{
  97        char *req = "ssr:shutdown";
  98        int ret;
  99
 100        mutex_lock(&sysmon->lock);
 101        reinit_completion(&sysmon->comp);
 102        sysmon->ssr_ack = false;
 103
 104        ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
 105        if (ret < 0) {
 106                dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
 107                goto out_unlock;
 108        }
 109
 110        ret = wait_for_completion_timeout(&sysmon->comp,
 111                                          msecs_to_jiffies(5000));
 112        if (!ret) {
 113                dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
 114                goto out_unlock;
 115        }
 116
 117        if (!sysmon->ssr_ack)
 118                dev_err(sysmon->dev,
 119                        "unexpected response to sysmon shutdown request\n");
 120
 121out_unlock:
 122        mutex_unlock(&sysmon->lock);
 123}
 124
 125static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
 126                           void *priv, u32 addr)
 127{
 128        struct qcom_sysmon *sysmon = priv;
 129        const char *ssr_ack = "ssr:ack";
 130        const int ssr_ack_len = strlen(ssr_ack) + 1;
 131
 132        if (!sysmon)
 133                return -EINVAL;
 134
 135        if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
 136                sysmon->ssr_ack = true;
 137
 138        complete(&sysmon->comp);
 139
 140        return 0;
 141}
 142
 143#define SSCTL_SHUTDOWN_REQ              0x21
 144#define SSCTL_SHUTDOWN_READY_IND        0x21
 145#define SSCTL_SUBSYS_EVENT_REQ          0x23
 146
 147#define SSCTL_MAX_MSG_LEN               7
 148
 149#define SSCTL_SUBSYS_NAME_LENGTH        15
 150
 151enum {
 152        SSCTL_SSR_EVENT_BEFORE_POWERUP,
 153        SSCTL_SSR_EVENT_AFTER_POWERUP,
 154        SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
 155        SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
 156};
 157
 158enum {
 159        SSCTL_SSR_EVENT_FORCED,
 160        SSCTL_SSR_EVENT_GRACEFUL,
 161};
 162
 163struct ssctl_shutdown_resp {
 164        struct qmi_response_type_v01 resp;
 165};
 166
 167static struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
 168        {
 169                .data_type      = QMI_STRUCT,
 170                .elem_len       = 1,
 171                .elem_size      = sizeof(struct qmi_response_type_v01),
 172                .array_type     = NO_ARRAY,
 173                .tlv_type       = 0x02,
 174                .offset         = offsetof(struct ssctl_shutdown_resp, resp),
 175                .ei_array       = qmi_response_type_v01_ei,
 176        },
 177        {}
 178};
 179
 180struct ssctl_subsys_event_req {
 181        u8 subsys_name_len;
 182        char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
 183        u32 event;
 184        u8 evt_driven_valid;
 185        u32 evt_driven;
 186};
 187
 188static struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
 189        {
 190                .data_type      = QMI_DATA_LEN,
 191                .elem_len       = 1,
 192                .elem_size      = sizeof(uint8_t),
 193                .array_type     = NO_ARRAY,
 194                .tlv_type       = 0x01,
 195                .offset         = offsetof(struct ssctl_subsys_event_req,
 196                                           subsys_name_len),
 197                .ei_array       = NULL,
 198        },
 199        {
 200                .data_type      = QMI_UNSIGNED_1_BYTE,
 201                .elem_len       = SSCTL_SUBSYS_NAME_LENGTH,
 202                .elem_size      = sizeof(char),
 203                .array_type     = VAR_LEN_ARRAY,
 204                .tlv_type       = 0x01,
 205                .offset         = offsetof(struct ssctl_subsys_event_req,
 206                                           subsys_name),
 207                .ei_array       = NULL,
 208        },
 209        {
 210                .data_type      = QMI_SIGNED_4_BYTE_ENUM,
 211                .elem_len       = 1,
 212                .elem_size      = sizeof(uint32_t),
 213                .array_type     = NO_ARRAY,
 214                .tlv_type       = 0x02,
 215                .offset         = offsetof(struct ssctl_subsys_event_req,
 216                                           event),
 217                .ei_array       = NULL,
 218        },
 219        {
 220                .data_type      = QMI_OPT_FLAG,
 221                .elem_len       = 1,
 222                .elem_size      = sizeof(uint8_t),
 223                .array_type     = NO_ARRAY,
 224                .tlv_type       = 0x10,
 225                .offset         = offsetof(struct ssctl_subsys_event_req,
 226                                           evt_driven_valid),
 227                .ei_array       = NULL,
 228        },
 229        {
 230                .data_type      = QMI_SIGNED_4_BYTE_ENUM,
 231                .elem_len       = 1,
 232                .elem_size      = sizeof(uint32_t),
 233                .array_type     = NO_ARRAY,
 234                .tlv_type       = 0x10,
 235                .offset         = offsetof(struct ssctl_subsys_event_req,
 236                                           evt_driven),
 237                .ei_array       = NULL,
 238        },
 239        {}
 240};
 241
 242struct ssctl_subsys_event_resp {
 243        struct qmi_response_type_v01 resp;
 244};
 245
 246static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
 247        {
 248                .data_type      = QMI_STRUCT,
 249                .elem_len       = 1,
 250                .elem_size      = sizeof(struct qmi_response_type_v01),
 251                .array_type     = NO_ARRAY,
 252                .tlv_type       = 0x02,
 253                .offset         = offsetof(struct ssctl_subsys_event_resp,
 254                                           resp),
 255                .ei_array       = qmi_response_type_v01_ei,
 256        },
 257        {}
 258};
 259
 260static struct qmi_elem_info ssctl_shutdown_ind_ei[] = {
 261        {}
 262};
 263
 264static 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
 272static 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
 283/**
 284 * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
 285 * @sysmon:     sysmon context
 286 */
 287static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
 288{
 289        struct ssctl_shutdown_resp resp;
 290        struct qmi_txn txn;
 291        int ret;
 292
 293        reinit_completion(&sysmon->ind_comp);
 294        reinit_completion(&sysmon->shutdown_comp);
 295        ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
 296        if (ret < 0) {
 297                dev_err(sysmon->dev, "failed to allocate QMI txn\n");
 298                return;
 299        }
 300
 301        ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
 302                               SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
 303        if (ret < 0) {
 304                dev_err(sysmon->dev, "failed to send shutdown request\n");
 305                qmi_txn_cancel(&txn);
 306                return;
 307        }
 308
 309        ret = qmi_txn_wait(&txn, 5 * HZ);
 310        if (ret < 0)
 311                dev_err(sysmon->dev, "failed receiving QMI response\n");
 312        else if (resp.resp.result)
 313                dev_err(sysmon->dev, "shutdown request failed\n");
 314        else
 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        }
 327}
 328
 329/**
 330 * ssctl_send_event() - send notification of other remote's SSR event
 331 * @sysmon:     sysmon context
 332 * @name:       other remote's name
 333 */
 334static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name)
 335{
 336        struct ssctl_subsys_event_resp resp;
 337        struct ssctl_subsys_event_req req;
 338        struct qmi_txn txn;
 339        int ret;
 340
 341        memset(&resp, 0, sizeof(resp));
 342        ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
 343        if (ret < 0) {
 344                dev_err(sysmon->dev, "failed to allocate QMI txn\n");
 345                return;
 346        }
 347
 348        memset(&req, 0, sizeof(req));
 349        strlcpy(req.subsys_name, name, sizeof(req.subsys_name));
 350        req.subsys_name_len = strlen(req.subsys_name);
 351        req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
 352        req.evt_driven_valid = true;
 353        req.evt_driven = SSCTL_SSR_EVENT_FORCED;
 354
 355        ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
 356                               SSCTL_SUBSYS_EVENT_REQ, 40,
 357                               ssctl_subsys_event_req_ei, &req);
 358        if (ret < 0) {
 359                dev_err(sysmon->dev, "failed to send shutdown request\n");
 360                qmi_txn_cancel(&txn);
 361                return;
 362        }
 363
 364        ret = qmi_txn_wait(&txn, 5 * HZ);
 365        if (ret < 0)
 366                dev_err(sysmon->dev, "failed receiving QMI response\n");
 367        else if (resp.resp.result)
 368                dev_err(sysmon->dev, "ssr event send failed\n");
 369        else
 370                dev_dbg(sysmon->dev, "ssr event send completed\n");
 371}
 372
 373/**
 374 * ssctl_new_server() - QMI callback indicating a new service
 375 * @qmi:        QMI handle
 376 * @svc:        service information
 377 *
 378 * Return: 0 if we're interested in this service, -EINVAL otherwise.
 379 */
 380static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
 381{
 382        struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
 383
 384        switch (svc->version) {
 385        case 1:
 386                if (svc->instance != 0)
 387                        return -EINVAL;
 388                if (strcmp(sysmon->name, "modem"))
 389                        return -EINVAL;
 390                break;
 391        case 2:
 392                if (svc->instance != sysmon->ssctl_instance)
 393                        return -EINVAL;
 394                break;
 395        default:
 396                return -EINVAL;
 397        };
 398
 399        sysmon->ssctl_version = svc->version;
 400
 401        sysmon->ssctl.sq_family = AF_QIPCRTR;
 402        sysmon->ssctl.sq_node = svc->node;
 403        sysmon->ssctl.sq_port = svc->port;
 404
 405        svc->priv = sysmon;
 406
 407        return 0;
 408}
 409
 410/**
 411 * ssctl_del_server() - QMI callback indicating that @svc is removed
 412 * @qmi:        QMI handle
 413 * @svc:        service information
 414 */
 415static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
 416{
 417        struct qcom_sysmon *sysmon = svc->priv;
 418
 419        sysmon->ssctl_version = 0;
 420}
 421
 422static const struct qmi_ops ssctl_ops = {
 423        .new_server = ssctl_new_server,
 424        .del_server = ssctl_del_server,
 425};
 426
 427static int sysmon_start(struct rproc_subdev *subdev)
 428{
 429        return 0;
 430}
 431
 432static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
 433{
 434        struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
 435
 436        blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name);
 437
 438        /* Don't request graceful shutdown if we've crashed */
 439        if (crashed)
 440                return;
 441
 442        if (sysmon->ssctl_version)
 443                ssctl_request_shutdown(sysmon);
 444        else if (sysmon->ept)
 445                sysmon_request_shutdown(sysmon);
 446}
 447
 448/**
 449 * sysmon_notify() - notify sysmon target of another's SSR
 450 * @nb:         notifier_block associated with sysmon instance
 451 * @event:      unused
 452 * @data:       SSR identifier of the remote that is going down
 453 */
 454static int sysmon_notify(struct notifier_block *nb, unsigned long event,
 455                         void *data)
 456{
 457        struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
 458        struct rproc *rproc = sysmon->rproc;
 459        const char *ssr_name = data;
 460
 461        /* Skip non-running rprocs and the originating instance */
 462        if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) {
 463                dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
 464                return NOTIFY_DONE;
 465        }
 466
 467        /* Only SSCTL version 2 supports SSR events */
 468        if (sysmon->ssctl_version == 2)
 469                ssctl_send_event(sysmon, ssr_name);
 470        else if (sysmon->ept)
 471                sysmon_send_event(sysmon, ssr_name);
 472
 473        return NOTIFY_DONE;
 474}
 475
 476static 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
 485/**
 486 * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
 487 * @rproc:      rproc context to associate the subdev with
 488 * @name:       name of this subdev, to use in SSR
 489 * @ssctl_instance: instance id of the ssctl QMI service
 490 *
 491 * Return: A new qcom_sysmon object, or NULL on failure
 492 */
 493struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
 494                                           const char *name,
 495                                           int ssctl_instance)
 496{
 497        struct qcom_sysmon *sysmon;
 498        int ret;
 499
 500        sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
 501        if (!sysmon)
 502                return ERR_PTR(-ENOMEM);
 503
 504        sysmon->dev = rproc->dev.parent;
 505        sysmon->rproc = rproc;
 506
 507        sysmon->name = name;
 508        sysmon->ssctl_instance = ssctl_instance;
 509
 510        init_completion(&sysmon->comp);
 511        init_completion(&sysmon->ind_comp);
 512        init_completion(&sysmon->shutdown_comp);
 513        mutex_init(&sysmon->lock);
 514
 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);
 538        if (ret < 0) {
 539                dev_err(sysmon->dev, "failed to initialize qmi handle\n");
 540                kfree(sysmon);
 541                return ERR_PTR(ret);
 542        }
 543
 544        qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
 545
 546        sysmon->subdev.start = sysmon_start;
 547        sysmon->subdev.stop = sysmon_stop;
 548
 549        rproc_add_subdev(rproc, &sysmon->subdev);
 550
 551        sysmon->nb.notifier_call = sysmon_notify;
 552        blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
 553
 554        mutex_lock(&sysmon_lock);
 555        list_add(&sysmon->node, &sysmon_list);
 556        mutex_unlock(&sysmon_lock);
 557
 558        return sysmon;
 559}
 560EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
 561
 562/**
 563 * qcom_remove_sysmon_subdev() - release a qcom_sysmon
 564 * @sysmon:     sysmon context, as retrieved by qcom_add_sysmon_subdev()
 565 */
 566void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
 567{
 568        if (!sysmon)
 569                return;
 570
 571        mutex_lock(&sysmon_lock);
 572        list_del(&sysmon->node);
 573        mutex_unlock(&sysmon_lock);
 574
 575        blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
 576
 577        rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
 578
 579        qmi_handle_release(&sysmon->qmi);
 580
 581        kfree(sysmon);
 582}
 583EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
 584
 585/**
 586 * sysmon_probe() - probe sys_mon channel
 587 * @rpdev:      rpmsg device handle
 588 *
 589 * Find the sysmon context associated with the ancestor remoteproc and assign
 590 * this rpmsg device with said sysmon context.
 591 *
 592 * Return: 0 on success, negative errno on failure.
 593 */
 594static int sysmon_probe(struct rpmsg_device *rpdev)
 595{
 596        struct qcom_sysmon *sysmon;
 597        struct rproc *rproc;
 598
 599        rproc = rproc_get_by_child(&rpdev->dev);
 600        if (!rproc) {
 601                dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
 602                return -EINVAL;
 603        }
 604
 605        mutex_lock(&sysmon_lock);
 606        list_for_each_entry(sysmon, &sysmon_list, node) {
 607                if (sysmon->rproc == rproc)
 608                        goto found;
 609        }
 610        mutex_unlock(&sysmon_lock);
 611
 612        dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
 613
 614        return -EINVAL;
 615
 616found:
 617        mutex_unlock(&sysmon_lock);
 618
 619        rpdev->ept->priv = sysmon;
 620        sysmon->ept = rpdev->ept;
 621
 622        return 0;
 623}
 624
 625/**
 626 * sysmon_remove() - sys_mon channel remove handler
 627 * @rpdev:      rpmsg device handle
 628 *
 629 * Disassociate the rpmsg device with the sysmon instance.
 630 */
 631static void sysmon_remove(struct rpmsg_device *rpdev)
 632{
 633        struct qcom_sysmon *sysmon = rpdev->ept->priv;
 634
 635        sysmon->ept = NULL;
 636}
 637
 638static const struct rpmsg_device_id sysmon_match[] = {
 639        { "sys_mon" },
 640        {}
 641};
 642
 643static struct rpmsg_driver sysmon_driver = {
 644        .probe = sysmon_probe,
 645        .remove = sysmon_remove,
 646        .callback = sysmon_callback,
 647        .id_table = sysmon_match,
 648        .drv = {
 649                .name = "qcom_sysmon",
 650        },
 651};
 652
 653module_rpmsg_driver(sysmon_driver);
 654
 655MODULE_DESCRIPTION("Qualcomm sysmon driver");
 656MODULE_LICENSE("GPL v2");
 657