162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017, Linaro Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/firmware.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/notifier.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/of_irq.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/remoteproc/qcom_rproc.h>
1462306a36Sopenharmony_ci#include <linux/rpmsg.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "qcom_common.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct qcom_sysmon {
2162306a36Sopenharmony_ci	struct rproc_subdev subdev;
2262306a36Sopenharmony_ci	struct rproc *rproc;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	int state;
2562306a36Sopenharmony_ci	struct mutex state_lock;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	struct list_head node;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	const char *name;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	int shutdown_irq;
3262306a36Sopenharmony_ci	int ssctl_version;
3362306a36Sopenharmony_ci	int ssctl_instance;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	struct notifier_block nb;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	struct device *dev;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	struct rpmsg_endpoint *ept;
4062306a36Sopenharmony_ci	struct completion comp;
4162306a36Sopenharmony_ci	struct completion ind_comp;
4262306a36Sopenharmony_ci	struct completion shutdown_comp;
4362306a36Sopenharmony_ci	struct completion ssctl_comp;
4462306a36Sopenharmony_ci	struct mutex lock;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	bool ssr_ack;
4762306a36Sopenharmony_ci	bool shutdown_acked;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	struct qmi_handle qmi;
5062306a36Sopenharmony_ci	struct sockaddr_qrtr ssctl;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cienum {
5462306a36Sopenharmony_ci	SSCTL_SSR_EVENT_BEFORE_POWERUP,
5562306a36Sopenharmony_ci	SSCTL_SSR_EVENT_AFTER_POWERUP,
5662306a36Sopenharmony_ci	SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
5762306a36Sopenharmony_ci	SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic const char * const sysmon_state_string[] = {
6162306a36Sopenharmony_ci	[SSCTL_SSR_EVENT_BEFORE_POWERUP]	= "before_powerup",
6262306a36Sopenharmony_ci	[SSCTL_SSR_EVENT_AFTER_POWERUP]		= "after_powerup",
6362306a36Sopenharmony_ci	[SSCTL_SSR_EVENT_BEFORE_SHUTDOWN]	= "before_shutdown",
6462306a36Sopenharmony_ci	[SSCTL_SSR_EVENT_AFTER_SHUTDOWN]	= "after_shutdown",
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct sysmon_event {
6862306a36Sopenharmony_ci	const char *subsys_name;
6962306a36Sopenharmony_ci	u32 ssr_event;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic DEFINE_MUTEX(sysmon_lock);
7362306a36Sopenharmony_cistatic LIST_HEAD(sysmon_list);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * sysmon_send_event() - send notification of other remote's SSR event
7762306a36Sopenharmony_ci * @sysmon:	sysmon context
7862306a36Sopenharmony_ci * @event:	sysmon event context
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic void sysmon_send_event(struct qcom_sysmon *sysmon,
8162306a36Sopenharmony_ci			      const struct sysmon_event *event)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	char req[50];
8462306a36Sopenharmony_ci	int len;
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name,
8862306a36Sopenharmony_ci		       sysmon_state_string[event->ssr_event]);
8962306a36Sopenharmony_ci	if (len >= sizeof(req))
9062306a36Sopenharmony_ci		return;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	mutex_lock(&sysmon->lock);
9362306a36Sopenharmony_ci	reinit_completion(&sysmon->comp);
9462306a36Sopenharmony_ci	sysmon->ssr_ack = false;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	ret = rpmsg_send(sysmon->ept, req, len);
9762306a36Sopenharmony_ci	if (ret < 0) {
9862306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to send sysmon event\n");
9962306a36Sopenharmony_ci		goto out_unlock;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&sysmon->comp,
10362306a36Sopenharmony_ci					  msecs_to_jiffies(5000));
10462306a36Sopenharmony_ci	if (!ret) {
10562306a36Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
10662306a36Sopenharmony_ci		goto out_unlock;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (!sysmon->ssr_ack)
11062306a36Sopenharmony_ci		dev_err(sysmon->dev, "unexpected response to sysmon event\n");
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciout_unlock:
11362306a36Sopenharmony_ci	mutex_unlock(&sysmon->lock);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * sysmon_request_shutdown() - request graceful shutdown of remote
11862306a36Sopenharmony_ci * @sysmon:	sysmon context
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * Return: boolean indicator of the remote processor acking the request
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic bool sysmon_request_shutdown(struct qcom_sysmon *sysmon)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	char *req = "ssr:shutdown";
12562306a36Sopenharmony_ci	bool acked = false;
12662306a36Sopenharmony_ci	int ret;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	mutex_lock(&sysmon->lock);
12962306a36Sopenharmony_ci	reinit_completion(&sysmon->comp);
13062306a36Sopenharmony_ci	sysmon->ssr_ack = false;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
13362306a36Sopenharmony_ci	if (ret < 0) {
13462306a36Sopenharmony_ci		dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
13562306a36Sopenharmony_ci		goto out_unlock;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&sysmon->comp,
13962306a36Sopenharmony_ci					  msecs_to_jiffies(5000));
14062306a36Sopenharmony_ci	if (!ret) {
14162306a36Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
14262306a36Sopenharmony_ci		goto out_unlock;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!sysmon->ssr_ack)
14662306a36Sopenharmony_ci		dev_err(sysmon->dev,
14762306a36Sopenharmony_ci			"unexpected response to sysmon shutdown request\n");
14862306a36Sopenharmony_ci	else
14962306a36Sopenharmony_ci		acked = true;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciout_unlock:
15262306a36Sopenharmony_ci	mutex_unlock(&sysmon->lock);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return acked;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
15862306a36Sopenharmony_ci			   void *priv, u32 addr)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = priv;
16162306a36Sopenharmony_ci	const char *ssr_ack = "ssr:ack";
16262306a36Sopenharmony_ci	const int ssr_ack_len = strlen(ssr_ack) + 1;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!sysmon)
16562306a36Sopenharmony_ci		return -EINVAL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
16862306a36Sopenharmony_ci		sysmon->ssr_ack = true;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	complete(&sysmon->comp);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define SSCTL_SHUTDOWN_REQ		0x21
17662306a36Sopenharmony_ci#define SSCTL_SHUTDOWN_READY_IND	0x21
17762306a36Sopenharmony_ci#define SSCTL_SUBSYS_EVENT_REQ		0x23
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci#define SSCTL_MAX_MSG_LEN		7
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#define SSCTL_SUBSYS_NAME_LENGTH	15
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cienum {
18462306a36Sopenharmony_ci	SSCTL_SSR_EVENT_FORCED,
18562306a36Sopenharmony_ci	SSCTL_SSR_EVENT_GRACEFUL,
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistruct ssctl_shutdown_resp {
18962306a36Sopenharmony_ci	struct qmi_response_type_v01 resp;
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
19362306a36Sopenharmony_ci	{
19462306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
19562306a36Sopenharmony_ci		.elem_len	= 1,
19662306a36Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
19762306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
19862306a36Sopenharmony_ci		.tlv_type	= 0x02,
19962306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_shutdown_resp, resp),
20062306a36Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
20162306a36Sopenharmony_ci	},
20262306a36Sopenharmony_ci	{}
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistruct ssctl_subsys_event_req {
20662306a36Sopenharmony_ci	u8 subsys_name_len;
20762306a36Sopenharmony_ci	char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
20862306a36Sopenharmony_ci	u32 event;
20962306a36Sopenharmony_ci	u8 evt_driven_valid;
21062306a36Sopenharmony_ci	u32 evt_driven;
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
21462306a36Sopenharmony_ci	{
21562306a36Sopenharmony_ci		.data_type	= QMI_DATA_LEN,
21662306a36Sopenharmony_ci		.elem_len	= 1,
21762306a36Sopenharmony_ci		.elem_size	= sizeof(uint8_t),
21862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
21962306a36Sopenharmony_ci		.tlv_type	= 0x01,
22062306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
22162306a36Sopenharmony_ci					   subsys_name_len),
22262306a36Sopenharmony_ci		.ei_array	= NULL,
22362306a36Sopenharmony_ci	},
22462306a36Sopenharmony_ci	{
22562306a36Sopenharmony_ci		.data_type	= QMI_UNSIGNED_1_BYTE,
22662306a36Sopenharmony_ci		.elem_len	= SSCTL_SUBSYS_NAME_LENGTH,
22762306a36Sopenharmony_ci		.elem_size	= sizeof(char),
22862306a36Sopenharmony_ci		.array_type	= VAR_LEN_ARRAY,
22962306a36Sopenharmony_ci		.tlv_type	= 0x01,
23062306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
23162306a36Sopenharmony_ci					   subsys_name),
23262306a36Sopenharmony_ci		.ei_array	= NULL,
23362306a36Sopenharmony_ci	},
23462306a36Sopenharmony_ci	{
23562306a36Sopenharmony_ci		.data_type	= QMI_SIGNED_4_BYTE_ENUM,
23662306a36Sopenharmony_ci		.elem_len	= 1,
23762306a36Sopenharmony_ci		.elem_size	= sizeof(uint32_t),
23862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
23962306a36Sopenharmony_ci		.tlv_type	= 0x02,
24062306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
24162306a36Sopenharmony_ci					   event),
24262306a36Sopenharmony_ci		.ei_array	= NULL,
24362306a36Sopenharmony_ci	},
24462306a36Sopenharmony_ci	{
24562306a36Sopenharmony_ci		.data_type	= QMI_OPT_FLAG,
24662306a36Sopenharmony_ci		.elem_len	= 1,
24762306a36Sopenharmony_ci		.elem_size	= sizeof(uint8_t),
24862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
24962306a36Sopenharmony_ci		.tlv_type	= 0x10,
25062306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
25162306a36Sopenharmony_ci					   evt_driven_valid),
25262306a36Sopenharmony_ci		.ei_array	= NULL,
25362306a36Sopenharmony_ci	},
25462306a36Sopenharmony_ci	{
25562306a36Sopenharmony_ci		.data_type	= QMI_SIGNED_4_BYTE_ENUM,
25662306a36Sopenharmony_ci		.elem_len	= 1,
25762306a36Sopenharmony_ci		.elem_size	= sizeof(uint32_t),
25862306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
25962306a36Sopenharmony_ci		.tlv_type	= 0x10,
26062306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_req,
26162306a36Sopenharmony_ci					   evt_driven),
26262306a36Sopenharmony_ci		.ei_array	= NULL,
26362306a36Sopenharmony_ci	},
26462306a36Sopenharmony_ci	{}
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistruct ssctl_subsys_event_resp {
26862306a36Sopenharmony_ci	struct qmi_response_type_v01 resp;
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
27262306a36Sopenharmony_ci	{
27362306a36Sopenharmony_ci		.data_type	= QMI_STRUCT,
27462306a36Sopenharmony_ci		.elem_len	= 1,
27562306a36Sopenharmony_ci		.elem_size	= sizeof(struct qmi_response_type_v01),
27662306a36Sopenharmony_ci		.array_type	= NO_ARRAY,
27762306a36Sopenharmony_ci		.tlv_type	= 0x02,
27862306a36Sopenharmony_ci		.offset		= offsetof(struct ssctl_subsys_event_resp,
27962306a36Sopenharmony_ci					   resp),
28062306a36Sopenharmony_ci		.ei_array	= qmi_response_type_v01_ei,
28162306a36Sopenharmony_ci	},
28262306a36Sopenharmony_ci	{}
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_shutdown_ind_ei[] = {
28662306a36Sopenharmony_ci	{}
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
29062306a36Sopenharmony_ci			  struct qmi_txn *txn, const void *data)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	complete(&sysmon->ind_comp);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic const struct qmi_msg_handler qmi_indication_handler[] = {
29862306a36Sopenharmony_ci	{
29962306a36Sopenharmony_ci		.type = QMI_INDICATION,
30062306a36Sopenharmony_ci		.msg_id = SSCTL_SHUTDOWN_READY_IND,
30162306a36Sopenharmony_ci		.ei = ssctl_shutdown_ind_ei,
30262306a36Sopenharmony_ci		.decoded_size = 0,
30362306a36Sopenharmony_ci		.fn = sysmon_ind_cb
30462306a36Sopenharmony_ci	},
30562306a36Sopenharmony_ci	{}
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic bool ssctl_request_shutdown_wait(struct qcom_sysmon *sysmon)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int ret;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&sysmon->shutdown_comp, 10 * HZ);
31362306a36Sopenharmony_ci	if (ret)
31462306a36Sopenharmony_ci		return true;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ret = try_wait_for_completion(&sysmon->ind_comp);
31762306a36Sopenharmony_ci	if (ret)
31862306a36Sopenharmony_ci		return true;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	dev_err(sysmon->dev, "timeout waiting for shutdown ack\n");
32162306a36Sopenharmony_ci	return false;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/**
32562306a36Sopenharmony_ci * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
32662306a36Sopenharmony_ci * @sysmon:	sysmon context
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * Return: boolean indicator of the remote processor acking the request
32962306a36Sopenharmony_ci */
33062306a36Sopenharmony_cistatic bool ssctl_request_shutdown(struct qcom_sysmon *sysmon)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct ssctl_shutdown_resp resp;
33362306a36Sopenharmony_ci	struct qmi_txn txn;
33462306a36Sopenharmony_ci	bool acked = false;
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	reinit_completion(&sysmon->ind_comp);
33862306a36Sopenharmony_ci	reinit_completion(&sysmon->shutdown_comp);
33962306a36Sopenharmony_ci	ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
34062306a36Sopenharmony_ci	if (ret < 0) {
34162306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to allocate QMI txn\n");
34262306a36Sopenharmony_ci		return false;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
34662306a36Sopenharmony_ci			       SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
34762306a36Sopenharmony_ci	if (ret < 0) {
34862306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to send shutdown request\n");
34962306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
35062306a36Sopenharmony_ci		return false;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
35462306a36Sopenharmony_ci	if (ret < 0) {
35562306a36Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for shutdown response\n");
35662306a36Sopenharmony_ci	} else if (resp.resp.result) {
35762306a36Sopenharmony_ci		dev_err(sysmon->dev, "shutdown request rejected\n");
35862306a36Sopenharmony_ci	} else {
35962306a36Sopenharmony_ci		dev_dbg(sysmon->dev, "shutdown request completed\n");
36062306a36Sopenharmony_ci		acked = true;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (sysmon->shutdown_irq > 0)
36462306a36Sopenharmony_ci		return ssctl_request_shutdown_wait(sysmon);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return acked;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/**
37062306a36Sopenharmony_ci * ssctl_send_event() - send notification of other remote's SSR event
37162306a36Sopenharmony_ci * @sysmon:	sysmon context
37262306a36Sopenharmony_ci * @event:	sysmon event context
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_cistatic void ssctl_send_event(struct qcom_sysmon *sysmon,
37562306a36Sopenharmony_ci			     const struct sysmon_event *event)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct ssctl_subsys_event_resp resp;
37862306a36Sopenharmony_ci	struct ssctl_subsys_event_req req;
37962306a36Sopenharmony_ci	struct qmi_txn txn;
38062306a36Sopenharmony_ci	int ret;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	memset(&resp, 0, sizeof(resp));
38362306a36Sopenharmony_ci	ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
38462306a36Sopenharmony_ci	if (ret < 0) {
38562306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to allocate QMI txn\n");
38662306a36Sopenharmony_ci		return;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
39062306a36Sopenharmony_ci	strscpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name));
39162306a36Sopenharmony_ci	req.subsys_name_len = strlen(req.subsys_name);
39262306a36Sopenharmony_ci	req.event = event->ssr_event;
39362306a36Sopenharmony_ci	req.evt_driven_valid = true;
39462306a36Sopenharmony_ci	req.evt_driven = SSCTL_SSR_EVENT_FORCED;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
39762306a36Sopenharmony_ci			       SSCTL_SUBSYS_EVENT_REQ, 40,
39862306a36Sopenharmony_ci			       ssctl_subsys_event_req_ei, &req);
39962306a36Sopenharmony_ci	if (ret < 0) {
40062306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to send subsystem event\n");
40162306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
40262306a36Sopenharmony_ci		return;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
40662306a36Sopenharmony_ci	if (ret < 0)
40762306a36Sopenharmony_ci		dev_err(sysmon->dev, "timeout waiting for subsystem event response\n");
40862306a36Sopenharmony_ci	else if (resp.resp.result)
40962306a36Sopenharmony_ci		dev_err(sysmon->dev, "subsystem event rejected\n");
41062306a36Sopenharmony_ci	else
41162306a36Sopenharmony_ci		dev_dbg(sysmon->dev, "subsystem event accepted\n");
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * ssctl_new_server() - QMI callback indicating a new service
41662306a36Sopenharmony_ci * @qmi:	QMI handle
41762306a36Sopenharmony_ci * @svc:	service information
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * Return: 0 if we're interested in this service, -EINVAL otherwise.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistatic int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	switch (svc->version) {
42662306a36Sopenharmony_ci	case 1:
42762306a36Sopenharmony_ci		if (svc->instance != 0)
42862306a36Sopenharmony_ci			return -EINVAL;
42962306a36Sopenharmony_ci		if (strcmp(sysmon->name, "modem"))
43062306a36Sopenharmony_ci			return -EINVAL;
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case 2:
43362306a36Sopenharmony_ci		if (svc->instance != sysmon->ssctl_instance)
43462306a36Sopenharmony_ci			return -EINVAL;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	default:
43762306a36Sopenharmony_ci		return -EINVAL;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	sysmon->ssctl_version = svc->version;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	sysmon->ssctl.sq_family = AF_QIPCRTR;
44362306a36Sopenharmony_ci	sysmon->ssctl.sq_node = svc->node;
44462306a36Sopenharmony_ci	sysmon->ssctl.sq_port = svc->port;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	svc->priv = sysmon;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	complete(&sysmon->ssctl_comp);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * ssctl_del_server() - QMI callback indicating that @svc is removed
45562306a36Sopenharmony_ci * @qmi:	QMI handle
45662306a36Sopenharmony_ci * @svc:	service information
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_cistatic void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = svc->priv;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	sysmon->ssctl_version = 0;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic const struct qmi_ops ssctl_ops = {
46662306a36Sopenharmony_ci	.new_server = ssctl_new_server,
46762306a36Sopenharmony_ci	.del_server = ssctl_del_server,
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int sysmon_prepare(struct rproc_subdev *subdev)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
47362306a36Sopenharmony_ci						  subdev);
47462306a36Sopenharmony_ci	struct sysmon_event event = {
47562306a36Sopenharmony_ci		.subsys_name = sysmon->name,
47662306a36Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP
47762306a36Sopenharmony_ci	};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
48062306a36Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_BEFORE_POWERUP;
48162306a36Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
48262306a36Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/**
48862306a36Sopenharmony_ci * sysmon_start() - start callback for the sysmon remoteproc subdevice
48962306a36Sopenharmony_ci * @subdev:	instance of the sysmon subdevice
49062306a36Sopenharmony_ci *
49162306a36Sopenharmony_ci * Inform all the listners of sysmon notifications that the rproc associated
49262306a36Sopenharmony_ci * to @subdev has booted up. The rproc that booted up also needs to know
49362306a36Sopenharmony_ci * which rprocs are already up and running, so send start notifications
49462306a36Sopenharmony_ci * on behalf of all the online rprocs.
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_cistatic int sysmon_start(struct rproc_subdev *subdev)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
49962306a36Sopenharmony_ci						  subdev);
50062306a36Sopenharmony_ci	struct qcom_sysmon *target;
50162306a36Sopenharmony_ci	struct sysmon_event event = {
50262306a36Sopenharmony_ci		.subsys_name = sysmon->name,
50362306a36Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP
50462306a36Sopenharmony_ci	};
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	reinit_completion(&sysmon->ssctl_comp);
50762306a36Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
50862306a36Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP;
50962306a36Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
51062306a36Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	mutex_lock(&sysmon_lock);
51362306a36Sopenharmony_ci	list_for_each_entry(target, &sysmon_list, node) {
51462306a36Sopenharmony_ci		mutex_lock(&target->state_lock);
51562306a36Sopenharmony_ci		if (target == sysmon || target->state != SSCTL_SSR_EVENT_AFTER_POWERUP) {
51662306a36Sopenharmony_ci			mutex_unlock(&target->state_lock);
51762306a36Sopenharmony_ci			continue;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		event.subsys_name = target->name;
52162306a36Sopenharmony_ci		event.ssr_event = target->state;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		if (sysmon->ssctl_version == 2)
52462306a36Sopenharmony_ci			ssctl_send_event(sysmon, &event);
52562306a36Sopenharmony_ci		else if (sysmon->ept)
52662306a36Sopenharmony_ci			sysmon_send_event(sysmon, &event);
52762306a36Sopenharmony_ci		mutex_unlock(&target->state_lock);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	mutex_unlock(&sysmon_lock);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
53762306a36Sopenharmony_ci	struct sysmon_event event = {
53862306a36Sopenharmony_ci		.subsys_name = sysmon->name,
53962306a36Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN
54062306a36Sopenharmony_ci	};
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	sysmon->shutdown_acked = false;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
54562306a36Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
54662306a36Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
54762306a36Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* Don't request graceful shutdown if we've crashed */
55062306a36Sopenharmony_ci	if (crashed)
55162306a36Sopenharmony_ci		return;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (sysmon->ssctl_instance) {
55462306a36Sopenharmony_ci		if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2))
55562306a36Sopenharmony_ci			dev_err(sysmon->dev, "timeout waiting for ssctl service\n");
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (sysmon->ssctl_version)
55962306a36Sopenharmony_ci		sysmon->shutdown_acked = ssctl_request_shutdown(sysmon);
56062306a36Sopenharmony_ci	else if (sysmon->ept)
56162306a36Sopenharmony_ci		sysmon->shutdown_acked = sysmon_request_shutdown(sysmon);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void sysmon_unprepare(struct rproc_subdev *subdev)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
56762306a36Sopenharmony_ci						  subdev);
56862306a36Sopenharmony_ci	struct sysmon_event event = {
56962306a36Sopenharmony_ci		.subsys_name = sysmon->name,
57062306a36Sopenharmony_ci		.ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN
57162306a36Sopenharmony_ci	};
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	mutex_lock(&sysmon->state_lock);
57462306a36Sopenharmony_ci	sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN;
57562306a36Sopenharmony_ci	blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
57662306a36Sopenharmony_ci	mutex_unlock(&sysmon->state_lock);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * sysmon_notify() - notify sysmon target of another's SSR
58162306a36Sopenharmony_ci * @nb:		notifier_block associated with sysmon instance
58262306a36Sopenharmony_ci * @event:	unused
58362306a36Sopenharmony_ci * @data:	SSR identifier of the remote that is going down
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_cistatic int sysmon_notify(struct notifier_block *nb, unsigned long event,
58662306a36Sopenharmony_ci			 void *data)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
58962306a36Sopenharmony_ci	struct sysmon_event *sysmon_event = data;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* Skip non-running rprocs and the originating instance */
59262306a36Sopenharmony_ci	if (sysmon->state != SSCTL_SSR_EVENT_AFTER_POWERUP ||
59362306a36Sopenharmony_ci	    !strcmp(sysmon_event->subsys_name, sysmon->name)) {
59462306a36Sopenharmony_ci		dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
59562306a36Sopenharmony_ci		return NOTIFY_DONE;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* Only SSCTL version 2 supports SSR events */
59962306a36Sopenharmony_ci	if (sysmon->ssctl_version == 2)
60062306a36Sopenharmony_ci		ssctl_send_event(sysmon, sysmon_event);
60162306a36Sopenharmony_ci	else if (sysmon->ept)
60262306a36Sopenharmony_ci		sysmon_send_event(sysmon, sysmon_event);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return NOTIFY_DONE;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic irqreturn_t sysmon_shutdown_interrupt(int irq, void *data)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = data;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	complete(&sysmon->shutdown_comp);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return IRQ_HANDLED;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci/**
61762306a36Sopenharmony_ci * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
61862306a36Sopenharmony_ci * @rproc:	rproc context to associate the subdev with
61962306a36Sopenharmony_ci * @name:	name of this subdev, to use in SSR
62062306a36Sopenharmony_ci * @ssctl_instance: instance id of the ssctl QMI service
62162306a36Sopenharmony_ci *
62262306a36Sopenharmony_ci * Return: A new qcom_sysmon object, or NULL on failure
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_cistruct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
62562306a36Sopenharmony_ci					   const char *name,
62662306a36Sopenharmony_ci					   int ssctl_instance)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct qcom_sysmon *sysmon;
62962306a36Sopenharmony_ci	int ret;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
63262306a36Sopenharmony_ci	if (!sysmon)
63362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	sysmon->dev = rproc->dev.parent;
63662306a36Sopenharmony_ci	sysmon->rproc = rproc;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	sysmon->name = name;
63962306a36Sopenharmony_ci	sysmon->ssctl_instance = ssctl_instance;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	init_completion(&sysmon->comp);
64262306a36Sopenharmony_ci	init_completion(&sysmon->ind_comp);
64362306a36Sopenharmony_ci	init_completion(&sysmon->shutdown_comp);
64462306a36Sopenharmony_ci	init_completion(&sysmon->ssctl_comp);
64562306a36Sopenharmony_ci	mutex_init(&sysmon->lock);
64662306a36Sopenharmony_ci	mutex_init(&sysmon->state_lock);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node,
64962306a36Sopenharmony_ci						 "shutdown-ack");
65062306a36Sopenharmony_ci	if (sysmon->shutdown_irq < 0) {
65162306a36Sopenharmony_ci		if (sysmon->shutdown_irq != -ENODATA) {
65262306a36Sopenharmony_ci			dev_err(sysmon->dev,
65362306a36Sopenharmony_ci				"failed to retrieve shutdown-ack IRQ\n");
65462306a36Sopenharmony_ci			ret = sysmon->shutdown_irq;
65562306a36Sopenharmony_ci			kfree(sysmon);
65662306a36Sopenharmony_ci			return ERR_PTR(ret);
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci	} else {
65962306a36Sopenharmony_ci		ret = devm_request_threaded_irq(sysmon->dev,
66062306a36Sopenharmony_ci						sysmon->shutdown_irq,
66162306a36Sopenharmony_ci						NULL, sysmon_shutdown_interrupt,
66262306a36Sopenharmony_ci						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
66362306a36Sopenharmony_ci						"q6v5 shutdown-ack", sysmon);
66462306a36Sopenharmony_ci		if (ret) {
66562306a36Sopenharmony_ci			dev_err(sysmon->dev,
66662306a36Sopenharmony_ci				"failed to acquire shutdown-ack IRQ\n");
66762306a36Sopenharmony_ci			kfree(sysmon);
66862306a36Sopenharmony_ci			return ERR_PTR(ret);
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops,
67362306a36Sopenharmony_ci			      qmi_indication_handler);
67462306a36Sopenharmony_ci	if (ret < 0) {
67562306a36Sopenharmony_ci		dev_err(sysmon->dev, "failed to initialize qmi handle\n");
67662306a36Sopenharmony_ci		kfree(sysmon);
67762306a36Sopenharmony_ci		return ERR_PTR(ret);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	sysmon->subdev.prepare = sysmon_prepare;
68362306a36Sopenharmony_ci	sysmon->subdev.start = sysmon_start;
68462306a36Sopenharmony_ci	sysmon->subdev.stop = sysmon_stop;
68562306a36Sopenharmony_ci	sysmon->subdev.unprepare = sysmon_unprepare;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	rproc_add_subdev(rproc, &sysmon->subdev);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	sysmon->nb.notifier_call = sysmon_notify;
69062306a36Sopenharmony_ci	blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	mutex_lock(&sysmon_lock);
69362306a36Sopenharmony_ci	list_add(&sysmon->node, &sysmon_list);
69462306a36Sopenharmony_ci	mutex_unlock(&sysmon_lock);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return sysmon;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/**
70162306a36Sopenharmony_ci * qcom_remove_sysmon_subdev() - release a qcom_sysmon
70262306a36Sopenharmony_ci * @sysmon:	sysmon context, as retrieved by qcom_add_sysmon_subdev()
70362306a36Sopenharmony_ci */
70462306a36Sopenharmony_civoid qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	if (!sysmon)
70762306a36Sopenharmony_ci		return;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	mutex_lock(&sysmon_lock);
71062306a36Sopenharmony_ci	list_del(&sysmon->node);
71162306a36Sopenharmony_ci	mutex_unlock(&sysmon_lock);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	qmi_handle_release(&sysmon->qmi);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	kfree(sysmon);
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci/**
72462306a36Sopenharmony_ci * qcom_sysmon_shutdown_acked() - query the success of the last shutdown
72562306a36Sopenharmony_ci * @sysmon:	sysmon context
72662306a36Sopenharmony_ci *
72762306a36Sopenharmony_ci * When sysmon is used to request a graceful shutdown of the remote processor
72862306a36Sopenharmony_ci * this can be used by the remoteproc driver to query the success, in order to
72962306a36Sopenharmony_ci * know if it should fall back to other means of requesting a shutdown.
73062306a36Sopenharmony_ci *
73162306a36Sopenharmony_ci * Return: boolean indicator of the success of the last shutdown request
73262306a36Sopenharmony_ci */
73362306a36Sopenharmony_cibool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	return sysmon && sysmon->shutdown_acked;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/**
74062306a36Sopenharmony_ci * sysmon_probe() - probe sys_mon channel
74162306a36Sopenharmony_ci * @rpdev:	rpmsg device handle
74262306a36Sopenharmony_ci *
74362306a36Sopenharmony_ci * Find the sysmon context associated with the ancestor remoteproc and assign
74462306a36Sopenharmony_ci * this rpmsg device with said sysmon context.
74562306a36Sopenharmony_ci *
74662306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure.
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic int sysmon_probe(struct rpmsg_device *rpdev)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct qcom_sysmon *sysmon;
75162306a36Sopenharmony_ci	struct rproc *rproc;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	rproc = rproc_get_by_child(&rpdev->dev);
75462306a36Sopenharmony_ci	if (!rproc) {
75562306a36Sopenharmony_ci		dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
75662306a36Sopenharmony_ci		return -EINVAL;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	mutex_lock(&sysmon_lock);
76062306a36Sopenharmony_ci	list_for_each_entry(sysmon, &sysmon_list, node) {
76162306a36Sopenharmony_ci		if (sysmon->rproc == rproc)
76262306a36Sopenharmony_ci			goto found;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	mutex_unlock(&sysmon_lock);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return -EINVAL;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cifound:
77162306a36Sopenharmony_ci	mutex_unlock(&sysmon_lock);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	rpdev->ept->priv = sysmon;
77462306a36Sopenharmony_ci	sysmon->ept = rpdev->ept;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/**
78062306a36Sopenharmony_ci * sysmon_remove() - sys_mon channel remove handler
78162306a36Sopenharmony_ci * @rpdev:	rpmsg device handle
78262306a36Sopenharmony_ci *
78362306a36Sopenharmony_ci * Disassociate the rpmsg device with the sysmon instance.
78462306a36Sopenharmony_ci */
78562306a36Sopenharmony_cistatic void sysmon_remove(struct rpmsg_device *rpdev)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct qcom_sysmon *sysmon = rpdev->ept->priv;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	sysmon->ept = NULL;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic const struct rpmsg_device_id sysmon_match[] = {
79362306a36Sopenharmony_ci	{ "sys_mon" },
79462306a36Sopenharmony_ci	{}
79562306a36Sopenharmony_ci};
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic struct rpmsg_driver sysmon_driver = {
79862306a36Sopenharmony_ci	.probe = sysmon_probe,
79962306a36Sopenharmony_ci	.remove = sysmon_remove,
80062306a36Sopenharmony_ci	.callback = sysmon_callback,
80162306a36Sopenharmony_ci	.id_table = sysmon_match,
80262306a36Sopenharmony_ci	.drv = {
80362306a36Sopenharmony_ci		.name = "qcom_sysmon",
80462306a36Sopenharmony_ci	},
80562306a36Sopenharmony_ci};
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cimodule_rpmsg_driver(sysmon_driver);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm sysmon driver");
81062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
811