18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (c) 2017, Linaro Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/completion.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/notifier.h> 108c2ecf20Sopenharmony_ci#include <linux/rpmsg.h> 118c2ecf20Sopenharmony_ci#include <linux/remoteproc/qcom_rproc.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/** 148c2ecf20Sopenharmony_ci * struct do_cleanup_msg - The data structure for an SSR do_cleanup message 158c2ecf20Sopenharmony_ci * version: The G-Link SSR protocol version 168c2ecf20Sopenharmony_ci * command: The G-Link SSR command - do_cleanup 178c2ecf20Sopenharmony_ci * seq_num: Sequence number 188c2ecf20Sopenharmony_ci * name_len: Length of the name of the subsystem being restarted 198c2ecf20Sopenharmony_ci * name: G-Link edge name of the subsystem being restarted 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistruct do_cleanup_msg { 228c2ecf20Sopenharmony_ci __le32 version; 238c2ecf20Sopenharmony_ci __le32 command; 248c2ecf20Sopenharmony_ci __le32 seq_num; 258c2ecf20Sopenharmony_ci __le32 name_len; 268c2ecf20Sopenharmony_ci char name[32]; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/** 308c2ecf20Sopenharmony_ci * struct cleanup_done_msg - The data structure for an SSR cleanup_done message 318c2ecf20Sopenharmony_ci * version: The G-Link SSR protocol version 328c2ecf20Sopenharmony_ci * response: The G-Link SSR response to a do_cleanup command, cleanup_done 338c2ecf20Sopenharmony_ci * seq_num: Sequence number 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistruct cleanup_done_msg { 368c2ecf20Sopenharmony_ci __le32 version; 378c2ecf20Sopenharmony_ci __le32 response; 388c2ecf20Sopenharmony_ci __le32 seq_num; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/** 428c2ecf20Sopenharmony_ci * G-Link SSR protocol commands 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define GLINK_SSR_DO_CLEANUP 0 458c2ecf20Sopenharmony_ci#define GLINK_SSR_CLEANUP_DONE 1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct glink_ssr { 488c2ecf20Sopenharmony_ci struct device *dev; 498c2ecf20Sopenharmony_ci struct rpmsg_endpoint *ept; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci struct notifier_block nb; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci u32 seq_num; 548c2ecf20Sopenharmony_ci struct completion completion; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* Notifier list for all registered glink_ssr instances */ 588c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(ssr_notifiers); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc 628c2ecf20Sopenharmony_ci * @ssr_name: name of the remoteproc that has been stopped 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_civoid qcom_glink_ssr_notify(const char *ssr_name) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_glink_ssr_notify); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, 718c2ecf20Sopenharmony_ci void *data, int len, void *priv, u32 addr) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct cleanup_done_msg *msg = data; 748c2ecf20Sopenharmony_ci struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (len < sizeof(*msg)) { 778c2ecf20Sopenharmony_ci dev_err(ssr->dev, "message too short\n"); 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (le32_to_cpu(msg->version) != 0) 828c2ecf20Sopenharmony_ci return -EINVAL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE) 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (le32_to_cpu(msg->seq_num) != ssr->seq_num) { 888c2ecf20Sopenharmony_ci dev_err(ssr->dev, "invalid sequence number of response\n"); 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci complete(&ssr->completion); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int qcom_glink_ssr_notifier_call(struct notifier_block *nb, 988c2ecf20Sopenharmony_ci unsigned long event, 998c2ecf20Sopenharmony_ci void *data) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb); 1028c2ecf20Sopenharmony_ci struct do_cleanup_msg msg; 1038c2ecf20Sopenharmony_ci char *ssr_name = data; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ssr->seq_num++; 1078c2ecf20Sopenharmony_ci reinit_completion(&ssr->completion); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 1108c2ecf20Sopenharmony_ci msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP); 1118c2ecf20Sopenharmony_ci msg.seq_num = cpu_to_le32(ssr->seq_num); 1128c2ecf20Sopenharmony_ci msg.name_len = cpu_to_le32(strlen(ssr_name)); 1138c2ecf20Sopenharmony_ci strlcpy(msg.name, ssr_name, sizeof(msg.name)); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret = rpmsg_send(ssr->ept, &msg, sizeof(msg)); 1168c2ecf20Sopenharmony_ci if (ret < 0) 1178c2ecf20Sopenharmony_ci dev_err(ssr->dev, "failed to send cleanup message\n"); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ssr->completion, HZ); 1208c2ecf20Sopenharmony_ci if (!ret) 1218c2ecf20Sopenharmony_ci dev_err(ssr->dev, "timeout waiting for cleanup done message\n"); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int qcom_glink_ssr_probe(struct rpmsg_device *rpdev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct glink_ssr *ssr; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL); 1318c2ecf20Sopenharmony_ci if (!ssr) 1328c2ecf20Sopenharmony_ci return -ENOMEM; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci init_completion(&ssr->completion); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ssr->dev = &rpdev->dev; 1378c2ecf20Sopenharmony_ci ssr->ept = rpdev->ept; 1388c2ecf20Sopenharmony_ci ssr->nb.notifier_call = qcom_glink_ssr_notifier_call; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci dev_set_drvdata(&rpdev->dev, ssr); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void qcom_glink_ssr_remove(struct rpmsg_device *rpdev) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct rpmsg_device_id qcom_glink_ssr_match[] = { 1538c2ecf20Sopenharmony_ci { "glink_ssr" }, 1548c2ecf20Sopenharmony_ci {} 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct rpmsg_driver qcom_glink_ssr_driver = { 1588c2ecf20Sopenharmony_ci .probe = qcom_glink_ssr_probe, 1598c2ecf20Sopenharmony_ci .remove = qcom_glink_ssr_remove, 1608c2ecf20Sopenharmony_ci .callback = qcom_glink_ssr_callback, 1618c2ecf20Sopenharmony_ci .id_table = qcom_glink_ssr_match, 1628c2ecf20Sopenharmony_ci .drv = { 1638c2ecf20Sopenharmony_ci .name = "qcom_glink_ssr", 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_cimodule_rpmsg_driver(qcom_glink_ssr_driver); 167