Browse Source

cnss2: Add pcss recovery support

Extend current recovery sysfs interface, add pcss recovery support.
Change previous sysfs recovery from bool to bitamp. Bit 0 is used
for original wlan recovery, bit 1 is newly added for pcss recovery.
Once fw is downloaded, host driver can use QMI message to notify
fw enable or disable this feature.

Change-Id: Ifdf49cecc70ab2d554bd267d18ff78d92049f5b8
Kai Liu 2 years ago
parent
commit
648ac5ccff
4 changed files with 145 additions and 7 deletions
  1. 57 5
      cnss2/main.c
  2. 1 0
      cnss2/main.h
  3. 75 1
      cnss2/qmi.c
  4. 12 1
      cnss2/qmi.h

+ 57 - 5
cnss2/main.c

@@ -70,6 +70,11 @@ enum cnss_cal_db_op {
 	CNSS_CAL_DB_INVALID_OP,
 };
 
+enum cnss_recovery_type {
+	CNSS_WLAN_RECOVERY = 0x1,
+	CNSS_PCSS_RECOVERY = 0x2,
+};
+
 static struct cnss_plat_data *plat_env;
 
 static DECLARE_RWSEM(cnss_pm_sem);
@@ -3303,12 +3308,52 @@ static ssize_t enable_hds_store(struct device *dev,
 	return count;
 }
 
+static ssize_t recovery_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct cnss_plat_data *plat_priv = dev_get_drvdata(dev);
+	u32 buf_size = PAGE_SIZE;
+	u32 curr_len = 0;
+	u32 buf_written = 0;
+
+	if (!plat_priv)
+		return -ENODEV;
+
+	buf_written = scnprintf(buf, buf_size,
+				"Usage: echo [recovery_bitmap] > /sys/kernel/cnss/recovery\n"
+				"BIT0 -- wlan fw recovery\n"
+				"BIT1 -- wlan pcss recovery\n"
+				"---------------------------------\n");
+	curr_len += buf_written;
+
+	buf_written = scnprintf(buf + curr_len, buf_size - curr_len,
+				"WLAN recovery %s[%d]\n",
+				plat_priv->recovery_enabled ? "Enabled" : "Disabled",
+				plat_priv->recovery_enabled);
+	curr_len += buf_written;
+
+	buf_written = scnprintf(buf + curr_len, buf_size - curr_len,
+				"WLAN PCSS recovery %s[%d]\n",
+				plat_priv->recovery_pcss_enabled ? "Enabled" : "Disabled",
+				plat_priv->recovery_pcss_enabled);
+	curr_len += buf_written;
+
+	/*
+	 * Now size of curr_len is not over page size for sure,
+	 * later if new item or none-fixed size item added, need
+	 * add check to make sure curr_len is not over page size.
+	 */
+	return curr_len;
+}
+
 static ssize_t recovery_store(struct device *dev,
 			      struct device_attribute *attr,
 			      const char *buf, size_t count)
 {
 	struct cnss_plat_data *plat_priv = dev_get_drvdata(dev);
 	unsigned int recovery = 0;
+	int ret;
 
 	if (!plat_priv)
 		return -ENODEV;
@@ -3318,13 +3363,20 @@ static ssize_t recovery_store(struct device *dev,
 		return -EINVAL;
 	}
 
-	if (recovery)
-		plat_priv->recovery_enabled = true;
-	else
-		plat_priv->recovery_enabled = false;
+	plat_priv->recovery_enabled = !!(recovery & CNSS_WLAN_RECOVERY);
+	plat_priv->recovery_pcss_enabled = !!(recovery & CNSS_PCSS_RECOVERY);
 
 	cnss_pr_dbg("%s WLAN recovery, count is %zu\n",
 		    plat_priv->recovery_enabled ? "Enable" : "Disable", count);
+	cnss_pr_dbg("%s PCSS recovery, count is %zu\n",
+		    plat_priv->recovery_pcss_enabled ? "Enable" : "Disable", count);
+
+	ret = cnss_send_subsys_restart_level_msg(plat_priv);
+	if (ret < 0) {
+		cnss_pr_err("pcss recovery setting failed with ret %d\n", ret);
+		plat_priv->recovery_pcss_enabled = false;
+		return -EINVAL;
+	}
 
 	return count;
 }
@@ -3460,7 +3512,7 @@ static ssize_t charger_mode_store(struct device *dev,
 
 static DEVICE_ATTR_WO(fs_ready);
 static DEVICE_ATTR_WO(shutdown);
-static DEVICE_ATTR_WO(recovery);
+static DEVICE_ATTR_RW(recovery);
 static DEVICE_ATTR_WO(enable_hds);
 static DEVICE_ATTR_WO(qdss_trace_start);
 static DEVICE_ATTR_WO(qdss_trace_stop);

+ 1 - 0
cnss2/main.h

@@ -465,6 +465,7 @@ struct cnss_plat_data {
 	enum cnss_driver_status driver_status;
 	u32 recovery_count;
 	u8 recovery_enabled;
+	u8 recovery_pcss_enabled;
 	u8 hds_enabled;
 	unsigned long driver_state;
 	struct list_head event_list;

+ 75 - 1
cnss2/qmi.c

@@ -86,7 +86,51 @@ static char *cnss_qmi_mode_to_str(enum cnss_driver_mode mode)
 	default:
 		return "UNKNOWN";
 	}
-};
+}
+
+static int qmi_send_wait(struct qmi_handle *qmi, void *req, void *rsp,
+			 struct qmi_elem_info *req_ei,
+			 struct qmi_elem_info *rsp_ei,
+			 int req_id, size_t req_len,
+			 unsigned long timeout)
+{
+	struct qmi_txn txn;
+	int ret;
+	char *err_msg;
+	struct qmi_response_type_v01 *resp = rsp;
+
+	ret = qmi_txn_init(qmi, &txn, rsp_ei, rsp);
+	if (ret < 0) {
+		err_msg = "Qmi fail: fail to init txn,";
+		goto out;
+	}
+
+	ret = qmi_send_request(qmi, NULL, &txn, req_id,
+			       req_len, req_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		err_msg = "Qmi fail: fail to send req,";
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, timeout);
+	if (ret < 0) {
+		err_msg = "Qmi fail: wait timeout,";
+		goto out;
+	} else if (resp->result != QMI_RESULT_SUCCESS_V01) {
+		err_msg = "Qmi fail: request rejected,";
+		cnss_pr_err("Qmi fail: respons with error:%d\n",
+			    resp->error);
+		ret = -resp->result;
+		goto out;
+	}
+
+	cnss_pr_dbg("req %x success\n", req_id);
+	return 0;
+out:
+	cnss_pr_err("%s req %x, ret %d\n", err_msg, req_id, ret);
+	return ret;
+}
 
 static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv)
 {
@@ -3224,6 +3268,36 @@ out:
 	return ret;
 }
 
+int cnss_send_subsys_restart_level_msg(struct cnss_plat_data *plat_priv)
+{
+	int ret;
+	struct wlfw_subsys_restart_level_req_msg_v01 req;
+	struct wlfw_subsys_restart_level_resp_msg_v01 resp;
+	u8 pcss_enabled;
+
+	if (!plat_priv)
+		return -ENODEV;
+
+	if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
+		cnss_pr_err("Can't send pcss cmd before fw ready\n");
+		return -EINVAL;
+	}
+
+	pcss_enabled = plat_priv->recovery_pcss_enabled;
+	cnss_pr_dbg("Sending pcss recovery status: %d\n", pcss_enabled);
+
+	req.restart_level_type_valid = 1;
+	req.restart_level_type = pcss_enabled;
+
+	ret = qmi_send_wait(&plat_priv->qmi_wlfw, &req, &resp,
+			    wlfw_subsys_restart_level_req_msg_v01_ei,
+			    wlfw_subsys_restart_level_resp_msg_v01_ei,
+			    QMI_WLFW_SUBSYS_RESTART_LEVEL_REQ_V01,
+			    WLFW_SUBSYS_RESTART_LEVEL_REQ_MSG_V01_MAX_MSG_LEN,
+			    QMI_WLFW_TIMEOUT_JF);
+	return ret;
+}
+
 static int coex_new_server(struct qmi_handle *qmi,
 			   struct qmi_service *service)
 {

+ 12 - 1
cnss2/qmi.h

@@ -1,5 +1,9 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
 
 #ifndef _CNSS_QMI_H
 #define _CNSS_QMI_H
@@ -84,6 +88,7 @@ int wlfw_qdss_trace_start(struct cnss_plat_data *plat_priv);
 int wlfw_qdss_trace_stop(struct cnss_plat_data *plat_priv, unsigned long long option);
 int cnss_wlfw_cal_report_req_send_sync(struct cnss_plat_data *plat_priv,
 				       u32 cal_file_download_size);
+int cnss_send_subsys_restart_level_msg(struct cnss_plat_data *plat_priv);
 #else
 #define QMI_WLFW_TIMEOUT_MS		10000
 
@@ -299,6 +304,12 @@ int cnss_wlfw_cal_report_req_send_sync(struct cnss_plat_data *plat_priv,
 {
 	return 0;
 }
+
+static inline
+int cnss_send_subsys_restart_level_msg(struct cnss_plat_data *plat_priv)
+{
+	return 0;
+}
 #endif /* CONFIG_CNSS2_QMI */
 
 #ifdef CONFIG_CNSS2_DEBUG