xref: /linux/drivers/rpmsg/qcom_glink_ssr.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
193bc3feeSBjorn Andersson // SPDX-License-Identifier: GPL-2.0-only
293bc3feeSBjorn Andersson /*
393bc3feeSBjorn Andersson  * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
493bc3feeSBjorn Andersson  * Copyright (c) 2017, Linaro Ltd.
593bc3feeSBjorn Andersson  */
693bc3feeSBjorn Andersson 
793bc3feeSBjorn Andersson #include <linux/completion.h>
893bc3feeSBjorn Andersson #include <linux/module.h>
993bc3feeSBjorn Andersson #include <linux/notifier.h>
1093bc3feeSBjorn Andersson #include <linux/rpmsg.h>
113e35772bSAlex Elder #include <linux/rpmsg/qcom_glink.h>
1293bc3feeSBjorn Andersson #include <linux/remoteproc/qcom_rproc.h>
1393bc3feeSBjorn Andersson 
1493bc3feeSBjorn Andersson /**
1593bc3feeSBjorn Andersson  * struct do_cleanup_msg - The data structure for an SSR do_cleanup message
16df2f392cSAlex Elder  * @version:	The G-Link SSR protocol version
17df2f392cSAlex Elder  * @command:	The G-Link SSR command - do_cleanup
18df2f392cSAlex Elder  * @seq_num:	Sequence number
19df2f392cSAlex Elder  * @name_len:	Length of the name of the subsystem being restarted
20df2f392cSAlex Elder  * @name:	G-Link edge name of the subsystem being restarted
2193bc3feeSBjorn Andersson  */
2293bc3feeSBjorn Andersson struct do_cleanup_msg {
2393bc3feeSBjorn Andersson 	__le32 version;
2493bc3feeSBjorn Andersson 	__le32 command;
2593bc3feeSBjorn Andersson 	__le32 seq_num;
2693bc3feeSBjorn Andersson 	__le32 name_len;
2793bc3feeSBjorn Andersson 	char name[32];
2893bc3feeSBjorn Andersson };
2993bc3feeSBjorn Andersson 
3093bc3feeSBjorn Andersson /**
3193bc3feeSBjorn Andersson  * struct cleanup_done_msg - The data structure for an SSR cleanup_done message
32df2f392cSAlex Elder  * @version:	The G-Link SSR protocol version
33df2f392cSAlex Elder  * @response:	The G-Link SSR response to a do_cleanup command, cleanup_done
34df2f392cSAlex Elder  * @seq_num:	Sequence number
3593bc3feeSBjorn Andersson  */
3693bc3feeSBjorn Andersson struct cleanup_done_msg {
3793bc3feeSBjorn Andersson 	__le32 version;
3893bc3feeSBjorn Andersson 	__le32 response;
3993bc3feeSBjorn Andersson 	__le32 seq_num;
4093bc3feeSBjorn Andersson };
4193bc3feeSBjorn Andersson 
42101042f4SKrzysztof Kozlowski /*
4393bc3feeSBjorn Andersson  * G-Link SSR protocol commands
4493bc3feeSBjorn Andersson  */
4593bc3feeSBjorn Andersson #define GLINK_SSR_DO_CLEANUP	0
4693bc3feeSBjorn Andersson #define GLINK_SSR_CLEANUP_DONE	1
4793bc3feeSBjorn Andersson 
4893bc3feeSBjorn Andersson struct glink_ssr {
4993bc3feeSBjorn Andersson 	struct device *dev;
5093bc3feeSBjorn Andersson 	struct rpmsg_endpoint *ept;
5193bc3feeSBjorn Andersson 
5293bc3feeSBjorn Andersson 	struct notifier_block nb;
5393bc3feeSBjorn Andersson 
5493bc3feeSBjorn Andersson 	u32 seq_num;
5593bc3feeSBjorn Andersson 	struct completion completion;
5693bc3feeSBjorn Andersson };
5793bc3feeSBjorn Andersson 
5893bc3feeSBjorn Andersson /* Notifier list for all registered glink_ssr instances */
5993bc3feeSBjorn Andersson static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
6093bc3feeSBjorn Andersson 
6193bc3feeSBjorn Andersson /**
6293bc3feeSBjorn Andersson  * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc
6393bc3feeSBjorn Andersson  * @ssr_name:	name of the remoteproc that has been stopped
6493bc3feeSBjorn Andersson  */
6593bc3feeSBjorn Andersson void qcom_glink_ssr_notify(const char *ssr_name)
6693bc3feeSBjorn Andersson {
6793bc3feeSBjorn Andersson 	blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name);
6893bc3feeSBjorn Andersson }
6993bc3feeSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify);
7093bc3feeSBjorn Andersson 
7193bc3feeSBjorn Andersson static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev,
7293bc3feeSBjorn Andersson 				   void *data, int len, void *priv, u32 addr)
7393bc3feeSBjorn Andersson {
7493bc3feeSBjorn Andersson 	struct cleanup_done_msg *msg = data;
7593bc3feeSBjorn Andersson 	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
7693bc3feeSBjorn Andersson 
7793bc3feeSBjorn Andersson 	if (len < sizeof(*msg)) {
7893bc3feeSBjorn Andersson 		dev_err(ssr->dev, "message too short\n");
7993bc3feeSBjorn Andersson 		return -EINVAL;
8093bc3feeSBjorn Andersson 	}
8193bc3feeSBjorn Andersson 
8293bc3feeSBjorn Andersson 	if (le32_to_cpu(msg->version) != 0)
8393bc3feeSBjorn Andersson 		return -EINVAL;
8493bc3feeSBjorn Andersson 
8593bc3feeSBjorn Andersson 	if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE)
8693bc3feeSBjorn Andersson 		return 0;
8793bc3feeSBjorn Andersson 
8893bc3feeSBjorn Andersson 	if (le32_to_cpu(msg->seq_num) != ssr->seq_num) {
8993bc3feeSBjorn Andersson 		dev_err(ssr->dev, "invalid sequence number of response\n");
9093bc3feeSBjorn Andersson 		return -EINVAL;
9193bc3feeSBjorn Andersson 	}
9293bc3feeSBjorn Andersson 
9393bc3feeSBjorn Andersson 	complete(&ssr->completion);
9493bc3feeSBjorn Andersson 
9593bc3feeSBjorn Andersson 	return 0;
9693bc3feeSBjorn Andersson }
9793bc3feeSBjorn Andersson 
9893bc3feeSBjorn Andersson static int qcom_glink_ssr_notifier_call(struct notifier_block *nb,
9993bc3feeSBjorn Andersson 					unsigned long event,
10093bc3feeSBjorn Andersson 					void *data)
10193bc3feeSBjorn Andersson {
10293bc3feeSBjorn Andersson 	struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb);
10393bc3feeSBjorn Andersson 	struct do_cleanup_msg msg;
10493bc3feeSBjorn Andersson 	char *ssr_name = data;
10593bc3feeSBjorn Andersson 	int ret;
10693bc3feeSBjorn Andersson 
10793bc3feeSBjorn Andersson 	ssr->seq_num++;
10893bc3feeSBjorn Andersson 	reinit_completion(&ssr->completion);
10993bc3feeSBjorn Andersson 
11093bc3feeSBjorn Andersson 	memset(&msg, 0, sizeof(msg));
11193bc3feeSBjorn Andersson 	msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP);
11293bc3feeSBjorn Andersson 	msg.seq_num = cpu_to_le32(ssr->seq_num);
11393bc3feeSBjorn Andersson 	msg.name_len = cpu_to_le32(strlen(ssr_name));
114d2ff0f84SWolfram Sang 	strscpy(msg.name, ssr_name, sizeof(msg.name));
11593bc3feeSBjorn Andersson 
11693bc3feeSBjorn Andersson 	ret = rpmsg_send(ssr->ept, &msg, sizeof(msg));
11793bc3feeSBjorn Andersson 	if (ret < 0)
11893bc3feeSBjorn Andersson 		dev_err(ssr->dev, "failed to send cleanup message\n");
11993bc3feeSBjorn Andersson 
12093bc3feeSBjorn Andersson 	ret = wait_for_completion_timeout(&ssr->completion, HZ);
12193bc3feeSBjorn Andersson 	if (!ret)
12293bc3feeSBjorn Andersson 		dev_err(ssr->dev, "timeout waiting for cleanup done message\n");
12393bc3feeSBjorn Andersson 
12493bc3feeSBjorn Andersson 	return NOTIFY_DONE;
12593bc3feeSBjorn Andersson }
12693bc3feeSBjorn Andersson 
12793bc3feeSBjorn Andersson static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev)
12893bc3feeSBjorn Andersson {
12993bc3feeSBjorn Andersson 	struct glink_ssr *ssr;
13093bc3feeSBjorn Andersson 
13193bc3feeSBjorn Andersson 	ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL);
13293bc3feeSBjorn Andersson 	if (!ssr)
13393bc3feeSBjorn Andersson 		return -ENOMEM;
13493bc3feeSBjorn Andersson 
13593bc3feeSBjorn Andersson 	init_completion(&ssr->completion);
13693bc3feeSBjorn Andersson 
13793bc3feeSBjorn Andersson 	ssr->dev = &rpdev->dev;
13893bc3feeSBjorn Andersson 	ssr->ept = rpdev->ept;
13993bc3feeSBjorn Andersson 	ssr->nb.notifier_call = qcom_glink_ssr_notifier_call;
14093bc3feeSBjorn Andersson 
14193bc3feeSBjorn Andersson 	dev_set_drvdata(&rpdev->dev, ssr);
14293bc3feeSBjorn Andersson 
14393bc3feeSBjorn Andersson 	return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb);
14493bc3feeSBjorn Andersson }
14593bc3feeSBjorn Andersson 
14693bc3feeSBjorn Andersson static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev)
14793bc3feeSBjorn Andersson {
14893bc3feeSBjorn Andersson 	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
14993bc3feeSBjorn Andersson 
15093bc3feeSBjorn Andersson 	blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb);
15193bc3feeSBjorn Andersson }
15293bc3feeSBjorn Andersson 
15393bc3feeSBjorn Andersson static const struct rpmsg_device_id qcom_glink_ssr_match[] = {
15493bc3feeSBjorn Andersson 	{ "glink_ssr" },
15593bc3feeSBjorn Andersson 	{}
15693bc3feeSBjorn Andersson };
157*bcbab579SKrzysztof Kozlowski MODULE_DEVICE_TABLE(rpmsg, qcom_glink_ssr_match);
15893bc3feeSBjorn Andersson 
15993bc3feeSBjorn Andersson static struct rpmsg_driver qcom_glink_ssr_driver = {
16093bc3feeSBjorn Andersson 	.probe = qcom_glink_ssr_probe,
16193bc3feeSBjorn Andersson 	.remove = qcom_glink_ssr_remove,
16293bc3feeSBjorn Andersson 	.callback = qcom_glink_ssr_callback,
16393bc3feeSBjorn Andersson 	.id_table = qcom_glink_ssr_match,
16493bc3feeSBjorn Andersson 	.drv = {
16593bc3feeSBjorn Andersson 		.name = "qcom_glink_ssr",
16693bc3feeSBjorn Andersson 	},
16793bc3feeSBjorn Andersson };
16893bc3feeSBjorn Andersson module_rpmsg_driver(qcom_glink_ssr_driver);
169