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