162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017, Linaro Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/firmware.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/notifier.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/of_irq.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/remoteproc/qcom_rproc.h> 1462306a36Sopenharmony_ci#include <linux/rpmsg.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "qcom_common.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(sysmon_notifiers); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct qcom_sysmon { 2162306a36Sopenharmony_ci struct rproc_subdev subdev; 2262306a36Sopenharmony_ci struct rproc *rproc; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci int state; 2562306a36Sopenharmony_ci struct mutex state_lock; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci struct list_head node; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci const char *name; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci int shutdown_irq; 3262306a36Sopenharmony_ci int ssctl_version; 3362306a36Sopenharmony_ci int ssctl_instance; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci struct notifier_block nb; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci struct device *dev; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci struct rpmsg_endpoint *ept; 4062306a36Sopenharmony_ci struct completion comp; 4162306a36Sopenharmony_ci struct completion ind_comp; 4262306a36Sopenharmony_ci struct completion shutdown_comp; 4362306a36Sopenharmony_ci struct completion ssctl_comp; 4462306a36Sopenharmony_ci struct mutex lock; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci bool ssr_ack; 4762306a36Sopenharmony_ci bool shutdown_acked; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci struct qmi_handle qmi; 5062306a36Sopenharmony_ci struct sockaddr_qrtr ssctl; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cienum { 5462306a36Sopenharmony_ci SSCTL_SSR_EVENT_BEFORE_POWERUP, 5562306a36Sopenharmony_ci SSCTL_SSR_EVENT_AFTER_POWERUP, 5662306a36Sopenharmony_ci SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, 5762306a36Sopenharmony_ci SSCTL_SSR_EVENT_AFTER_SHUTDOWN, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const char * const sysmon_state_string[] = { 6162306a36Sopenharmony_ci [SSCTL_SSR_EVENT_BEFORE_POWERUP] = "before_powerup", 6262306a36Sopenharmony_ci [SSCTL_SSR_EVENT_AFTER_POWERUP] = "after_powerup", 6362306a36Sopenharmony_ci [SSCTL_SSR_EVENT_BEFORE_SHUTDOWN] = "before_shutdown", 6462306a36Sopenharmony_ci [SSCTL_SSR_EVENT_AFTER_SHUTDOWN] = "after_shutdown", 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct sysmon_event { 6862306a36Sopenharmony_ci const char *subsys_name; 6962306a36Sopenharmony_ci u32 ssr_event; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic DEFINE_MUTEX(sysmon_lock); 7362306a36Sopenharmony_cistatic LIST_HEAD(sysmon_list); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * sysmon_send_event() - send notification of other remote's SSR event 7762306a36Sopenharmony_ci * @sysmon: sysmon context 7862306a36Sopenharmony_ci * @event: sysmon event context 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void sysmon_send_event(struct qcom_sysmon *sysmon, 8162306a36Sopenharmony_ci const struct sysmon_event *event) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci char req[50]; 8462306a36Sopenharmony_ci int len; 8562306a36Sopenharmony_ci int ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name, 8862306a36Sopenharmony_ci sysmon_state_string[event->ssr_event]); 8962306a36Sopenharmony_ci if (len >= sizeof(req)) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci mutex_lock(&sysmon->lock); 9362306a36Sopenharmony_ci reinit_completion(&sysmon->comp); 9462306a36Sopenharmony_ci sysmon->ssr_ack = false; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = rpmsg_send(sysmon->ept, req, len); 9762306a36Sopenharmony_ci if (ret < 0) { 9862306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to send sysmon event\n"); 9962306a36Sopenharmony_ci goto out_unlock; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ret = wait_for_completion_timeout(&sysmon->comp, 10362306a36Sopenharmony_ci msecs_to_jiffies(5000)); 10462306a36Sopenharmony_ci if (!ret) { 10562306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 10662306a36Sopenharmony_ci goto out_unlock; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!sysmon->ssr_ack) 11062306a36Sopenharmony_ci dev_err(sysmon->dev, "unexpected response to sysmon event\n"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciout_unlock: 11362306a36Sopenharmony_ci mutex_unlock(&sysmon->lock); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/** 11762306a36Sopenharmony_ci * sysmon_request_shutdown() - request graceful shutdown of remote 11862306a36Sopenharmony_ci * @sysmon: sysmon context 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Return: boolean indicator of the remote processor acking the request 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic bool sysmon_request_shutdown(struct qcom_sysmon *sysmon) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci char *req = "ssr:shutdown"; 12562306a36Sopenharmony_ci bool acked = false; 12662306a36Sopenharmony_ci int ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci mutex_lock(&sysmon->lock); 12962306a36Sopenharmony_ci reinit_completion(&sysmon->comp); 13062306a36Sopenharmony_ci sysmon->ssr_ack = false; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1); 13362306a36Sopenharmony_ci if (ret < 0) { 13462306a36Sopenharmony_ci dev_err(sysmon->dev, "send sysmon shutdown request failed\n"); 13562306a36Sopenharmony_ci goto out_unlock; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = wait_for_completion_timeout(&sysmon->comp, 13962306a36Sopenharmony_ci msecs_to_jiffies(5000)); 14062306a36Sopenharmony_ci if (!ret) { 14162306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for sysmon ack\n"); 14262306a36Sopenharmony_ci goto out_unlock; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!sysmon->ssr_ack) 14662306a36Sopenharmony_ci dev_err(sysmon->dev, 14762306a36Sopenharmony_ci "unexpected response to sysmon shutdown request\n"); 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci acked = true; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciout_unlock: 15262306a36Sopenharmony_ci mutex_unlock(&sysmon->lock); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return acked; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, 15862306a36Sopenharmony_ci void *priv, u32 addr) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct qcom_sysmon *sysmon = priv; 16162306a36Sopenharmony_ci const char *ssr_ack = "ssr:ack"; 16262306a36Sopenharmony_ci const int ssr_ack_len = strlen(ssr_ack) + 1; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!sysmon) 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len)) 16862306a36Sopenharmony_ci sysmon->ssr_ack = true; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci complete(&sysmon->comp); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define SSCTL_SHUTDOWN_REQ 0x21 17662306a36Sopenharmony_ci#define SSCTL_SHUTDOWN_READY_IND 0x21 17762306a36Sopenharmony_ci#define SSCTL_SUBSYS_EVENT_REQ 0x23 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define SSCTL_MAX_MSG_LEN 7 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci#define SSCTL_SUBSYS_NAME_LENGTH 15 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cienum { 18462306a36Sopenharmony_ci SSCTL_SSR_EVENT_FORCED, 18562306a36Sopenharmony_ci SSCTL_SSR_EVENT_GRACEFUL, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct ssctl_shutdown_resp { 18962306a36Sopenharmony_ci struct qmi_response_type_v01 resp; 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_shutdown_resp_ei[] = { 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci .data_type = QMI_STRUCT, 19562306a36Sopenharmony_ci .elem_len = 1, 19662306a36Sopenharmony_ci .elem_size = sizeof(struct qmi_response_type_v01), 19762306a36Sopenharmony_ci .array_type = NO_ARRAY, 19862306a36Sopenharmony_ci .tlv_type = 0x02, 19962306a36Sopenharmony_ci .offset = offsetof(struct ssctl_shutdown_resp, resp), 20062306a36Sopenharmony_ci .ei_array = qmi_response_type_v01_ei, 20162306a36Sopenharmony_ci }, 20262306a36Sopenharmony_ci {} 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistruct ssctl_subsys_event_req { 20662306a36Sopenharmony_ci u8 subsys_name_len; 20762306a36Sopenharmony_ci char subsys_name[SSCTL_SUBSYS_NAME_LENGTH]; 20862306a36Sopenharmony_ci u32 event; 20962306a36Sopenharmony_ci u8 evt_driven_valid; 21062306a36Sopenharmony_ci u32 evt_driven; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_subsys_event_req_ei[] = { 21462306a36Sopenharmony_ci { 21562306a36Sopenharmony_ci .data_type = QMI_DATA_LEN, 21662306a36Sopenharmony_ci .elem_len = 1, 21762306a36Sopenharmony_ci .elem_size = sizeof(uint8_t), 21862306a36Sopenharmony_ci .array_type = NO_ARRAY, 21962306a36Sopenharmony_ci .tlv_type = 0x01, 22062306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_req, 22162306a36Sopenharmony_ci subsys_name_len), 22262306a36Sopenharmony_ci .ei_array = NULL, 22362306a36Sopenharmony_ci }, 22462306a36Sopenharmony_ci { 22562306a36Sopenharmony_ci .data_type = QMI_UNSIGNED_1_BYTE, 22662306a36Sopenharmony_ci .elem_len = SSCTL_SUBSYS_NAME_LENGTH, 22762306a36Sopenharmony_ci .elem_size = sizeof(char), 22862306a36Sopenharmony_ci .array_type = VAR_LEN_ARRAY, 22962306a36Sopenharmony_ci .tlv_type = 0x01, 23062306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_req, 23162306a36Sopenharmony_ci subsys_name), 23262306a36Sopenharmony_ci .ei_array = NULL, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci .data_type = QMI_SIGNED_4_BYTE_ENUM, 23662306a36Sopenharmony_ci .elem_len = 1, 23762306a36Sopenharmony_ci .elem_size = sizeof(uint32_t), 23862306a36Sopenharmony_ci .array_type = NO_ARRAY, 23962306a36Sopenharmony_ci .tlv_type = 0x02, 24062306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_req, 24162306a36Sopenharmony_ci event), 24262306a36Sopenharmony_ci .ei_array = NULL, 24362306a36Sopenharmony_ci }, 24462306a36Sopenharmony_ci { 24562306a36Sopenharmony_ci .data_type = QMI_OPT_FLAG, 24662306a36Sopenharmony_ci .elem_len = 1, 24762306a36Sopenharmony_ci .elem_size = sizeof(uint8_t), 24862306a36Sopenharmony_ci .array_type = NO_ARRAY, 24962306a36Sopenharmony_ci .tlv_type = 0x10, 25062306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_req, 25162306a36Sopenharmony_ci evt_driven_valid), 25262306a36Sopenharmony_ci .ei_array = NULL, 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci { 25562306a36Sopenharmony_ci .data_type = QMI_SIGNED_4_BYTE_ENUM, 25662306a36Sopenharmony_ci .elem_len = 1, 25762306a36Sopenharmony_ci .elem_size = sizeof(uint32_t), 25862306a36Sopenharmony_ci .array_type = NO_ARRAY, 25962306a36Sopenharmony_ci .tlv_type = 0x10, 26062306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_req, 26162306a36Sopenharmony_ci evt_driven), 26262306a36Sopenharmony_ci .ei_array = NULL, 26362306a36Sopenharmony_ci }, 26462306a36Sopenharmony_ci {} 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct ssctl_subsys_event_resp { 26862306a36Sopenharmony_ci struct qmi_response_type_v01 resp; 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { 27262306a36Sopenharmony_ci { 27362306a36Sopenharmony_ci .data_type = QMI_STRUCT, 27462306a36Sopenharmony_ci .elem_len = 1, 27562306a36Sopenharmony_ci .elem_size = sizeof(struct qmi_response_type_v01), 27662306a36Sopenharmony_ci .array_type = NO_ARRAY, 27762306a36Sopenharmony_ci .tlv_type = 0x02, 27862306a36Sopenharmony_ci .offset = offsetof(struct ssctl_subsys_event_resp, 27962306a36Sopenharmony_ci resp), 28062306a36Sopenharmony_ci .ei_array = qmi_response_type_v01_ei, 28162306a36Sopenharmony_ci }, 28262306a36Sopenharmony_ci {} 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct qmi_elem_info ssctl_shutdown_ind_ei[] = { 28662306a36Sopenharmony_ci {} 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, 29062306a36Sopenharmony_ci struct qmi_txn *txn, const void *data) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci complete(&sysmon->ind_comp); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct qmi_msg_handler qmi_indication_handler[] = { 29862306a36Sopenharmony_ci { 29962306a36Sopenharmony_ci .type = QMI_INDICATION, 30062306a36Sopenharmony_ci .msg_id = SSCTL_SHUTDOWN_READY_IND, 30162306a36Sopenharmony_ci .ei = ssctl_shutdown_ind_ei, 30262306a36Sopenharmony_ci .decoded_size = 0, 30362306a36Sopenharmony_ci .fn = sysmon_ind_cb 30462306a36Sopenharmony_ci }, 30562306a36Sopenharmony_ci {} 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic bool ssctl_request_shutdown_wait(struct qcom_sysmon *sysmon) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int ret; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = wait_for_completion_timeout(&sysmon->shutdown_comp, 10 * HZ); 31362306a36Sopenharmony_ci if (ret) 31462306a36Sopenharmony_ci return true; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = try_wait_for_completion(&sysmon->ind_comp); 31762306a36Sopenharmony_ci if (ret) 31862306a36Sopenharmony_ci return true; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for shutdown ack\n"); 32162306a36Sopenharmony_ci return false; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * ssctl_request_shutdown() - request shutdown via SSCTL QMI service 32662306a36Sopenharmony_ci * @sysmon: sysmon context 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * Return: boolean indicator of the remote processor acking the request 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic bool ssctl_request_shutdown(struct qcom_sysmon *sysmon) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct ssctl_shutdown_resp resp; 33362306a36Sopenharmony_ci struct qmi_txn txn; 33462306a36Sopenharmony_ci bool acked = false; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci reinit_completion(&sysmon->ind_comp); 33862306a36Sopenharmony_ci reinit_completion(&sysmon->shutdown_comp); 33962306a36Sopenharmony_ci ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); 34062306a36Sopenharmony_ci if (ret < 0) { 34162306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 34262306a36Sopenharmony_ci return false; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 34662306a36Sopenharmony_ci SSCTL_SHUTDOWN_REQ, 0, NULL, NULL); 34762306a36Sopenharmony_ci if (ret < 0) { 34862306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to send shutdown request\n"); 34962306a36Sopenharmony_ci qmi_txn_cancel(&txn); 35062306a36Sopenharmony_ci return false; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, 5 * HZ); 35462306a36Sopenharmony_ci if (ret < 0) { 35562306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for shutdown response\n"); 35662306a36Sopenharmony_ci } else if (resp.resp.result) { 35762306a36Sopenharmony_ci dev_err(sysmon->dev, "shutdown request rejected\n"); 35862306a36Sopenharmony_ci } else { 35962306a36Sopenharmony_ci dev_dbg(sysmon->dev, "shutdown request completed\n"); 36062306a36Sopenharmony_ci acked = true; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (sysmon->shutdown_irq > 0) 36462306a36Sopenharmony_ci return ssctl_request_shutdown_wait(sysmon); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return acked; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/** 37062306a36Sopenharmony_ci * ssctl_send_event() - send notification of other remote's SSR event 37162306a36Sopenharmony_ci * @sysmon: sysmon context 37262306a36Sopenharmony_ci * @event: sysmon event context 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void ssctl_send_event(struct qcom_sysmon *sysmon, 37562306a36Sopenharmony_ci const struct sysmon_event *event) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct ssctl_subsys_event_resp resp; 37862306a36Sopenharmony_ci struct ssctl_subsys_event_req req; 37962306a36Sopenharmony_ci struct qmi_txn txn; 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci memset(&resp, 0, sizeof(resp)); 38362306a36Sopenharmony_ci ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp); 38462306a36Sopenharmony_ci if (ret < 0) { 38562306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to allocate QMI txn\n"); 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 39062306a36Sopenharmony_ci strscpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name)); 39162306a36Sopenharmony_ci req.subsys_name_len = strlen(req.subsys_name); 39262306a36Sopenharmony_ci req.event = event->ssr_event; 39362306a36Sopenharmony_ci req.evt_driven_valid = true; 39462306a36Sopenharmony_ci req.evt_driven = SSCTL_SSR_EVENT_FORCED; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, 39762306a36Sopenharmony_ci SSCTL_SUBSYS_EVENT_REQ, 40, 39862306a36Sopenharmony_ci ssctl_subsys_event_req_ei, &req); 39962306a36Sopenharmony_ci if (ret < 0) { 40062306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to send subsystem event\n"); 40162306a36Sopenharmony_ci qmi_txn_cancel(&txn); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = qmi_txn_wait(&txn, 5 * HZ); 40662306a36Sopenharmony_ci if (ret < 0) 40762306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for subsystem event response\n"); 40862306a36Sopenharmony_ci else if (resp.resp.result) 40962306a36Sopenharmony_ci dev_err(sysmon->dev, "subsystem event rejected\n"); 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci dev_dbg(sysmon->dev, "subsystem event accepted\n"); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * ssctl_new_server() - QMI callback indicating a new service 41662306a36Sopenharmony_ci * @qmi: QMI handle 41762306a36Sopenharmony_ci * @svc: service information 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Return: 0 if we're interested in this service, -EINVAL otherwise. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci switch (svc->version) { 42662306a36Sopenharmony_ci case 1: 42762306a36Sopenharmony_ci if (svc->instance != 0) 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci if (strcmp(sysmon->name, "modem")) 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case 2: 43362306a36Sopenharmony_ci if (svc->instance != sysmon->ssctl_instance) 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci default: 43762306a36Sopenharmony_ci return -EINVAL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci sysmon->ssctl_version = svc->version; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci sysmon->ssctl.sq_family = AF_QIPCRTR; 44362306a36Sopenharmony_ci sysmon->ssctl.sq_node = svc->node; 44462306a36Sopenharmony_ci sysmon->ssctl.sq_port = svc->port; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci svc->priv = sysmon; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci complete(&sysmon->ssctl_comp); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/** 45462306a36Sopenharmony_ci * ssctl_del_server() - QMI callback indicating that @svc is removed 45562306a36Sopenharmony_ci * @qmi: QMI handle 45662306a36Sopenharmony_ci * @svc: service information 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct qcom_sysmon *sysmon = svc->priv; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci sysmon->ssctl_version = 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic const struct qmi_ops ssctl_ops = { 46662306a36Sopenharmony_ci .new_server = ssctl_new_server, 46762306a36Sopenharmony_ci .del_server = ssctl_del_server, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int sysmon_prepare(struct rproc_subdev *subdev) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 47362306a36Sopenharmony_ci subdev); 47462306a36Sopenharmony_ci struct sysmon_event event = { 47562306a36Sopenharmony_ci .subsys_name = sysmon->name, 47662306a36Sopenharmony_ci .ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP 47762306a36Sopenharmony_ci }; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci mutex_lock(&sysmon->state_lock); 48062306a36Sopenharmony_ci sysmon->state = SSCTL_SSR_EVENT_BEFORE_POWERUP; 48162306a36Sopenharmony_ci blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 48262306a36Sopenharmony_ci mutex_unlock(&sysmon->state_lock); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci/** 48862306a36Sopenharmony_ci * sysmon_start() - start callback for the sysmon remoteproc subdevice 48962306a36Sopenharmony_ci * @subdev: instance of the sysmon subdevice 49062306a36Sopenharmony_ci * 49162306a36Sopenharmony_ci * Inform all the listners of sysmon notifications that the rproc associated 49262306a36Sopenharmony_ci * to @subdev has booted up. The rproc that booted up also needs to know 49362306a36Sopenharmony_ci * which rprocs are already up and running, so send start notifications 49462306a36Sopenharmony_ci * on behalf of all the online rprocs. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_cistatic int sysmon_start(struct rproc_subdev *subdev) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 49962306a36Sopenharmony_ci subdev); 50062306a36Sopenharmony_ci struct qcom_sysmon *target; 50162306a36Sopenharmony_ci struct sysmon_event event = { 50262306a36Sopenharmony_ci .subsys_name = sysmon->name, 50362306a36Sopenharmony_ci .ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP 50462306a36Sopenharmony_ci }; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci reinit_completion(&sysmon->ssctl_comp); 50762306a36Sopenharmony_ci mutex_lock(&sysmon->state_lock); 50862306a36Sopenharmony_ci sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP; 50962306a36Sopenharmony_ci blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 51062306a36Sopenharmony_ci mutex_unlock(&sysmon->state_lock); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci mutex_lock(&sysmon_lock); 51362306a36Sopenharmony_ci list_for_each_entry(target, &sysmon_list, node) { 51462306a36Sopenharmony_ci mutex_lock(&target->state_lock); 51562306a36Sopenharmony_ci if (target == sysmon || target->state != SSCTL_SSR_EVENT_AFTER_POWERUP) { 51662306a36Sopenharmony_ci mutex_unlock(&target->state_lock); 51762306a36Sopenharmony_ci continue; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci event.subsys_name = target->name; 52162306a36Sopenharmony_ci event.ssr_event = target->state; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (sysmon->ssctl_version == 2) 52462306a36Sopenharmony_ci ssctl_send_event(sysmon, &event); 52562306a36Sopenharmony_ci else if (sysmon->ept) 52662306a36Sopenharmony_ci sysmon_send_event(sysmon, &event); 52762306a36Sopenharmony_ci mutex_unlock(&target->state_lock); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci mutex_unlock(&sysmon_lock); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic void sysmon_stop(struct rproc_subdev *subdev, bool crashed) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); 53762306a36Sopenharmony_ci struct sysmon_event event = { 53862306a36Sopenharmony_ci .subsys_name = sysmon->name, 53962306a36Sopenharmony_ci .ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN 54062306a36Sopenharmony_ci }; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci sysmon->shutdown_acked = false; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci mutex_lock(&sysmon->state_lock); 54562306a36Sopenharmony_ci sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; 54662306a36Sopenharmony_ci blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 54762306a36Sopenharmony_ci mutex_unlock(&sysmon->state_lock); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Don't request graceful shutdown if we've crashed */ 55062306a36Sopenharmony_ci if (crashed) 55162306a36Sopenharmony_ci return; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (sysmon->ssctl_instance) { 55462306a36Sopenharmony_ci if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2)) 55562306a36Sopenharmony_ci dev_err(sysmon->dev, "timeout waiting for ssctl service\n"); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (sysmon->ssctl_version) 55962306a36Sopenharmony_ci sysmon->shutdown_acked = ssctl_request_shutdown(sysmon); 56062306a36Sopenharmony_ci else if (sysmon->ept) 56162306a36Sopenharmony_ci sysmon->shutdown_acked = sysmon_request_shutdown(sysmon); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic void sysmon_unprepare(struct rproc_subdev *subdev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, 56762306a36Sopenharmony_ci subdev); 56862306a36Sopenharmony_ci struct sysmon_event event = { 56962306a36Sopenharmony_ci .subsys_name = sysmon->name, 57062306a36Sopenharmony_ci .ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN 57162306a36Sopenharmony_ci }; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci mutex_lock(&sysmon->state_lock); 57462306a36Sopenharmony_ci sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN; 57562306a36Sopenharmony_ci blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); 57662306a36Sopenharmony_ci mutex_unlock(&sysmon->state_lock); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/** 58062306a36Sopenharmony_ci * sysmon_notify() - notify sysmon target of another's SSR 58162306a36Sopenharmony_ci * @nb: notifier_block associated with sysmon instance 58262306a36Sopenharmony_ci * @event: unused 58362306a36Sopenharmony_ci * @data: SSR identifier of the remote that is going down 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_cistatic int sysmon_notify(struct notifier_block *nb, unsigned long event, 58662306a36Sopenharmony_ci void *data) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); 58962306a36Sopenharmony_ci struct sysmon_event *sysmon_event = data; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Skip non-running rprocs and the originating instance */ 59262306a36Sopenharmony_ci if (sysmon->state != SSCTL_SSR_EVENT_AFTER_POWERUP || 59362306a36Sopenharmony_ci !strcmp(sysmon_event->subsys_name, sysmon->name)) { 59462306a36Sopenharmony_ci dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); 59562306a36Sopenharmony_ci return NOTIFY_DONE; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Only SSCTL version 2 supports SSR events */ 59962306a36Sopenharmony_ci if (sysmon->ssctl_version == 2) 60062306a36Sopenharmony_ci ssctl_send_event(sysmon, sysmon_event); 60162306a36Sopenharmony_ci else if (sysmon->ept) 60262306a36Sopenharmony_ci sysmon_send_event(sysmon, sysmon_event); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return NOTIFY_DONE; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct qcom_sysmon *sysmon = data; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci complete(&sysmon->shutdown_comp); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return IRQ_HANDLED; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/** 61762306a36Sopenharmony_ci * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc 61862306a36Sopenharmony_ci * @rproc: rproc context to associate the subdev with 61962306a36Sopenharmony_ci * @name: name of this subdev, to use in SSR 62062306a36Sopenharmony_ci * @ssctl_instance: instance id of the ssctl QMI service 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * Return: A new qcom_sysmon object, or NULL on failure 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cistruct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, 62562306a36Sopenharmony_ci const char *name, 62662306a36Sopenharmony_ci int ssctl_instance) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct qcom_sysmon *sysmon; 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); 63262306a36Sopenharmony_ci if (!sysmon) 63362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci sysmon->dev = rproc->dev.parent; 63662306a36Sopenharmony_ci sysmon->rproc = rproc; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci sysmon->name = name; 63962306a36Sopenharmony_ci sysmon->ssctl_instance = ssctl_instance; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci init_completion(&sysmon->comp); 64262306a36Sopenharmony_ci init_completion(&sysmon->ind_comp); 64362306a36Sopenharmony_ci init_completion(&sysmon->shutdown_comp); 64462306a36Sopenharmony_ci init_completion(&sysmon->ssctl_comp); 64562306a36Sopenharmony_ci mutex_init(&sysmon->lock); 64662306a36Sopenharmony_ci mutex_init(&sysmon->state_lock); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, 64962306a36Sopenharmony_ci "shutdown-ack"); 65062306a36Sopenharmony_ci if (sysmon->shutdown_irq < 0) { 65162306a36Sopenharmony_ci if (sysmon->shutdown_irq != -ENODATA) { 65262306a36Sopenharmony_ci dev_err(sysmon->dev, 65362306a36Sopenharmony_ci "failed to retrieve shutdown-ack IRQ\n"); 65462306a36Sopenharmony_ci ret = sysmon->shutdown_irq; 65562306a36Sopenharmony_ci kfree(sysmon); 65662306a36Sopenharmony_ci return ERR_PTR(ret); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } else { 65962306a36Sopenharmony_ci ret = devm_request_threaded_irq(sysmon->dev, 66062306a36Sopenharmony_ci sysmon->shutdown_irq, 66162306a36Sopenharmony_ci NULL, sysmon_shutdown_interrupt, 66262306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 66362306a36Sopenharmony_ci "q6v5 shutdown-ack", sysmon); 66462306a36Sopenharmony_ci if (ret) { 66562306a36Sopenharmony_ci dev_err(sysmon->dev, 66662306a36Sopenharmony_ci "failed to acquire shutdown-ack IRQ\n"); 66762306a36Sopenharmony_ci kfree(sysmon); 66862306a36Sopenharmony_ci return ERR_PTR(ret); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, 67362306a36Sopenharmony_ci qmi_indication_handler); 67462306a36Sopenharmony_ci if (ret < 0) { 67562306a36Sopenharmony_ci dev_err(sysmon->dev, "failed to initialize qmi handle\n"); 67662306a36Sopenharmony_ci kfree(sysmon); 67762306a36Sopenharmony_ci return ERR_PTR(ret); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci qmi_add_lookup(&sysmon->qmi, 43, 0, 0); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci sysmon->subdev.prepare = sysmon_prepare; 68362306a36Sopenharmony_ci sysmon->subdev.start = sysmon_start; 68462306a36Sopenharmony_ci sysmon->subdev.stop = sysmon_stop; 68562306a36Sopenharmony_ci sysmon->subdev.unprepare = sysmon_unprepare; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci rproc_add_subdev(rproc, &sysmon->subdev); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci sysmon->nb.notifier_call = sysmon_notify; 69062306a36Sopenharmony_ci blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci mutex_lock(&sysmon_lock); 69362306a36Sopenharmony_ci list_add(&sysmon->node, &sysmon_list); 69462306a36Sopenharmony_ci mutex_unlock(&sysmon_lock); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return sysmon; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/** 70162306a36Sopenharmony_ci * qcom_remove_sysmon_subdev() - release a qcom_sysmon 70262306a36Sopenharmony_ci * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev() 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_civoid qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci if (!sysmon) 70762306a36Sopenharmony_ci return; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci mutex_lock(&sysmon_lock); 71062306a36Sopenharmony_ci list_del(&sysmon->node); 71162306a36Sopenharmony_ci mutex_unlock(&sysmon_lock); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci rproc_remove_subdev(sysmon->rproc, &sysmon->subdev); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci qmi_handle_release(&sysmon->qmi); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci kfree(sysmon); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/** 72462306a36Sopenharmony_ci * qcom_sysmon_shutdown_acked() - query the success of the last shutdown 72562306a36Sopenharmony_ci * @sysmon: sysmon context 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * When sysmon is used to request a graceful shutdown of the remote processor 72862306a36Sopenharmony_ci * this can be used by the remoteproc driver to query the success, in order to 72962306a36Sopenharmony_ci * know if it should fall back to other means of requesting a shutdown. 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * Return: boolean indicator of the success of the last shutdown request 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_cibool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci return sysmon && sysmon->shutdown_acked; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/** 74062306a36Sopenharmony_ci * sysmon_probe() - probe sys_mon channel 74162306a36Sopenharmony_ci * @rpdev: rpmsg device handle 74262306a36Sopenharmony_ci * 74362306a36Sopenharmony_ci * Find the sysmon context associated with the ancestor remoteproc and assign 74462306a36Sopenharmony_ci * this rpmsg device with said sysmon context. 74562306a36Sopenharmony_ci * 74662306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_cistatic int sysmon_probe(struct rpmsg_device *rpdev) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct qcom_sysmon *sysmon; 75162306a36Sopenharmony_ci struct rproc *rproc; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci rproc = rproc_get_by_child(&rpdev->dev); 75462306a36Sopenharmony_ci if (!rproc) { 75562306a36Sopenharmony_ci dev_err(&rpdev->dev, "sysmon device not child of rproc\n"); 75662306a36Sopenharmony_ci return -EINVAL; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci mutex_lock(&sysmon_lock); 76062306a36Sopenharmony_ci list_for_each_entry(sysmon, &sysmon_list, node) { 76162306a36Sopenharmony_ci if (sysmon->rproc == rproc) 76262306a36Sopenharmony_ci goto found; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci mutex_unlock(&sysmon_lock); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n"); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return -EINVAL; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cifound: 77162306a36Sopenharmony_ci mutex_unlock(&sysmon_lock); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci rpdev->ept->priv = sysmon; 77462306a36Sopenharmony_ci sysmon->ept = rpdev->ept; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/** 78062306a36Sopenharmony_ci * sysmon_remove() - sys_mon channel remove handler 78162306a36Sopenharmony_ci * @rpdev: rpmsg device handle 78262306a36Sopenharmony_ci * 78362306a36Sopenharmony_ci * Disassociate the rpmsg device with the sysmon instance. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_cistatic void sysmon_remove(struct rpmsg_device *rpdev) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct qcom_sysmon *sysmon = rpdev->ept->priv; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci sysmon->ept = NULL; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic const struct rpmsg_device_id sysmon_match[] = { 79362306a36Sopenharmony_ci { "sys_mon" }, 79462306a36Sopenharmony_ci {} 79562306a36Sopenharmony_ci}; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic struct rpmsg_driver sysmon_driver = { 79862306a36Sopenharmony_ci .probe = sysmon_probe, 79962306a36Sopenharmony_ci .remove = sysmon_remove, 80062306a36Sopenharmony_ci .callback = sysmon_callback, 80162306a36Sopenharmony_ci .id_table = sysmon_match, 80262306a36Sopenharmony_ci .drv = { 80362306a36Sopenharmony_ci .name = "qcom_sysmon", 80462306a36Sopenharmony_ci }, 80562306a36Sopenharmony_ci}; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cimodule_rpmsg_driver(sysmon_driver); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm sysmon driver"); 81062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 811