18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2017, Linaro Ltd.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/firmware.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/notifier.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
128c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/remoteproc/qcom_rproc.h>
158c2ecf20Sopenharmony_ci#include <linux/rpmsg.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "qcom_common.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct qcom_sysmon {
228c2ecf20Sopenharmony_ci	struct rproc_subdev subdev;
238c2ecf20Sopenharmony_ci	struct rproc *rproc;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	int state;
268c2ecf20Sopenharmony_ci	struct mutex state_lock;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	struct list_head node;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	const char *name;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	int shutdown_irq;
338c2ecf20Sopenharmony_ci	int ssctl_version;
348c2ecf20Sopenharmony_ci	int ssctl_instance;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	struct notifier_block nb;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	struct device *dev;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	struct rpmsg_endpoint *ept;
418c2ecf20Sopenharmony_ci	struct completion comp;
428c2ecf20Sopenharmony_ci	struct completion ind_comp;
438c2ecf20Sopenharmony_ci	struct completion shutdown_comp;
448c2ecf20Sopenharmony_ci	struct completion ssctl_comp;
458c2ecf20Sopenharmony_ci	struct mutex lock;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	bool ssr_ack;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	struct qmi_handle qmi;
508c2ecf20Sopenharmony_ci	struct sockaddr_qrtr ssctl;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cienum {
548c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_BEFORE_POWERUP,
558c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_AFTER_POWERUP,
568c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
578c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const char * const sysmon_state_string[] = {
618c2ecf20Sopenharmony_ci	[SSCTL_SSR_EVENT_BEFORE_POWERUP]	= "before_powerup",
628c2ecf20Sopenharmony_ci	[SSCTL_SSR_EVENT_AFTER_POWERUP]		= "after_powerup",
638c2ecf20Sopenharmony_ci	[SSCTL_SSR_EVENT_BEFORE_SHUTDOWN]	= "before_shutdown",
648c2ecf20Sopenharmony_ci	[SSCTL_SSR_EVENT_AFTER_SHUTDOWN]	= "after_shutdown",
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct sysmon_event {
688c2ecf20Sopenharmony_ci	const char *subsys_name;
698c2ecf20Sopenharmony_ci	u32 ssr_event;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sysmon_lock);
738c2ecf20Sopenharmony_cistatic LIST_HEAD(sysmon_list);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/**
768c2ecf20Sopenharmony_ci * sysmon_send_event() - send notification of other remote's SSR event
778c2ecf20Sopenharmony_ci * @sysmon:	sysmon context
788c2ecf20Sopenharmony_ci * @event:	sysmon event context
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic void sysmon_send_event(struct qcom_sysmon *sysmon,
818c2ecf20Sopenharmony_ci			      const struct sysmon_event *event)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	char req[50];
848c2ecf20Sopenharmony_ci	int len;
858c2ecf20Sopenharmony_ci	int ret;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name,
888c2ecf20Sopenharmony_ci		       sysmon_state_string[event->ssr_event]);
898c2ecf20Sopenharmony_ci	if (len >= sizeof(req))
908c2ecf20Sopenharmony_ci		return;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->lock);
938c2ecf20Sopenharmony_ci	reinit_completion(&sysmon->comp);
948c2ecf20Sopenharmony_ci	sysmon->ssr_ack = false;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ret = rpmsg_send(sysmon->ept, req, len);
978c2ecf20Sopenharmony_ci	if (ret < 0) {
988c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to send sysmon event\n");
998c2ecf20Sopenharmony_ci		goto out_unlock;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&sysmon->comp,
1038c2ecf20Sopenharmony_ci					  msecs_to_jiffies(5000));
1048c2ecf20Sopenharmony_ci	if (!ret) {
1058c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
1068c2ecf20Sopenharmony_ci		goto out_unlock;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (!sysmon->ssr_ack)
1108c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "unexpected response to sysmon event\n");
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciout_unlock:
1138c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->lock);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * sysmon_request_shutdown() - request graceful shutdown of remote
1188c2ecf20Sopenharmony_ci * @sysmon:	sysmon context
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	char *req = "ssr:shutdown";
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->lock);
1268c2ecf20Sopenharmony_ci	reinit_completion(&sysmon->comp);
1278c2ecf20Sopenharmony_ci	sysmon->ssr_ack = false;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
1308c2ecf20Sopenharmony_ci	if (ret < 0) {
1318c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
1328c2ecf20Sopenharmony_ci		goto out_unlock;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&sysmon->comp,
1368c2ecf20Sopenharmony_ci					  msecs_to_jiffies(5000));
1378c2ecf20Sopenharmony_ci	if (!ret) {
1388c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
1398c2ecf20Sopenharmony_ci		goto out_unlock;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!sysmon->ssr_ack)
1438c2ecf20Sopenharmony_ci		dev_err(sysmon->dev,
1448c2ecf20Sopenharmony_ci			"unexpected response to sysmon shutdown request\n");
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciout_unlock:
1478c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->lock);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
1518c2ecf20Sopenharmony_ci			   void *priv, u32 addr)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = priv;
1548c2ecf20Sopenharmony_ci	const char *ssr_ack = "ssr:ack";
1558c2ecf20Sopenharmony_ci	const int ssr_ack_len = strlen(ssr_ack) + 1;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!sysmon)
1588c2ecf20Sopenharmony_ci		return -EINVAL;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
1618c2ecf20Sopenharmony_ci		sysmon->ssr_ack = true;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	complete(&sysmon->comp);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#define SSCTL_SHUTDOWN_REQ		0x21
1698c2ecf20Sopenharmony_ci#define SSCTL_SHUTDOWN_READY_IND	0x21
1708c2ecf20Sopenharmony_ci#define SSCTL_SUBSYS_EVENT_REQ		0x23
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci#define SSCTL_MAX_MSG_LEN		7
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define SSCTL_SUBSYS_NAME_LENGTH	15
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cienum {
1778c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_FORCED,
1788c2ecf20Sopenharmony_ci	SSCTL_SSR_EVENT_GRACEFUL,
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct ssctl_shutdown_resp {
1828c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
1868c2ecf20Sopenharmony_ci	{
1878c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
1888c2ecf20Sopenharmony_ci		.elem_len	= 1,
1898c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
1908c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
1918c2ecf20Sopenharmony_ci		.tlv_type	= 0x02,
1928c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_shutdown_resp, resp),
1938c2ecf20Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci	{}
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistruct ssctl_subsys_event_req {
1998c2ecf20Sopenharmony_ci	u8 subsys_name_len;
2008c2ecf20Sopenharmony_ci	char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
2018c2ecf20Sopenharmony_ci	u32 event;
2028c2ecf20Sopenharmony_ci	u8 evt_driven_valid;
2038c2ecf20Sopenharmony_ci	u32 evt_driven;
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
2078c2ecf20Sopenharmony_ci	{
2088c2ecf20Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
2098c2ecf20Sopenharmony_ci		.elem_len	= 1,
2108c2ecf20Sopenharmony_ci		.elem_size	= sizeof(uint8_t),
2118c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2128c2ecf20Sopenharmony_ci		.tlv_type	= 0x01,
2138c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
2148c2ecf20Sopenharmony_ci					   subsys_name_len),
2158c2ecf20Sopenharmony_ci		.ei_array	= NULL,
2168c2ecf20Sopenharmony_ci	},
2178c2ecf20Sopenharmony_ci	{
2188c2ecf20Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
2198c2ecf20Sopenharmony_ci		.elem_len	= SSCTL_SUBSYS_NAME_LENGTH,
2208c2ecf20Sopenharmony_ci		.elem_size	= sizeof(char),
2218c2ecf20Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
2228c2ecf20Sopenharmony_ci		.tlv_type	= 0x01,
2238c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
2248c2ecf20Sopenharmony_ci					   subsys_name),
2258c2ecf20Sopenharmony_ci		.ei_array	= NULL,
2268c2ecf20Sopenharmony_ci	},
2278c2ecf20Sopenharmony_ci	{
2288c2ecf20Sopenharmony_ci		.data_type	= QMI_SIGNED_4_BYTE_ENUM,
2298c2ecf20Sopenharmony_ci		.elem_len	= 1,
2308c2ecf20Sopenharmony_ci		.elem_size	= sizeof(uint32_t),
2318c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2328c2ecf20Sopenharmony_ci		.tlv_type	= 0x02,
2338c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
2348c2ecf20Sopenharmony_ci					   event),
2358c2ecf20Sopenharmony_ci		.ei_array	= NULL,
2368c2ecf20Sopenharmony_ci	},
2378c2ecf20Sopenharmony_ci	{
2388c2ecf20Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
2398c2ecf20Sopenharmony_ci		.elem_len	= 1,
2408c2ecf20Sopenharmony_ci		.elem_size	= sizeof(uint8_t),
2418c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2428c2ecf20Sopenharmony_ci		.tlv_type	= 0x10,
2438c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
2448c2ecf20Sopenharmony_ci					   evt_driven_valid),
2458c2ecf20Sopenharmony_ci		.ei_array	= NULL,
2468c2ecf20Sopenharmony_ci	},
2478c2ecf20Sopenharmony_ci	{
2488c2ecf20Sopenharmony_ci		.data_type	= QMI_SIGNED_4_BYTE_ENUM,
2498c2ecf20Sopenharmony_ci		.elem_len	= 1,
2508c2ecf20Sopenharmony_ci		.elem_size	= sizeof(uint32_t),
2518c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2528c2ecf20Sopenharmony_ci		.tlv_type	= 0x10,
2538c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
2548c2ecf20Sopenharmony_ci					   evt_driven),
2558c2ecf20Sopenharmony_ci		.ei_array	= NULL,
2568c2ecf20Sopenharmony_ci	},
2578c2ecf20Sopenharmony_ci	{}
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistruct ssctl_subsys_event_resp {
2618c2ecf20Sopenharmony_ci	struct qmi_response_type_v01 resp;
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
2658c2ecf20Sopenharmony_ci	{
2668c2ecf20Sopenharmony_ci		.data_type	= QMI_STRUCT,
2678c2ecf20Sopenharmony_ci		.elem_len	= 1,
2688c2ecf20Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
2698c2ecf20Sopenharmony_ci		.array_type	= NO_ARRAY,
2708c2ecf20Sopenharmony_ci		.tlv_type	= 0x02,
2718c2ecf20Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_resp,
2728c2ecf20Sopenharmony_ci					   resp),
2738c2ecf20Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
2748c2ecf20Sopenharmony_ci	},
2758c2ecf20Sopenharmony_ci	{}
2768c2ecf20Sopenharmony_ci};
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic struct qmi_elem_info ssctl_shutdown_ind_ei[] = {
2798c2ecf20Sopenharmony_ci	{}
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
2838c2ecf20Sopenharmony_ci			  struct qmi_txn *txn, const void *data)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	complete(&sysmon->ind_comp);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic struct qmi_msg_handler qmi_indication_handler[] = {
2918c2ecf20Sopenharmony_ci	{
2928c2ecf20Sopenharmony_ci		.type = QMI_INDICATION,
2938c2ecf20Sopenharmony_ci		.msg_id = SSCTL_SHUTDOWN_READY_IND,
2948c2ecf20Sopenharmony_ci		.ei = ssctl_shutdown_ind_ei,
2958c2ecf20Sopenharmony_ci		.decoded_size = 0,
2968c2ecf20Sopenharmony_ci		.fn = sysmon_ind_cb
2978c2ecf20Sopenharmony_ci	},
2988c2ecf20Sopenharmony_ci	{}
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/**
3028c2ecf20Sopenharmony_ci * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
3038c2ecf20Sopenharmony_ci * @sysmon:	sysmon context
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_cistatic void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct ssctl_shutdown_resp resp;
3088c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3098c2ecf20Sopenharmony_ci	int ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	reinit_completion(&sysmon->ind_comp);
3128c2ecf20Sopenharmony_ci	reinit_completion(&sysmon->shutdown_comp);
3138c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
3148c2ecf20Sopenharmony_ci	if (ret < 0) {
3158c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to allocate QMI txn\n");
3168c2ecf20Sopenharmony_ci		return;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
3208c2ecf20Sopenharmony_ci			       SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
3218c2ecf20Sopenharmony_ci	if (ret < 0) {
3228c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to send shutdown request\n");
3238c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
3288c2ecf20Sopenharmony_ci	if (ret < 0)
3298c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed receiving QMI response\n");
3308c2ecf20Sopenharmony_ci	else if (resp.resp.result)
3318c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "shutdown request failed\n");
3328c2ecf20Sopenharmony_ci	else
3338c2ecf20Sopenharmony_ci		dev_dbg(sysmon->dev, "shutdown request completed\n");
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (sysmon->shutdown_irq > 0) {
3368c2ecf20Sopenharmony_ci		ret = wait_for_completion_timeout(&sysmon->shutdown_comp,
3378c2ecf20Sopenharmony_ci						  10 * HZ);
3388c2ecf20Sopenharmony_ci		if (!ret) {
3398c2ecf20Sopenharmony_ci			ret = try_wait_for_completion(&sysmon->ind_comp);
3408c2ecf20Sopenharmony_ci			if (!ret)
3418c2ecf20Sopenharmony_ci				dev_err(sysmon->dev,
3428c2ecf20Sopenharmony_ci					"timeout waiting for shutdown ack\n");
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/**
3488c2ecf20Sopenharmony_ci * ssctl_send_event() - send notification of other remote's SSR event
3498c2ecf20Sopenharmony_ci * @sysmon:	sysmon context
3508c2ecf20Sopenharmony_ci * @event:	sysmon event context
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_cistatic void ssctl_send_event(struct qcom_sysmon *sysmon,
3538c2ecf20Sopenharmony_ci			     const struct sysmon_event *event)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct ssctl_subsys_event_resp resp;
3568c2ecf20Sopenharmony_ci	struct ssctl_subsys_event_req req;
3578c2ecf20Sopenharmony_ci	struct qmi_txn txn;
3588c2ecf20Sopenharmony_ci	int ret;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	memset(&resp, 0, sizeof(resp));
3618c2ecf20Sopenharmony_ci	ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
3628c2ecf20Sopenharmony_ci	if (ret < 0) {
3638c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to allocate QMI txn\n");
3648c2ecf20Sopenharmony_ci		return;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	memset(&req, 0, sizeof(req));
3688c2ecf20Sopenharmony_ci	strlcpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name));
3698c2ecf20Sopenharmony_ci	req.subsys_name_len = strlen(req.subsys_name);
3708c2ecf20Sopenharmony_ci	req.event = event->ssr_event;
3718c2ecf20Sopenharmony_ci	req.evt_driven_valid = true;
3728c2ecf20Sopenharmony_ci	req.evt_driven = SSCTL_SSR_EVENT_FORCED;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
3758c2ecf20Sopenharmony_ci			       SSCTL_SUBSYS_EVENT_REQ, 40,
3768c2ecf20Sopenharmony_ci			       ssctl_subsys_event_req_ei, &req);
3778c2ecf20Sopenharmony_ci	if (ret < 0) {
3788c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to send shutdown request\n");
3798c2ecf20Sopenharmony_ci		qmi_txn_cancel(&txn);
3808c2ecf20Sopenharmony_ci		return;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
3848c2ecf20Sopenharmony_ci	if (ret < 0)
3858c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed receiving QMI response\n");
3868c2ecf20Sopenharmony_ci	else if (resp.resp.result)
3878c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "ssr event send failed\n");
3888c2ecf20Sopenharmony_ci	else
3898c2ecf20Sopenharmony_ci		dev_dbg(sysmon->dev, "ssr event send completed\n");
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci/**
3938c2ecf20Sopenharmony_ci * ssctl_new_server() - QMI callback indicating a new service
3948c2ecf20Sopenharmony_ci * @qmi:	QMI handle
3958c2ecf20Sopenharmony_ci * @svc:	service information
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * Return: 0 if we're interested in this service, -EINVAL otherwise.
3988c2ecf20Sopenharmony_ci */
3998c2ecf20Sopenharmony_cistatic int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	switch (svc->version) {
4048c2ecf20Sopenharmony_ci	case 1:
4058c2ecf20Sopenharmony_ci		if (svc->instance != 0)
4068c2ecf20Sopenharmony_ci			return -EINVAL;
4078c2ecf20Sopenharmony_ci		if (strcmp(sysmon->name, "modem"))
4088c2ecf20Sopenharmony_ci			return -EINVAL;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	case 2:
4118c2ecf20Sopenharmony_ci		if (svc->instance != sysmon->ssctl_instance)
4128c2ecf20Sopenharmony_ci			return -EINVAL;
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci	default:
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	sysmon->ssctl_version = svc->version;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	sysmon->ssctl.sq_family = AF_QIPCRTR;
4218c2ecf20Sopenharmony_ci	sysmon->ssctl.sq_node = svc->node;
4228c2ecf20Sopenharmony_ci	sysmon->ssctl.sq_port = svc->port;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	svc->priv = sysmon;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	complete(&sysmon->ssctl_comp);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/**
4328c2ecf20Sopenharmony_ci * ssctl_del_server() - QMI callback indicating that @svc is removed
4338c2ecf20Sopenharmony_ci * @qmi:	QMI handle
4348c2ecf20Sopenharmony_ci * @svc:	service information
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_cistatic void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = svc->priv;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	sysmon->ssctl_version = 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic const struct qmi_ops ssctl_ops = {
4448c2ecf20Sopenharmony_ci	.new_server = ssctl_new_server,
4458c2ecf20Sopenharmony_ci	.del_server = ssctl_del_server,
4468c2ecf20Sopenharmony_ci};
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int sysmon_prepare(struct rproc_subdev *subdev)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
4518c2ecf20Sopenharmony_ci						  subdev);
4528c2ecf20Sopenharmony_ci	struct sysmon_event event = {
4538c2ecf20Sopenharmony_ci		.subsys_name = sysmon->name,
4548c2ecf20Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP
4558c2ecf20Sopenharmony_ci	};
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
4588c2ecf20Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_BEFORE_POWERUP;
4598c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
4608c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/**
4668c2ecf20Sopenharmony_ci * sysmon_start() - start callback for the sysmon remoteproc subdevice
4678c2ecf20Sopenharmony_ci * @subdev:	instance of the sysmon subdevice
4688c2ecf20Sopenharmony_ci *
4698c2ecf20Sopenharmony_ci * Inform all the listners of sysmon notifications that the rproc associated
4708c2ecf20Sopenharmony_ci * to @subdev has booted up. The rproc that booted up also needs to know
4718c2ecf20Sopenharmony_ci * which rprocs are already up and running, so send start notifications
4728c2ecf20Sopenharmony_ci * on behalf of all the online rprocs.
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic int sysmon_start(struct rproc_subdev *subdev)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
4778c2ecf20Sopenharmony_ci						  subdev);
4788c2ecf20Sopenharmony_ci	struct qcom_sysmon *target;
4798c2ecf20Sopenharmony_ci	struct sysmon_event event = {
4808c2ecf20Sopenharmony_ci		.subsys_name = sysmon->name,
4818c2ecf20Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP
4828c2ecf20Sopenharmony_ci	};
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	reinit_completion(&sysmon->ssctl_comp);
4858c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
4868c2ecf20Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP;
4878c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
4888c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	mutex_lock(&sysmon_lock);
4918c2ecf20Sopenharmony_ci	list_for_each_entry(target, &sysmon_list, node) {
4928c2ecf20Sopenharmony_ci		if (target == sysmon)
4938c2ecf20Sopenharmony_ci			continue;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		mutex_lock(&target->state_lock);
4968c2ecf20Sopenharmony_ci		event.subsys_name = target->name;
4978c2ecf20Sopenharmony_ci		event.ssr_event = target->state;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		if (sysmon->ssctl_version == 2)
5008c2ecf20Sopenharmony_ci			ssctl_send_event(sysmon, &event);
5018c2ecf20Sopenharmony_ci		else if (sysmon->ept)
5028c2ecf20Sopenharmony_ci			sysmon_send_event(sysmon, &event);
5038c2ecf20Sopenharmony_ci		mutex_unlock(&target->state_lock);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon_lock);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return 0;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
5138c2ecf20Sopenharmony_ci	struct sysmon_event event = {
5148c2ecf20Sopenharmony_ci		.subsys_name = sysmon->name,
5158c2ecf20Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN
5168c2ecf20Sopenharmony_ci	};
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
5198c2ecf20Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
5208c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
5218c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Don't request graceful shutdown if we've crashed */
5248c2ecf20Sopenharmony_ci	if (crashed)
5258c2ecf20Sopenharmony_ci		return;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (sysmon->ssctl_instance) {
5288c2ecf20Sopenharmony_ci		if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2))
5298c2ecf20Sopenharmony_ci			dev_err(sysmon->dev, "timeout waiting for ssctl service\n");
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (sysmon->ssctl_version)
5338c2ecf20Sopenharmony_ci		ssctl_request_shutdown(sysmon);
5348c2ecf20Sopenharmony_ci	else if (sysmon->ept)
5358c2ecf20Sopenharmony_ci		sysmon_request_shutdown(sysmon);
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic void sysmon_unprepare(struct rproc_subdev *subdev)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
5418c2ecf20Sopenharmony_ci						  subdev);
5428c2ecf20Sopenharmony_ci	struct sysmon_event event = {
5438c2ecf20Sopenharmony_ci		.subsys_name = sysmon->name,
5448c2ecf20Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN
5458c2ecf20Sopenharmony_ci	};
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
5488c2ecf20Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN;
5498c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
5508c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/**
5548c2ecf20Sopenharmony_ci * sysmon_notify() - notify sysmon target of another's SSR
5558c2ecf20Sopenharmony_ci * @nb:		notifier_block associated with sysmon instance
5568c2ecf20Sopenharmony_ci * @event:	unused
5578c2ecf20Sopenharmony_ci * @data:	SSR identifier of the remote that is going down
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_cistatic int sysmon_notify(struct notifier_block *nb, unsigned long event,
5608c2ecf20Sopenharmony_ci			 void *data)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
5638c2ecf20Sopenharmony_ci	struct sysmon_event *sysmon_event = data;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* Skip non-running rprocs and the originating instance */
5668c2ecf20Sopenharmony_ci	if (sysmon->state != SSCTL_SSR_EVENT_AFTER_POWERUP ||
5678c2ecf20Sopenharmony_ci	    !strcmp(sysmon_event->subsys_name, sysmon->name)) {
5688c2ecf20Sopenharmony_ci		dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
5698c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* Only SSCTL version 2 supports SSR events */
5738c2ecf20Sopenharmony_ci	if (sysmon->ssctl_version == 2)
5748c2ecf20Sopenharmony_ci		ssctl_send_event(sysmon, sysmon_event);
5758c2ecf20Sopenharmony_ci	else if (sysmon->ept)
5768c2ecf20Sopenharmony_ci		sysmon_send_event(sysmon, sysmon_event);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic irqreturn_t sysmon_shutdown_interrupt(int irq, void *data)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = data;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	complete(&sysmon->shutdown_comp);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/**
5918c2ecf20Sopenharmony_ci * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
5928c2ecf20Sopenharmony_ci * @rproc:	rproc context to associate the subdev with
5938c2ecf20Sopenharmony_ci * @name:	name of this subdev, to use in SSR
5948c2ecf20Sopenharmony_ci * @ssctl_instance: instance id of the ssctl QMI service
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * Return: A new qcom_sysmon object, or NULL on failure
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_cistruct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
5998c2ecf20Sopenharmony_ci					   const char *name,
6008c2ecf20Sopenharmony_ci					   int ssctl_instance)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon;
6038c2ecf20Sopenharmony_ci	int ret;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
6068c2ecf20Sopenharmony_ci	if (!sysmon)
6078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	sysmon->dev = rproc->dev.parent;
6108c2ecf20Sopenharmony_ci	sysmon->rproc = rproc;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	sysmon->name = name;
6138c2ecf20Sopenharmony_ci	sysmon->ssctl_instance = ssctl_instance;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	init_completion(&sysmon->comp);
6168c2ecf20Sopenharmony_ci	init_completion(&sysmon->ind_comp);
6178c2ecf20Sopenharmony_ci	init_completion(&sysmon->shutdown_comp);
6188c2ecf20Sopenharmony_ci	init_completion(&sysmon->ssctl_comp);
6198c2ecf20Sopenharmony_ci	mutex_init(&sysmon->lock);
6208c2ecf20Sopenharmony_ci	mutex_init(&sysmon->state_lock);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node,
6238c2ecf20Sopenharmony_ci						 "shutdown-ack");
6248c2ecf20Sopenharmony_ci	if (sysmon->shutdown_irq < 0) {
6258c2ecf20Sopenharmony_ci		if (sysmon->shutdown_irq != -ENODATA) {
6268c2ecf20Sopenharmony_ci			dev_err(sysmon->dev,
6278c2ecf20Sopenharmony_ci				"failed to retrieve shutdown-ack IRQ\n");
6288c2ecf20Sopenharmony_ci			ret = sysmon->shutdown_irq;
6298c2ecf20Sopenharmony_ci			kfree(sysmon);
6308c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci	} else {
6338c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(sysmon->dev,
6348c2ecf20Sopenharmony_ci						sysmon->shutdown_irq,
6358c2ecf20Sopenharmony_ci						NULL, sysmon_shutdown_interrupt,
6368c2ecf20Sopenharmony_ci						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
6378c2ecf20Sopenharmony_ci						"q6v5 shutdown-ack", sysmon);
6388c2ecf20Sopenharmony_ci		if (ret) {
6398c2ecf20Sopenharmony_ci			dev_err(sysmon->dev,
6408c2ecf20Sopenharmony_ci				"failed to acquire shutdown-ack IRQ\n");
6418c2ecf20Sopenharmony_ci			kfree(sysmon);
6428c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
6438c2ecf20Sopenharmony_ci		}
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops,
6478c2ecf20Sopenharmony_ci			      qmi_indication_handler);
6488c2ecf20Sopenharmony_ci	if (ret < 0) {
6498c2ecf20Sopenharmony_ci		dev_err(sysmon->dev, "failed to initialize qmi handle\n");
6508c2ecf20Sopenharmony_ci		kfree(sysmon);
6518c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	sysmon->subdev.prepare = sysmon_prepare;
6578c2ecf20Sopenharmony_ci	sysmon->subdev.start = sysmon_start;
6588c2ecf20Sopenharmony_ci	sysmon->subdev.stop = sysmon_stop;
6598c2ecf20Sopenharmony_ci	sysmon->subdev.unprepare = sysmon_unprepare;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	rproc_add_subdev(rproc, &sysmon->subdev);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	sysmon->nb.notifier_call = sysmon_notify;
6648c2ecf20Sopenharmony_ci	blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	mutex_lock(&sysmon_lock);
6678c2ecf20Sopenharmony_ci	list_add(&sysmon->node, &sysmon_list);
6688c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon_lock);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return sysmon;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/**
6758c2ecf20Sopenharmony_ci * qcom_remove_sysmon_subdev() - release a qcom_sysmon
6768c2ecf20Sopenharmony_ci * @sysmon:	sysmon context, as retrieved by qcom_add_sysmon_subdev()
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_civoid qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	if (!sysmon)
6818c2ecf20Sopenharmony_ci		return;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	mutex_lock(&sysmon_lock);
6848c2ecf20Sopenharmony_ci	list_del(&sysmon->node);
6858c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon_lock);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	qmi_handle_release(&sysmon->qmi);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	kfree(sysmon);
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci/**
6988c2ecf20Sopenharmony_ci * sysmon_probe() - probe sys_mon channel
6998c2ecf20Sopenharmony_ci * @rpdev:	rpmsg device handle
7008c2ecf20Sopenharmony_ci *
7018c2ecf20Sopenharmony_ci * Find the sysmon context associated with the ancestor remoteproc and assign
7028c2ecf20Sopenharmony_ci * this rpmsg device with said sysmon context.
7038c2ecf20Sopenharmony_ci *
7048c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on failure.
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_cistatic int sysmon_probe(struct rpmsg_device *rpdev)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon;
7098c2ecf20Sopenharmony_ci	struct rproc *rproc;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	rproc = rproc_get_by_child(&rpdev->dev);
7128c2ecf20Sopenharmony_ci	if (!rproc) {
7138c2ecf20Sopenharmony_ci		dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
7148c2ecf20Sopenharmony_ci		return -EINVAL;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	mutex_lock(&sysmon_lock);
7188c2ecf20Sopenharmony_ci	list_for_each_entry(sysmon, &sysmon_list, node) {
7198c2ecf20Sopenharmony_ci		if (sysmon->rproc == rproc)
7208c2ecf20Sopenharmony_ci			goto found;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon_lock);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return -EINVAL;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cifound:
7298c2ecf20Sopenharmony_ci	mutex_unlock(&sysmon_lock);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	rpdev->ept->priv = sysmon;
7328c2ecf20Sopenharmony_ci	sysmon->ept = rpdev->ept;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return 0;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/**
7388c2ecf20Sopenharmony_ci * sysmon_remove() - sys_mon channel remove handler
7398c2ecf20Sopenharmony_ci * @rpdev:	rpmsg device handle
7408c2ecf20Sopenharmony_ci *
7418c2ecf20Sopenharmony_ci * Disassociate the rpmsg device with the sysmon instance.
7428c2ecf20Sopenharmony_ci */
7438c2ecf20Sopenharmony_cistatic void sysmon_remove(struct rpmsg_device *rpdev)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct qcom_sysmon *sysmon = rpdev->ept->priv;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	sysmon->ept = NULL;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic const struct rpmsg_device_id sysmon_match[] = {
7518c2ecf20Sopenharmony_ci	{ "sys_mon" },
7528c2ecf20Sopenharmony_ci	{}
7538c2ecf20Sopenharmony_ci};
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic struct rpmsg_driver sysmon_driver = {
7568c2ecf20Sopenharmony_ci	.probe = sysmon_probe,
7578c2ecf20Sopenharmony_ci	.remove = sysmon_remove,
7588c2ecf20Sopenharmony_ci	.callback = sysmon_callback,
7598c2ecf20Sopenharmony_ci	.id_table = sysmon_match,
7608c2ecf20Sopenharmony_ci	.drv = {
7618c2ecf20Sopenharmony_ci		.name = "qcom_sysmon",
7628c2ecf20Sopenharmony_ci	},
7638c2ecf20Sopenharmony_ci};
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cimodule_rpmsg_driver(sysmon_driver);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm sysmon driver");
7688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
769