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