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