qcom_sysmon.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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. #include "qcom_common.h"
  17. static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);
  18. struct qcom_sysmon {
  19. struct rproc_subdev subdev;
  20. struct rproc *rproc;
  21. struct list_head node;
  22. const char *name;
  23. int shutdown_irq;
  24. int ssctl_version;
  25. int ssctl_instance;
  26. struct notifier_block nb;
  27. struct device *dev;
  28. struct rpmsg_endpoint *ept;
  29. struct completion comp;
  30. struct completion ind_comp;
  31. struct completion shutdown_comp;
  32. struct mutex lock;
  33. bool ssr_ack;
  34. struct qmi_handle qmi;
  35. struct sockaddr_qrtr ssctl;
  36. };
  37. static DEFINE_MUTEX(sysmon_lock);
  38. static LIST_HEAD(sysmon_list);
  39. /**
  40. * sysmon_send_event() - send notification of other remote's SSR event
  41. * @sysmon: sysmon context
  42. * @name: other remote's name
  43. */
  44. static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name)
  45. {
  46. char req[50];
  47. int len;
  48. int ret;
  49. len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name);
  50. if (len >= sizeof(req))
  51. return;
  52. mutex_lock(&sysmon->lock);
  53. reinit_completion(&sysmon->comp);
  54. sysmon->ssr_ack = false;
  55. ret = rpmsg_send(sysmon->ept, req, len);
  56. if (ret < 0) {
  57. dev_err(sysmon->dev, "failed to send sysmon event\n");
  58. goto out_unlock;
  59. }
  60. ret = wait_for_completion_timeout(&sysmon->comp,
  61. msecs_to_jiffies(5000));
  62. if (!ret) {
  63. dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
  64. goto out_unlock;
  65. }
  66. if (!sysmon->ssr_ack)
  67. dev_err(sysmon->dev, "unexpected response to sysmon event\n");
  68. out_unlock:
  69. mutex_unlock(&sysmon->lock);
  70. }
  71. /**
  72. * sysmon_request_shutdown() - request graceful shutdown of remote
  73. * @sysmon: sysmon context
  74. */
  75. static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
  76. {
  77. char *req = "ssr:shutdown";
  78. int ret;
  79. mutex_lock(&sysmon->lock);
  80. reinit_completion(&sysmon->comp);
  81. sysmon->ssr_ack = false;
  82. ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
  83. if (ret < 0) {
  84. dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
  85. goto out_unlock;
  86. }
  87. ret = wait_for_completion_timeout(&sysmon->comp,
  88. msecs_to_jiffies(5000));
  89. if (!ret) {
  90. dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
  91. goto out_unlock;
  92. }
  93. if (!sysmon->ssr_ack)
  94. dev_err(sysmon->dev,
  95. "unexpected response to sysmon shutdown request\n");
  96. out_unlock:
  97. mutex_unlock(&sysmon->lock);
  98. }
  99. static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
  100. void *priv, u32 addr)
  101. {
  102. struct qcom_sysmon *sysmon = priv;
  103. const char *ssr_ack = "ssr:ack";
  104. const int ssr_ack_len = strlen(ssr_ack) + 1;
  105. if (!sysmon)
  106. return -EINVAL;
  107. if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
  108. sysmon->ssr_ack = true;
  109. complete(&sysmon->comp);
  110. return 0;
  111. }
  112. #define SSCTL_SHUTDOWN_REQ 0x21
  113. #define SSCTL_SHUTDOWN_READY_IND 0x21
  114. #define SSCTL_SUBSYS_EVENT_REQ 0x23
  115. #define SSCTL_MAX_MSG_LEN 7
  116. #define SSCTL_SUBSYS_NAME_LENGTH 15
  117. enum {
  118. SSCTL_SSR_EVENT_BEFORE_POWERUP,
  119. SSCTL_SSR_EVENT_AFTER_POWERUP,
  120. SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
  121. SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
  122. };
  123. enum {
  124. SSCTL_SSR_EVENT_FORCED,
  125. SSCTL_SSR_EVENT_GRACEFUL,
  126. };
  127. struct ssctl_shutdown_resp {
  128. struct qmi_response_type_v01 resp;
  129. };
  130. static struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
  131. {
  132. .data_type = QMI_STRUCT,
  133. .elem_len = 1,
  134. .elem_size = sizeof(struct qmi_response_type_v01),
  135. .array_type = NO_ARRAY,
  136. .tlv_type = 0x02,
  137. .offset = offsetof(struct ssctl_shutdown_resp, resp),
  138. .ei_array = qmi_response_type_v01_ei,
  139. },
  140. {}
  141. };
  142. struct ssctl_subsys_event_req {
  143. u8 subsys_name_len;
  144. char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
  145. u32 event;
  146. u8 evt_driven_valid;
  147. u32 evt_driven;
  148. };
  149. static struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
  150. {
  151. .data_type = QMI_DATA_LEN,
  152. .elem_len = 1,
  153. .elem_size = sizeof(uint8_t),
  154. .array_type = NO_ARRAY,
  155. .tlv_type = 0x01,
  156. .offset = offsetof(struct ssctl_subsys_event_req,
  157. subsys_name_len),
  158. .ei_array = NULL,
  159. },
  160. {
  161. .data_type = QMI_UNSIGNED_1_BYTE,
  162. .elem_len = SSCTL_SUBSYS_NAME_LENGTH,
  163. .elem_size = sizeof(char),
  164. .array_type = VAR_LEN_ARRAY,
  165. .tlv_type = 0x01,
  166. .offset = offsetof(struct ssctl_subsys_event_req,
  167. subsys_name),
  168. .ei_array = NULL,
  169. },
  170. {
  171. .data_type = QMI_SIGNED_4_BYTE_ENUM,
  172. .elem_len = 1,
  173. .elem_size = sizeof(uint32_t),
  174. .array_type = NO_ARRAY,
  175. .tlv_type = 0x02,
  176. .offset = offsetof(struct ssctl_subsys_event_req,
  177. event),
  178. .ei_array = NULL,
  179. },
  180. {
  181. .data_type = QMI_OPT_FLAG,
  182. .elem_len = 1,
  183. .elem_size = sizeof(uint8_t),
  184. .array_type = NO_ARRAY,
  185. .tlv_type = 0x10,
  186. .offset = offsetof(struct ssctl_subsys_event_req,
  187. evt_driven_valid),
  188. .ei_array = NULL,
  189. },
  190. {
  191. .data_type = QMI_SIGNED_4_BYTE_ENUM,
  192. .elem_len = 1,
  193. .elem_size = sizeof(uint32_t),
  194. .array_type = NO_ARRAY,
  195. .tlv_type = 0x10,
  196. .offset = offsetof(struct ssctl_subsys_event_req,
  197. evt_driven),
  198. .ei_array = NULL,
  199. },
  200. {}
  201. };
  202. struct ssctl_subsys_event_resp {
  203. struct qmi_response_type_v01 resp;
  204. };
  205. static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
  206. {
  207. .data_type = QMI_STRUCT,
  208. .elem_len = 1,
  209. .elem_size = sizeof(struct qmi_response_type_v01),
  210. .array_type = NO_ARRAY,
  211. .tlv_type = 0x02,
  212. .offset = offsetof(struct ssctl_subsys_event_resp,
  213. resp),
  214. .ei_array = qmi_response_type_v01_ei,
  215. },
  216. {}
  217. };
  218. static struct qmi_elem_info ssctl_shutdown_ind_ei[] = {
  219. {}
  220. };
  221. static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
  222. struct qmi_txn *txn, const void *data)
  223. {
  224. struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
  225. complete(&sysmon->ind_comp);
  226. }
  227. static struct qmi_msg_handler qmi_indication_handler[] = {
  228. {
  229. .type = QMI_INDICATION,
  230. .msg_id = SSCTL_SHUTDOWN_READY_IND,
  231. .ei = ssctl_shutdown_ind_ei,
  232. .decoded_size = 0,
  233. .fn = sysmon_ind_cb
  234. },
  235. {}
  236. };
  237. /**
  238. * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
  239. * @sysmon: sysmon context
  240. */
  241. static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
  242. {
  243. struct ssctl_shutdown_resp resp;
  244. struct qmi_txn txn;
  245. int ret;
  246. reinit_completion(&sysmon->ind_comp);
  247. reinit_completion(&sysmon->shutdown_comp);
  248. ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
  249. if (ret < 0) {
  250. dev_err(sysmon->dev, "failed to allocate QMI txn\n");
  251. return;
  252. }
  253. ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
  254. SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
  255. if (ret < 0) {
  256. dev_err(sysmon->dev, "failed to send shutdown request\n");
  257. qmi_txn_cancel(&txn);
  258. return;
  259. }
  260. ret = qmi_txn_wait(&txn, 5 * HZ);
  261. if (ret < 0)
  262. dev_err(sysmon->dev, "failed receiving QMI response\n");
  263. else if (resp.resp.result)
  264. dev_err(sysmon->dev, "shutdown request failed\n");
  265. else
  266. dev_dbg(sysmon->dev, "shutdown request completed\n");
  267. if (sysmon->shutdown_irq > 0) {
  268. ret = wait_for_completion_timeout(&sysmon->shutdown_comp,
  269. 10 * HZ);
  270. if (!ret) {
  271. ret = try_wait_for_completion(&sysmon->ind_comp);
  272. if (!ret)
  273. dev_err(sysmon->dev,
  274. "timeout waiting for shutdown ack\n");
  275. }
  276. }
  277. }
  278. /**
  279. * ssctl_send_event() - send notification of other remote's SSR event
  280. * @sysmon: sysmon context
  281. * @name: other remote's name
  282. */
  283. static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name)
  284. {
  285. struct ssctl_subsys_event_resp resp;
  286. struct ssctl_subsys_event_req req;
  287. struct qmi_txn txn;
  288. int ret;
  289. memset(&resp, 0, sizeof(resp));
  290. ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
  291. if (ret < 0) {
  292. dev_err(sysmon->dev, "failed to allocate QMI txn\n");
  293. return;
  294. }
  295. memset(&req, 0, sizeof(req));
  296. strlcpy(req.subsys_name, name, sizeof(req.subsys_name));
  297. req.subsys_name_len = strlen(req.subsys_name);
  298. req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
  299. req.evt_driven_valid = true;
  300. req.evt_driven = SSCTL_SSR_EVENT_FORCED;
  301. ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
  302. SSCTL_SUBSYS_EVENT_REQ, 40,
  303. ssctl_subsys_event_req_ei, &req);
  304. if (ret < 0) {
  305. dev_err(sysmon->dev, "failed to send shutdown request\n");
  306. qmi_txn_cancel(&txn);
  307. return;
  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, "ssr event send failed\n");
  314. else
  315. dev_dbg(sysmon->dev, "ssr event send completed\n");
  316. }
  317. /**
  318. * ssctl_new_server() - QMI callback indicating a new service
  319. * @qmi: QMI handle
  320. * @svc: service information
  321. *
  322. * Return: 0 if we're interested in this service, -EINVAL otherwise.
  323. */
  324. static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
  325. {
  326. struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
  327. switch (svc->version) {
  328. case 1:
  329. if (svc->instance != 0)
  330. return -EINVAL;
  331. if (strcmp(sysmon->name, "modem"))
  332. return -EINVAL;
  333. break;
  334. case 2:
  335. if (svc->instance != sysmon->ssctl_instance)
  336. return -EINVAL;
  337. break;
  338. default:
  339. return -EINVAL;
  340. };
  341. sysmon->ssctl_version = svc->version;
  342. sysmon->ssctl.sq_family = AF_QIPCRTR;
  343. sysmon->ssctl.sq_node = svc->node;
  344. sysmon->ssctl.sq_port = svc->port;
  345. svc->priv = sysmon;
  346. return 0;
  347. }
  348. /**
  349. * ssctl_del_server() - QMI callback indicating that @svc is removed
  350. * @qmi: QMI handle
  351. * @svc: service information
  352. */
  353. static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
  354. {
  355. struct qcom_sysmon *sysmon = svc->priv;
  356. sysmon->ssctl_version = 0;
  357. }
  358. static const struct qmi_ops ssctl_ops = {
  359. .new_server = ssctl_new_server,
  360. .del_server = ssctl_del_server,
  361. };
  362. static int sysmon_start(struct rproc_subdev *subdev)
  363. {
  364. return 0;
  365. }
  366. static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
  367. {
  368. struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
  369. blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name);
  370. /* Don't request graceful shutdown if we've crashed */
  371. if (crashed)
  372. return;
  373. if (sysmon->ssctl_version)
  374. ssctl_request_shutdown(sysmon);
  375. else if (sysmon->ept)
  376. sysmon_request_shutdown(sysmon);
  377. }
  378. /**
  379. * sysmon_notify() - notify sysmon target of another's SSR
  380. * @nb: notifier_block associated with sysmon instance
  381. * @event: unused
  382. * @data: SSR identifier of the remote that is going down
  383. */
  384. static int sysmon_notify(struct notifier_block *nb, unsigned long event,
  385. void *data)
  386. {
  387. struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
  388. struct rproc *rproc = sysmon->rproc;
  389. const char *ssr_name = data;
  390. /* Skip non-running rprocs and the originating instance */
  391. if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) {
  392. dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
  393. return NOTIFY_DONE;
  394. }
  395. /* Only SSCTL version 2 supports SSR events */
  396. if (sysmon->ssctl_version == 2)
  397. ssctl_send_event(sysmon, ssr_name);
  398. else if (sysmon->ept)
  399. sysmon_send_event(sysmon, ssr_name);
  400. return NOTIFY_DONE;
  401. }
  402. static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data)
  403. {
  404. struct qcom_sysmon *sysmon = data;
  405. complete(&sysmon->shutdown_comp);
  406. return IRQ_HANDLED;
  407. }
  408. /**
  409. * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
  410. * @rproc: rproc context to associate the subdev with
  411. * @name: name of this subdev, to use in SSR
  412. * @ssctl_instance: instance id of the ssctl QMI service
  413. *
  414. * Return: A new qcom_sysmon object, or NULL on failure
  415. */
  416. struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
  417. const char *name,
  418. int ssctl_instance)
  419. {
  420. struct qcom_sysmon *sysmon;
  421. int ret;
  422. sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
  423. if (!sysmon)
  424. return ERR_PTR(-ENOMEM);
  425. sysmon->dev = rproc->dev.parent;
  426. sysmon->rproc = rproc;
  427. sysmon->name = name;
  428. sysmon->ssctl_instance = ssctl_instance;
  429. init_completion(&sysmon->comp);
  430. init_completion(&sysmon->ind_comp);
  431. init_completion(&sysmon->shutdown_comp);
  432. mutex_init(&sysmon->lock);
  433. sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node,
  434. "shutdown-ack");
  435. if (sysmon->shutdown_irq < 0) {
  436. if (sysmon->shutdown_irq != -ENODATA) {
  437. dev_err(sysmon->dev,
  438. "failed to retrieve shutdown-ack IRQ\n");
  439. return ERR_PTR(sysmon->shutdown_irq);
  440. }
  441. } else {
  442. ret = devm_request_threaded_irq(sysmon->dev,
  443. sysmon->shutdown_irq,
  444. NULL, sysmon_shutdown_interrupt,
  445. IRQF_TRIGGER_RISING | IRQF_ONESHOT,
  446. "q6v5 shutdown-ack", sysmon);
  447. if (ret) {
  448. dev_err(sysmon->dev,
  449. "failed to acquire shutdown-ack IRQ\n");
  450. return ERR_PTR(ret);
  451. }
  452. }
  453. ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops,
  454. qmi_indication_handler);
  455. if (ret < 0) {
  456. dev_err(sysmon->dev, "failed to initialize qmi handle\n");
  457. kfree(sysmon);
  458. return ERR_PTR(ret);
  459. }
  460. qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
  461. sysmon->subdev.start = sysmon_start;
  462. sysmon->subdev.stop = sysmon_stop;
  463. rproc_add_subdev(rproc, &sysmon->subdev);
  464. sysmon->nb.notifier_call = sysmon_notify;
  465. blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
  466. mutex_lock(&sysmon_lock);
  467. list_add(&sysmon->node, &sysmon_list);
  468. mutex_unlock(&sysmon_lock);
  469. return sysmon;
  470. }
  471. EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
  472. /**
  473. * qcom_remove_sysmon_subdev() - release a qcom_sysmon
  474. * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev()
  475. */
  476. void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
  477. {
  478. if (!sysmon)
  479. return;
  480. mutex_lock(&sysmon_lock);
  481. list_del(&sysmon->node);
  482. mutex_unlock(&sysmon_lock);
  483. blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
  484. rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
  485. qmi_handle_release(&sysmon->qmi);
  486. kfree(sysmon);
  487. }
  488. EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
  489. /**
  490. * sysmon_probe() - probe sys_mon channel
  491. * @rpdev: rpmsg device handle
  492. *
  493. * Find the sysmon context associated with the ancestor remoteproc and assign
  494. * this rpmsg device with said sysmon context.
  495. *
  496. * Return: 0 on success, negative errno on failure.
  497. */
  498. static int sysmon_probe(struct rpmsg_device *rpdev)
  499. {
  500. struct qcom_sysmon *sysmon;
  501. struct rproc *rproc;
  502. rproc = rproc_get_by_child(&rpdev->dev);
  503. if (!rproc) {
  504. dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
  505. return -EINVAL;
  506. }
  507. mutex_lock(&sysmon_lock);
  508. list_for_each_entry(sysmon, &sysmon_list, node) {
  509. if (sysmon->rproc == rproc)
  510. goto found;
  511. }
  512. mutex_unlock(&sysmon_lock);
  513. dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
  514. return -EINVAL;
  515. found:
  516. mutex_unlock(&sysmon_lock);
  517. rpdev->ept->priv = sysmon;
  518. sysmon->ept = rpdev->ept;
  519. return 0;
  520. }
  521. /**
  522. * sysmon_remove() - sys_mon channel remove handler
  523. * @rpdev: rpmsg device handle
  524. *
  525. * Disassociate the rpmsg device with the sysmon instance.
  526. */
  527. static void sysmon_remove(struct rpmsg_device *rpdev)
  528. {
  529. struct qcom_sysmon *sysmon = rpdev->ept->priv;
  530. sysmon->ept = NULL;
  531. }
  532. static const struct rpmsg_device_id sysmon_match[] = {
  533. { "sys_mon" },
  534. {}
  535. };
  536. static struct rpmsg_driver sysmon_driver = {
  537. .probe = sysmon_probe,
  538. .remove = sysmon_remove,
  539. .callback = sysmon_callback,
  540. .id_table = sysmon_match,
  541. .drv = {
  542. .name = "qcom_sysmon",
  543. },
  544. };
  545. module_rpmsg_driver(sysmon_driver);
  546. MODULE_DESCRIPTION("Qualcomm sysmon driver");
  547. MODULE_LICENSE("GPL v2");