193bc3feeSBjorn Andersson // SPDX-License-Identifier: GPL-2.0-only 293bc3feeSBjorn Andersson /* 393bc3feeSBjorn Andersson * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. 493bc3feeSBjorn Andersson * Copyright (c) 2017, Linaro Ltd. 593bc3feeSBjorn Andersson */ 693bc3feeSBjorn Andersson 793bc3feeSBjorn Andersson #include <linux/completion.h> 893bc3feeSBjorn Andersson #include <linux/module.h> 993bc3feeSBjorn Andersson #include <linux/notifier.h> 1093bc3feeSBjorn Andersson #include <linux/rpmsg.h> 113e35772bSAlex Elder #include <linux/rpmsg/qcom_glink.h> 1293bc3feeSBjorn Andersson #include <linux/remoteproc/qcom_rproc.h> 1393bc3feeSBjorn Andersson 1493bc3feeSBjorn Andersson /** 1593bc3feeSBjorn Andersson * struct do_cleanup_msg - The data structure for an SSR do_cleanup message 16df2f392cSAlex Elder * @version: The G-Link SSR protocol version 17df2f392cSAlex Elder * @command: The G-Link SSR command - do_cleanup 18df2f392cSAlex Elder * @seq_num: Sequence number 19df2f392cSAlex Elder * @name_len: Length of the name of the subsystem being restarted 20df2f392cSAlex Elder * @name: G-Link edge name of the subsystem being restarted 2193bc3feeSBjorn Andersson */ 2293bc3feeSBjorn Andersson struct do_cleanup_msg { 2393bc3feeSBjorn Andersson __le32 version; 2493bc3feeSBjorn Andersson __le32 command; 2593bc3feeSBjorn Andersson __le32 seq_num; 2693bc3feeSBjorn Andersson __le32 name_len; 2793bc3feeSBjorn Andersson char name[32]; 2893bc3feeSBjorn Andersson }; 2993bc3feeSBjorn Andersson 3093bc3feeSBjorn Andersson /** 3193bc3feeSBjorn Andersson * struct cleanup_done_msg - The data structure for an SSR cleanup_done message 32df2f392cSAlex Elder * @version: The G-Link SSR protocol version 33df2f392cSAlex Elder * @response: The G-Link SSR response to a do_cleanup command, cleanup_done 34df2f392cSAlex Elder * @seq_num: Sequence number 3593bc3feeSBjorn Andersson */ 3693bc3feeSBjorn Andersson struct cleanup_done_msg { 3793bc3feeSBjorn Andersson __le32 version; 3893bc3feeSBjorn Andersson __le32 response; 3993bc3feeSBjorn Andersson __le32 seq_num; 4093bc3feeSBjorn Andersson }; 4193bc3feeSBjorn Andersson 42101042f4SKrzysztof Kozlowski /* 4393bc3feeSBjorn Andersson * G-Link SSR protocol commands 4493bc3feeSBjorn Andersson */ 4593bc3feeSBjorn Andersson #define GLINK_SSR_DO_CLEANUP 0 4693bc3feeSBjorn Andersson #define GLINK_SSR_CLEANUP_DONE 1 4793bc3feeSBjorn Andersson 4893bc3feeSBjorn Andersson struct glink_ssr { 4993bc3feeSBjorn Andersson struct device *dev; 5093bc3feeSBjorn Andersson struct rpmsg_endpoint *ept; 5193bc3feeSBjorn Andersson 5293bc3feeSBjorn Andersson struct notifier_block nb; 5393bc3feeSBjorn Andersson 5493bc3feeSBjorn Andersson u32 seq_num; 5593bc3feeSBjorn Andersson struct completion completion; 5693bc3feeSBjorn Andersson }; 5793bc3feeSBjorn Andersson 5893bc3feeSBjorn Andersson /* Notifier list for all registered glink_ssr instances */ 5993bc3feeSBjorn Andersson static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); 6093bc3feeSBjorn Andersson 6193bc3feeSBjorn Andersson /** 6293bc3feeSBjorn Andersson * qcom_glink_ssr_notify() - notify GLINK SSR about stopped remoteproc 6393bc3feeSBjorn Andersson * @ssr_name: name of the remoteproc that has been stopped 6493bc3feeSBjorn Andersson */ 6593bc3feeSBjorn Andersson void qcom_glink_ssr_notify(const char *ssr_name) 6693bc3feeSBjorn Andersson { 6793bc3feeSBjorn Andersson blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr_name); 6893bc3feeSBjorn Andersson } 6993bc3feeSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_ssr_notify); 7093bc3feeSBjorn Andersson 7193bc3feeSBjorn Andersson static int qcom_glink_ssr_callback(struct rpmsg_device *rpdev, 7293bc3feeSBjorn Andersson void *data, int len, void *priv, u32 addr) 7393bc3feeSBjorn Andersson { 7493bc3feeSBjorn Andersson struct cleanup_done_msg *msg = data; 7593bc3feeSBjorn Andersson struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); 7693bc3feeSBjorn Andersson 7793bc3feeSBjorn Andersson if (len < sizeof(*msg)) { 7893bc3feeSBjorn Andersson dev_err(ssr->dev, "message too short\n"); 7993bc3feeSBjorn Andersson return -EINVAL; 8093bc3feeSBjorn Andersson } 8193bc3feeSBjorn Andersson 8293bc3feeSBjorn Andersson if (le32_to_cpu(msg->version) != 0) 8393bc3feeSBjorn Andersson return -EINVAL; 8493bc3feeSBjorn Andersson 8593bc3feeSBjorn Andersson if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE) 8693bc3feeSBjorn Andersson return 0; 8793bc3feeSBjorn Andersson 8893bc3feeSBjorn Andersson if (le32_to_cpu(msg->seq_num) != ssr->seq_num) { 8993bc3feeSBjorn Andersson dev_err(ssr->dev, "invalid sequence number of response\n"); 9093bc3feeSBjorn Andersson return -EINVAL; 9193bc3feeSBjorn Andersson } 9293bc3feeSBjorn Andersson 9393bc3feeSBjorn Andersson complete(&ssr->completion); 9493bc3feeSBjorn Andersson 9593bc3feeSBjorn Andersson return 0; 9693bc3feeSBjorn Andersson } 9793bc3feeSBjorn Andersson 9893bc3feeSBjorn Andersson static int qcom_glink_ssr_notifier_call(struct notifier_block *nb, 9993bc3feeSBjorn Andersson unsigned long event, 10093bc3feeSBjorn Andersson void *data) 10193bc3feeSBjorn Andersson { 10293bc3feeSBjorn Andersson struct glink_ssr *ssr = container_of(nb, struct glink_ssr, nb); 10393bc3feeSBjorn Andersson struct do_cleanup_msg msg; 10493bc3feeSBjorn Andersson char *ssr_name = data; 10593bc3feeSBjorn Andersson int ret; 10693bc3feeSBjorn Andersson 10793bc3feeSBjorn Andersson ssr->seq_num++; 10893bc3feeSBjorn Andersson reinit_completion(&ssr->completion); 10993bc3feeSBjorn Andersson 11093bc3feeSBjorn Andersson memset(&msg, 0, sizeof(msg)); 11193bc3feeSBjorn Andersson msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP); 11293bc3feeSBjorn Andersson msg.seq_num = cpu_to_le32(ssr->seq_num); 11393bc3feeSBjorn Andersson msg.name_len = cpu_to_le32(strlen(ssr_name)); 114d2ff0f84SWolfram Sang strscpy(msg.name, ssr_name, sizeof(msg.name)); 11593bc3feeSBjorn Andersson 11693bc3feeSBjorn Andersson ret = rpmsg_send(ssr->ept, &msg, sizeof(msg)); 11793bc3feeSBjorn Andersson if (ret < 0) 11893bc3feeSBjorn Andersson dev_err(ssr->dev, "failed to send cleanup message\n"); 11993bc3feeSBjorn Andersson 12093bc3feeSBjorn Andersson ret = wait_for_completion_timeout(&ssr->completion, HZ); 12193bc3feeSBjorn Andersson if (!ret) 12293bc3feeSBjorn Andersson dev_err(ssr->dev, "timeout waiting for cleanup done message\n"); 12393bc3feeSBjorn Andersson 12493bc3feeSBjorn Andersson return NOTIFY_DONE; 12593bc3feeSBjorn Andersson } 12693bc3feeSBjorn Andersson 12793bc3feeSBjorn Andersson static int qcom_glink_ssr_probe(struct rpmsg_device *rpdev) 12893bc3feeSBjorn Andersson { 12993bc3feeSBjorn Andersson struct glink_ssr *ssr; 13093bc3feeSBjorn Andersson 13193bc3feeSBjorn Andersson ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL); 13293bc3feeSBjorn Andersson if (!ssr) 13393bc3feeSBjorn Andersson return -ENOMEM; 13493bc3feeSBjorn Andersson 13593bc3feeSBjorn Andersson init_completion(&ssr->completion); 13693bc3feeSBjorn Andersson 13793bc3feeSBjorn Andersson ssr->dev = &rpdev->dev; 13893bc3feeSBjorn Andersson ssr->ept = rpdev->ept; 13993bc3feeSBjorn Andersson ssr->nb.notifier_call = qcom_glink_ssr_notifier_call; 14093bc3feeSBjorn Andersson 14193bc3feeSBjorn Andersson dev_set_drvdata(&rpdev->dev, ssr); 14293bc3feeSBjorn Andersson 14393bc3feeSBjorn Andersson return blocking_notifier_chain_register(&ssr_notifiers, &ssr->nb); 14493bc3feeSBjorn Andersson } 14593bc3feeSBjorn Andersson 14693bc3feeSBjorn Andersson static void qcom_glink_ssr_remove(struct rpmsg_device *rpdev) 14793bc3feeSBjorn Andersson { 14893bc3feeSBjorn Andersson struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); 14993bc3feeSBjorn Andersson 15093bc3feeSBjorn Andersson blocking_notifier_chain_unregister(&ssr_notifiers, &ssr->nb); 15193bc3feeSBjorn Andersson } 15293bc3feeSBjorn Andersson 15393bc3feeSBjorn Andersson static const struct rpmsg_device_id qcom_glink_ssr_match[] = { 15493bc3feeSBjorn Andersson { "glink_ssr" }, 15593bc3feeSBjorn Andersson {} 15693bc3feeSBjorn Andersson }; 157*bcbab579SKrzysztof Kozlowski MODULE_DEVICE_TABLE(rpmsg, qcom_glink_ssr_match); 15893bc3feeSBjorn Andersson 15993bc3feeSBjorn Andersson static struct rpmsg_driver qcom_glink_ssr_driver = { 16093bc3feeSBjorn Andersson .probe = qcom_glink_ssr_probe, 16193bc3feeSBjorn Andersson .remove = qcom_glink_ssr_remove, 16293bc3feeSBjorn Andersson .callback = qcom_glink_ssr_callback, 16393bc3feeSBjorn Andersson .id_table = qcom_glink_ssr_match, 16493bc3feeSBjorn Andersson .drv = { 16593bc3feeSBjorn Andersson .name = "qcom_glink_ssr", 16693bc3feeSBjorn Andersson }, 16793bc3feeSBjorn Andersson }; 16893bc3feeSBjorn Andersson module_rpmsg_driver(qcom_glink_ssr_driver); 169