xref: /linux/drivers/rpmsg/qcom_glink_ssr.c (revision 93bc3feee8bd5fbe29ad27779c5e7b369fd7c80b)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c4d77d5fSBjorn Andersson /*
3c4d77d5fSBjorn Andersson  * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
4c4d77d5fSBjorn Andersson  * Copyright (c) 2017, Linaro Ltd.
5c4d77d5fSBjorn Andersson  */
6c4d77d5fSBjorn Andersson 
7c4d77d5fSBjorn Andersson #include <linux/completion.h>
8c4d77d5fSBjorn Andersson #include <linux/module.h>
9c4d77d5fSBjorn Andersson #include <linux/notifier.h>
10c4d77d5fSBjorn Andersson #include <linux/rpmsg.h>
11c4d77d5fSBjorn Andersson #include <linux/remoteproc/qcom_rproc.h>
12c4d77d5fSBjorn Andersson 
13c4d77d5fSBjorn Andersson /**
14c4d77d5fSBjorn Andersson  * struct do_cleanup_msg - The data structure for an SSR do_cleanup message
15c4d77d5fSBjorn Andersson  * version:     The G-Link SSR protocol version
16c4d77d5fSBjorn Andersson  * command:     The G-Link SSR command - do_cleanup
17c4d77d5fSBjorn Andersson  * seq_num:     Sequence number
18c4d77d5fSBjorn Andersson  * name_len:    Length of the name of the subsystem being restarted
19c4d77d5fSBjorn Andersson  * name:        G-Link edge name of the subsystem being restarted
20c4d77d5fSBjorn Andersson  */
21c4d77d5fSBjorn Andersson struct do_cleanup_msg {
22c4d77d5fSBjorn Andersson 	__le32 version;
23c4d77d5fSBjorn Andersson 	__le32 command;
24c4d77d5fSBjorn Andersson 	__le32 seq_num;
25c4d77d5fSBjorn Andersson 	__le32 name_len;
26c4d77d5fSBjorn Andersson 	char name[32];
27c4d77d5fSBjorn Andersson };
28c4d77d5fSBjorn Andersson 
29c4d77d5fSBjorn Andersson /**
30c4d77d5fSBjorn Andersson  * struct cleanup_done_msg - The data structure for an SSR cleanup_done message
31c4d77d5fSBjorn Andersson  * version:     The G-Link SSR protocol version
32c4d77d5fSBjorn Andersson  * response:    The G-Link SSR response to a do_cleanup command, cleanup_done
33c4d77d5fSBjorn Andersson  * seq_num:     Sequence number
34c4d77d5fSBjorn Andersson  */
35c4d77d5fSBjorn Andersson struct cleanup_done_msg {
36c4d77d5fSBjorn Andersson 	__le32 version;
37c4d77d5fSBjorn Andersson 	__le32 response;
38c4d77d5fSBjorn Andersson 	__le32 seq_num;
39c4d77d5fSBjorn Andersson };
40c4d77d5fSBjorn Andersson 
41c4d77d5fSBjorn Andersson /**
42c4d77d5fSBjorn Andersson  * G-Link SSR protocol commands
43c4d77d5fSBjorn Andersson  */
44c4d77d5fSBjorn Andersson #define GLINK_SSR_DO_CLEANUP	0
45c4d77d5fSBjorn Andersson #define GLINK_SSR_CLEANUP_DONE	1
46c4d77d5fSBjorn Andersson 
47c4d77d5fSBjorn Andersson struct glink_ssr {
48c4d77d5fSBjorn Andersson 	struct device *dev;
49c4d77d5fSBjorn Andersson 	struct rpmsg_endpoint *ept;
50c4d77d5fSBjorn Andersson 
51c4d77d5fSBjorn Andersson 	struct notifier_block nb;
52c4d77d5fSBjorn Andersson 
53c4d77d5fSBjorn Andersson 	u32 seq_num;
54c4d77d5fSBjorn Andersson 	struct completion completion;
55c4d77d5fSBjorn Andersson };
56c4d77d5fSBjorn Andersson 
57*5d1f2e3cSBjorn Andersson /* Notifier list for all registered glink_ssr instances */
58*5d1f2e3cSBjorn Andersson static BLOCKING_NOTIFIER_HEAD(ssr_notifiers);
59*5d1f2e3cSBjorn Andersson 
60*5d1f2e3cSBjorn Andersson /**
61*5d1f2e3cSBjorn Andersson  * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc
62*5d1f2e3cSBjorn Andersson  * @ssr_name:	name of the remoteproc that has been stopped
63*5d1f2e3cSBjorn Andersson  */
64*5d1f2e3cSBjorn Andersson void qcom_glink_ssr_notify(const char *ssr_name)
65*5d1f2e3cSBjorn Andersson {
66*5d1f2e3cSBjorn Andersson 	blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name);
67*5d1f2e3cSBjorn Andersson }
68*5d1f2e3cSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify);
69*5d1f2e3cSBjorn Andersson 
70c4d77d5fSBjorn Andersson static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev,
71c4d77d5fSBjorn Andersson 				   void *data, int len, void *priv, u32 addr)
72c4d77d5fSBjorn Andersson {
73c4d77d5fSBjorn Andersson 	struct cleanup_done_msg *msg = data;
74c4d77d5fSBjorn Andersson 	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
75c4d77d5fSBjorn Andersson 
76c4d77d5fSBjorn Andersson 	if (len < sizeof(*msg)) {
77c4d77d5fSBjorn Andersson 		dev_err(ssr->dev, "message too short\n");
78c4d77d5fSBjorn Andersson 		return -EINVAL;
79c4d77d5fSBjorn Andersson 	}
80c4d77d5fSBjorn Andersson 
81c4d77d5fSBjorn Andersson 	if (le32_to_cpu(msg->version) != 0)
82c4d77d5fSBjorn Andersson 		return -EINVAL;
83c4d77d5fSBjorn Andersson 
84c4d77d5fSBjorn Andersson 	if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE)
85c4d77d5fSBjorn Andersson 		return 0;
86c4d77d5fSBjorn Andersson 
87c4d77d5fSBjorn Andersson 	if (le32_to_cpu(msg->seq_num) != ssr->seq_num) {
88c4d77d5fSBjorn Andersson 		dev_err(ssr->dev, "invalid sequence number of response\n");
89c4d77d5fSBjorn Andersson 		return -EINVAL;
90c4d77d5fSBjorn Andersson 	}
91c4d77d5fSBjorn Andersson 
92c4d77d5fSBjorn Andersson 	complete(&ssr->completion);
93c4d77d5fSBjorn Andersson 
94c4d77d5fSBjorn Andersson 	return 0;
95c4d77d5fSBjorn Andersson }
96c4d77d5fSBjorn Andersson 
97*5d1f2e3cSBjorn Andersson static int qcom_glink_ssr_notifier_call(struct notifier_block *nb,
98*5d1f2e3cSBjorn Andersson 					unsigned long event,
99c4d77d5fSBjorn Andersson 					void *data)
100c4d77d5fSBjorn Andersson {
101c4d77d5fSBjorn Andersson 	struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb);
102c4d77d5fSBjorn Andersson 	struct do_cleanup_msg msg;
103c4d77d5fSBjorn Andersson 	char *ssr_name = data;
104c4d77d5fSBjorn Andersson 	int ret;
105c4d77d5fSBjorn Andersson 
106c4d77d5fSBjorn Andersson 	ssr->seq_num++;
107c4d77d5fSBjorn Andersson 	reinit_completion(&ssr->completion);
108c4d77d5fSBjorn Andersson 
109c4d77d5fSBjorn Andersson 	memset(&msg, 0, sizeof(msg));
110c4d77d5fSBjorn Andersson 	msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP);
111c4d77d5fSBjorn Andersson 	msg.seq_num = cpu_to_le32(ssr->seq_num);
112c4d77d5fSBjorn Andersson 	msg.name_len = cpu_to_le32(strlen(ssr_name));
113c4d77d5fSBjorn Andersson 	strlcpy(msg.name, ssr_name, sizeof(msg.name));
114c4d77d5fSBjorn Andersson 
115c4d77d5fSBjorn Andersson 	ret = rpmsg_send(ssr->ept, &msg, sizeof(msg));
116c4d77d5fSBjorn Andersson 	if (ret < 0)
117c4d77d5fSBjorn Andersson 		dev_err(ssr->dev, "failed to send cleanup message\n");
118c4d77d5fSBjorn Andersson 
119c4d77d5fSBjorn Andersson 	ret = wait_for_completion_timeout(&ssr->completion, HZ);
120c4d77d5fSBjorn Andersson 	if (!ret)
121c4d77d5fSBjorn Andersson 		dev_err(ssr->dev, "timeout waiting for cleanup done message\n");
122c4d77d5fSBjorn Andersson 
123c4d77d5fSBjorn Andersson 	return NOTIFY_DONE;
124c4d77d5fSBjorn Andersson }
125c4d77d5fSBjorn Andersson 
126c4d77d5fSBjorn Andersson static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev)
127c4d77d5fSBjorn Andersson {
128c4d77d5fSBjorn Andersson 	struct glink_ssr *ssr;
129c4d77d5fSBjorn Andersson 
130c4d77d5fSBjorn Andersson 	ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL);
131c4d77d5fSBjorn Andersson 	if (!ssr)
132c4d77d5fSBjorn Andersson 		return -ENOMEM;
133c4d77d5fSBjorn Andersson 
134c4d77d5fSBjorn Andersson 	init_completion(&ssr->completion);
135c4d77d5fSBjorn Andersson 
136c4d77d5fSBjorn Andersson 	ssr->dev = &rpdev->dev;
137c4d77d5fSBjorn Andersson 	ssr->ept = rpdev->ept;
138*5d1f2e3cSBjorn Andersson 	ssr->nb.notifier_call = qcom_glink_ssr_notifier_call;
139c4d77d5fSBjorn Andersson 
140c4d77d5fSBjorn Andersson 	dev_set_drvdata(&rpdev->dev, ssr);
141c4d77d5fSBjorn Andersson 
142*5d1f2e3cSBjorn Andersson 	return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb);
143c4d77d5fSBjorn Andersson }
144c4d77d5fSBjorn Andersson 
145c4d77d5fSBjorn Andersson static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev)
146c4d77d5fSBjorn Andersson {
147c4d77d5fSBjorn Andersson 	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
148c4d77d5fSBjorn Andersson 
149*5d1f2e3cSBjorn Andersson 	blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb);
150c4d77d5fSBjorn Andersson }
151c4d77d5fSBjorn Andersson 
152c4d77d5fSBjorn Andersson static const struct rpmsg_device_id qcom_glink_ssr_match[] = {
153c4d77d5fSBjorn Andersson 	{ "glink_ssr" },
154c4d77d5fSBjorn Andersson 	{}
155c4d77d5fSBjorn Andersson };
156c4d77d5fSBjorn Andersson 
157c4d77d5fSBjorn Andersson static struct rpmsg_driver qcom_glink_ssr_driver = {
158c4d77d5fSBjorn Andersson 	.probe = qcom_glink_ssr_probe,
159c4d77d5fSBjorn Andersson 	.remove = qcom_glink_ssr_remove,
160c4d77d5fSBjorn Andersson 	.callback = qcom_glink_ssr_callback,
161c4d77d5fSBjorn Andersson 	.id_table = qcom_glink_ssr_match,
162c4d77d5fSBjorn Andersson 	.drv = {
163c4d77d5fSBjorn Andersson 		.name = "qcom_glink_ssr",
164c4d77d5fSBjorn Andersson 	},
165c4d77d5fSBjorn Andersson };
166c4d77d5fSBjorn Andersson module_rpmsg_driver(qcom_glink_ssr_driver);
167