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