瀏覽代碼

Merge 08ec0c895d767025483b47736d3169d9d57897d5 on remote branch

Change-Id: Ibaf748d50abac010d18a09d3c39ead5b44f4e919
Linux Build Service Account 10 月之前
父節點
當前提交
e2d94dac9f
共有 12 個文件被更改,包括 371 次插入129 次删除
  1. 11 0
      cnss2/debug.c
  2. 2 1
      cnss2/main.c
  3. 1 3
      cnss2/main.h
  4. 60 24
      cnss2/pci.c
  5. 170 91
      cnss2/power.c
  6. 7 0
      cnss2/qmi.c
  7. 69 0
      cnss_utils/wlan_firmware_service_v01.c
  8. 16 3
      cnss_utils/wlan_firmware_service_v01.h
  9. 13 5
      icnss2/main.c
  10. 1 0
      icnss2/main.h
  11. 13 1
      icnss2/qmi.c
  12. 8 1
      inc/icnss2.h

+ 11 - 0
cnss2/debug.c

@@ -73,6 +73,15 @@ static const struct file_operations cnss_pin_connect_fops = {
 	.llseek		= seq_lseek,
 };
 
+static u64 cnss_get_serial_id(struct cnss_plat_data *plat_priv)
+{
+	u32 msb = plat_priv->serial_id.serial_id_msb;
+	u32 lsb = plat_priv->serial_id.serial_id_lsb;
+
+	msb &= 0xFFFF;
+	return (((u64)msb << 32) | lsb);
+}
+
 static int cnss_stats_show_state(struct seq_file *s,
 				 struct cnss_plat_data *plat_priv)
 {
@@ -80,6 +89,8 @@ static int cnss_stats_show_state(struct seq_file *s,
 	int skip = 0;
 	unsigned long state;
 
+	seq_printf(s, "\nSerial Number: 0x%llx",
+		   cnss_get_serial_id(plat_priv));
 	seq_printf(s, "\nState: 0x%lx(", plat_priv->driver_state);
 	for (i = 0, state = plat_priv->driver_state; state != 0;
 	     state >>= 1, i++) {

+ 2 - 1
cnss2/main.c

@@ -4742,7 +4742,8 @@ int cnss_wlan_hw_disable_check(struct cnss_plat_data *plat_priv)
 
 	peripheralStateInfo = qcom_smem_get(QCOM_SMEM_HOST_ANY, PERISEC_SMEM_ID, &size);
 	if (IS_ERR_OR_NULL(peripheralStateInfo)) {
-		if (PTR_ERR(peripheralStateInfo) != -ENOENT)
+		if (PTR_ERR(peripheralStateInfo) != -ENOENT &&
+		    PTR_ERR(peripheralStateInfo) != -ENODEV)
 			CNSS_ASSERT(0);
 
 		cnss_pr_dbg("Secure HW feature not enabled. ret = %d\n",

+ 1 - 3
cnss2/main.h

@@ -620,10 +620,7 @@ struct cnss_plat_data {
 	u8 charger_mode;
 	struct mbox_client mbox_client_data;
 	struct mbox_chan *mbox_chan;
-#if IS_ENABLED(CONFIG_MSM_QMP)
 	struct qmp *qmp;
-#endif
-	bool use_direct_qmp;
 	const char *vreg_ol_cpr, *vreg_ipa;
 	const char **pdc_init_table, **vreg_pdc_map, **pmu_vreg_map;
 	int pdc_init_table_len, vreg_pdc_map_len, pmu_vreg_map_len;
@@ -648,6 +645,7 @@ struct cnss_plat_data {
 	u32 *on_chip_pmic_board_ids;
 	bool no_bwscale;
 	bool sleep_clk;
+	struct wlchip_serial_id_v01 serial_id;
 };
 
 #if IS_ENABLED(CONFIG_ARCH_QCOM)

+ 60 - 24
cnss2/pci.c

@@ -6931,14 +6931,61 @@ static void cnss_mhi_write_reg(struct mhi_controller *mhi_ctrl,
 	writel_relaxed(val, addr);
 }
 
+#if IS_ENABLED(CONFIG_MHI_BUS_MISC)
+/**
+ * __cnss_get_mhi_soc_info - Get SoC info before registering mhi controller
+ * @mhi_ctrl: MHI controller
+ *
+ * Return: 0 for success, error code on failure
+ */
+static inline int __cnss_get_mhi_soc_info(struct mhi_controller *mhi_ctrl)
+{
+	return mhi_get_soc_info(mhi_ctrl);
+}
+#else
+#define SOC_HW_VERSION_OFFS (0x224)
+#define SOC_HW_VERSION_FAM_NUM_BMSK (0xF0000000)
+#define SOC_HW_VERSION_FAM_NUM_SHFT (28)
+#define SOC_HW_VERSION_DEV_NUM_BMSK (0x0FFF0000)
+#define SOC_HW_VERSION_DEV_NUM_SHFT (16)
+#define SOC_HW_VERSION_MAJOR_VER_BMSK (0x0000FF00)
+#define SOC_HW_VERSION_MAJOR_VER_SHFT (8)
+#define SOC_HW_VERSION_MINOR_VER_BMSK (0x000000FF)
+#define SOC_HW_VERSION_MINOR_VER_SHFT (0)
+
+static int __cnss_get_mhi_soc_info(struct mhi_controller *mhi_ctrl)
+{
+	u32 soc_info;
+	int ret;
+
+	ret = mhi_ctrl->read_reg(mhi_ctrl,
+				 mhi_ctrl->regs + SOC_HW_VERSION_OFFS,
+				 &soc_info);
+	if (ret)
+		return ret;
+
+	mhi_ctrl->family_number = (soc_info & SOC_HW_VERSION_FAM_NUM_BMSK) >>
+		SOC_HW_VERSION_FAM_NUM_SHFT;
+	mhi_ctrl->device_number = (soc_info & SOC_HW_VERSION_DEV_NUM_BMSK) >>
+		SOC_HW_VERSION_DEV_NUM_SHFT;
+	mhi_ctrl->major_version = (soc_info & SOC_HW_VERSION_MAJOR_VER_BMSK) >>
+		SOC_HW_VERSION_MAJOR_VER_SHFT;
+	mhi_ctrl->minor_version = (soc_info & SOC_HW_VERSION_MINOR_VER_BMSK) >>
+		SOC_HW_VERSION_MINOR_VER_SHFT;
+	return 0;
+}
+#endif
+
 static int cnss_get_mhi_soc_info(struct cnss_plat_data *plat_priv,
 				 struct mhi_controller *mhi_ctrl)
 {
 	int ret = 0;
 
-	ret = mhi_get_soc_info(mhi_ctrl);
-	if (ret)
+	ret = __cnss_get_mhi_soc_info(mhi_ctrl);
+	if (ret) {
+		cnss_pr_err("failed to get mhi soc info, ret %d\n", ret);
 		goto exit;
+	}
 
 	plat_priv->device_version.family_number = mhi_ctrl->family_number;
 	plat_priv->device_version.device_number = mhi_ctrl->device_number;
@@ -7388,24 +7435,6 @@ static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev)
 }
 #endif
 
-static int cnss_pci_set_gen2_speed(struct cnss_plat_data *plat_priv, u32 rc_num)
-{
-	int ret;
-
-	/* Always set initial target PCIe link speed to Gen2 for QCA6490 device
-	 * since there may be link issues if it boots up with Gen3 link speed.
-	 * Device is able to change it later at any time. It will be rejected
-	 * if requested speed is higher than the one specified in PCIe DT.
-	 */
-	ret = cnss_pci_set_max_link_speed(plat_priv->bus_priv, rc_num,
-					  PCI_EXP_LNKSTA_CLS_5_0GB);
-	if (ret && ret != -EPROBE_DEFER)
-		cnss_pr_err("Failed to set max PCIe RC%x link speed to Gen2, err = %d\n",
-				rc_num, ret);
-
-	return ret;
-}
-
 #ifdef CONFIG_CNSS2_ENUM_WITH_LOW_SPEED
 static void
 cnss_pci_downgrade_rc_speed(struct cnss_plat_data *plat_priv, u32 rc_num)
@@ -7426,9 +7455,7 @@ cnss_pci_restore_rc_speed(struct cnss_pci_data *pci_priv)
 	struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
 
 	/* if not Genoa, do not restore rc speed */
-	if (pci_priv->device_id == QCA6490_DEVICE_ID) {
-		cnss_pci_set_gen2_speed(plat_priv, plat_priv->rc_num);
-	} else if (pci_priv->device_id != QCN7605_DEVICE_ID) {
+	if (pci_priv->device_id != QCN7605_DEVICE_ID) {
 		/* The request 0 will reset maximum GEN speed to default */
 		ret = cnss_pci_set_max_link_speed(pci_priv, plat_priv->rc_num, 0);
 		if (ret)
@@ -7727,8 +7754,17 @@ static int cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num)
 {
 	int ret, retry = 0;
 
+	/* Always set initial target PCIe link speed to Gen2 for QCA6490 device
+	 * since there may be link issues if it boots up with Gen3 link speed.
+	 * Device is able to change it later at any time. It will be rejected
+	 * if requested speed is higher than the one specified in PCIe DT.
+	 */
 	if (plat_priv->device_id == QCA6490_DEVICE_ID) {
-		cnss_pci_set_gen2_speed(plat_priv, rc_num);
+		ret = cnss_pci_set_max_link_speed(plat_priv->bus_priv, rc_num,
+						  PCI_EXP_LNKSTA_CLS_5_0GB);
+		if (ret && ret != -EPROBE_DEFER)
+			cnss_pr_err("Failed to set max PCIe RC%x link speed to Gen2, err = %d\n",
+				    rc_num, ret);
 	} else {
 		cnss_pci_downgrade_rc_speed(plat_priv, rc_num);
 	}

+ 170 - 91
cnss2/power.c

@@ -18,9 +18,7 @@
 #include "main.h"
 #include "debug.h"
 #include "bus.h"
-#if IS_ENABLED(CONFIG_MSM_QMP)
 #include <linux/soc/qcom/qcom_aoss.h>
-#endif
 
 #if IS_ENABLED(CONFIG_ARCH_QCOM)
 static struct cnss_vreg_cfg cnss_vreg_list[] = {
@@ -1360,49 +1358,180 @@ out:
 
 #if IS_ENABLED(CONFIG_MSM_QMP)
 /**
- * cnss_aop_interface_init: Initialize AOP interface: either mbox channel or direct QMP
+ * cnss_mbox_init: Initialize mbox interface
  * @plat_priv: Pointer to cnss platform data
  *
- * Device tree file should have either mbox or qmp configured, but not both.
- * Based on device tree configuration setup mbox channel or QMP
+ * Try to get property 'mboxes' from device tree and
+ * initialize the interface for AOP configuration.
  *
  * Return: 0 for success, otherwise error code
  */
-int cnss_aop_interface_init(struct cnss_plat_data *plat_priv)
+static int cnss_mbox_init(struct cnss_plat_data *plat_priv)
 {
 	struct mbox_client *mbox = &plat_priv->mbox_client_data;
 	struct mbox_chan *chan;
-	int ret;
+	int ret = 0;
 
 	plat_priv->mbox_chan = NULL;
-	plat_priv->qmp = NULL;
-	plat_priv->use_direct_qmp = false;
-
 	mbox->dev = &plat_priv->plat_dev->dev;
 	mbox->tx_block = true;
 	mbox->tx_tout = CNSS_MBOX_TIMEOUT_MS;
 	mbox->knows_txdone = false;
-
-	/* First try to get mbox channel, if it fails then try qmp_get
-	 * In device tree file there should be either mboxes or qmp,
-	 * cannot have both properties at the same time.
-	 */
 	chan = mbox_request_channel(mbox, 0);
 	if (IS_ERR(chan)) {
-		cnss_pr_dbg("Failed to get mbox channel, try qmp get\n");
-		plat_priv->qmp = qmp_get(&plat_priv->plat_dev->dev);
-		if (IS_ERR(plat_priv->qmp)) {
-			cnss_pr_err("Failed to get qmp\n");
-			return PTR_ERR(plat_priv->qmp);
-		} else {
-			plat_priv->use_direct_qmp = true;
-			cnss_pr_dbg("QMP initialized\n");
-		}
+		ret = PTR_ERR(chan);
+		cnss_pr_dbg("Failed to get mbox channel[%d]\n", ret);
 	} else {
 		plat_priv->mbox_chan = chan;
 		cnss_pr_dbg("Mbox channel initialized\n");
 	}
 
+	return ret;
+}
+
+/**
+ * cnss_mbox_deinit: De-Initialize mbox interface
+ * @plat_priv: Pointer to cnss platform data
+ *
+ * Return: None
+ */
+static void cnss_mbox_deinit(struct cnss_plat_data *plat_priv)
+{
+	if (!plat_priv->mbox_chan) {
+		mbox_free_channel(plat_priv->mbox_chan);
+		plat_priv->mbox_chan = NULL;
+	}
+}
+
+/**
+ * cnss_mbox_send_msg: Send json message to AOP using mbox channel
+ * @plat_priv: Pointer to cnss platform data
+ * @msg: String in json format
+ *
+ * Return: 0 for success, otherwise error code
+ */
+static int
+cnss_mbox_send_msg(struct cnss_plat_data *plat_priv, char *mbox_msg)
+{
+	struct qmp_pkt pkt;
+	int ret = 0;
+
+	if (!plat_priv->mbox_chan)
+		return -ENODEV;
+
+	cnss_pr_dbg("Sending AOP Mbox msg: %s\n", mbox_msg);
+	pkt.size = CNSS_MBOX_MSG_MAX_LEN;
+	pkt.data = mbox_msg;
+	ret = mbox_send_message(plat_priv->mbox_chan, &pkt);
+	if (ret < 0)
+		cnss_pr_err("Failed to send AOP mbox msg: %s\n", mbox_msg);
+
+	return ret;
+}
+#else
+static inline int cnss_mbox_init(struct cnss_plat_data *plat_priv)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void cnss_mbox_deinit(struct cnss_plat_data *plat_priv)
+{
+}
+
+static inline int
+cnss_mbox_send_msg(struct cnss_plat_data *plat_priv, char *mbox_msg)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+/**
+ * cnss_qmp_init: Initialize direct QMP interface
+ * @plat_priv: Pointer to cnss platform data
+ *
+ * Try to get property 'qcom,qmp' from device tree and
+ * initialize the interface for AOP configuration.
+ *
+ * Return: 0 for success, otherwise error code
+ */
+static int cnss_qmp_init(struct cnss_plat_data *plat_priv)
+{
+	struct qmp *qmp;
+
+	plat_priv->qmp = NULL;
+	qmp = qmp_get(&plat_priv->plat_dev->dev);
+	if (IS_ERR(qmp)) {
+		cnss_pr_err("Failed to get qmp: %d\n",
+			    PTR_ERR(qmp));
+		return PTR_ERR(qmp);
+	}
+
+	plat_priv->qmp = qmp;
+	cnss_pr_dbg("QMP initialized\n");
+	return 0;
+}
+
+/**
+ * cnss_qmp_deinit: De-Initialize direct QMP interface
+ * @plat_priv: Pointer to cnss platform data
+ *
+ * Return: None
+ */
+static void cnss_qmp_deinit(struct cnss_plat_data *plat_priv)
+{
+	if (plat_priv->qmp) {
+		qmp_put(plat_priv->qmp);
+		plat_priv->qmp = NULL;
+	}
+}
+
+/**
+ * cnss_qmp_send_msg: Send json message to AOP using direct QMP
+ * @plat_priv: Pointer to cnss platform data
+ * @msg: String in json format
+ *
+ * Return: 0 for success, otherwise error code
+ */
+static int
+cnss_qmp_send_msg(struct cnss_plat_data *plat_priv, char *mbox_msg)
+{
+	int ret;
+
+	if (!plat_priv->qmp)
+		return -ENODEV;
+
+	cnss_pr_dbg("Sending AOP QMP msg: %s\n", mbox_msg);
+	ret = qmp_send(plat_priv->qmp, mbox_msg, CNSS_MBOX_MSG_MAX_LEN);
+	if (ret)
+		cnss_pr_err("Failed to send AOP QMP msg: %d[%s]\n", ret, mbox_msg);
+
+	return ret;
+}
+
+/**
+ * cnss_aop_interface_init: Initialize AOP interface: either mbox channel or direct QMP
+ * @plat_priv: Pointer to cnss platform data
+ *
+ * Device tree file should have either mbox or qmp configured, but not both.
+ * Based on device tree configuration setup mbox channel or QMP
+ *
+ * Return: 0 for success, otherwise error code
+ */
+int cnss_aop_interface_init(struct cnss_plat_data *plat_priv)
+{
+	int ret;
+
+	/* First try to get mbox channel, if it fails then try qmp_get
+	 * In device tree file there should be either mboxes or qmp,
+	 * cannot have both properties at the same time.
+	 */
+	ret = cnss_mbox_init(plat_priv);
+	if (ret) {
+		ret = cnss_qmp_init(plat_priv);
+		if (ret)
+			return ret;
+	}
+
 	ret = cnss_aop_pdc_reconfig(plat_priv);
 	if (ret)
 		cnss_pr_err("Failed to reconfig WLAN PDC, err = %d\n", ret);
@@ -1420,13 +1549,8 @@ int cnss_aop_interface_init(struct cnss_plat_data *plat_priv)
  */
 void cnss_aop_interface_deinit(struct cnss_plat_data *plat_priv)
 {
-	if (!IS_ERR_OR_NULL(plat_priv->mbox_chan))
-		mbox_free_channel(plat_priv->mbox_chan);
-
-	if (!IS_ERR_OR_NULL(plat_priv->qmp)) {
-		qmp_put(plat_priv->qmp);
-		plat_priv->use_direct_qmp = false;
-	}
+	cnss_mbox_deinit(plat_priv);
+	cnss_qmp_deinit(plat_priv);
 }
 
 /**
@@ -1445,31 +1569,22 @@ void cnss_aop_interface_deinit(struct cnss_plat_data *plat_priv)
  */
 int cnss_aop_send_msg(struct cnss_plat_data *plat_priv, char *mbox_msg)
 {
-	struct qmp_pkt pkt;
-	int ret = 0;
-
+	int ret;
 
-	if (plat_priv->use_direct_qmp) {
-		cnss_pr_dbg("Sending AOP QMP msg: %s\n", mbox_msg);
-		ret = qmp_send(plat_priv->qmp, mbox_msg, CNSS_MBOX_MSG_MAX_LEN);
-		if (ret < 0)
-			cnss_pr_err("Failed to send AOP QMP msg: %s\n", mbox_msg);
-		else
-			ret = 0;
-	} else {
-		cnss_pr_dbg("Sending AOP Mbox msg: %s\n", mbox_msg);
-		pkt.size = CNSS_MBOX_MSG_MAX_LEN;
-		pkt.data = mbox_msg;
-		ret = mbox_send_message(plat_priv->mbox_chan, &pkt);
-		if (ret < 0)
-			cnss_pr_err("Failed to send AOP mbox msg: %s\n", mbox_msg);
-		else
-			ret = 0;
-	}
+	ret = cnss_mbox_send_msg(plat_priv, mbox_msg);
+	if (ret)
+		ret = cnss_qmp_send_msg(plat_priv, mbox_msg);
 
+	if (ret)
+		cnss_pr_err("Failed to send AOP msg: %d\n", ret);
 	return ret;
 }
 
+static inline bool cnss_aop_interface_ready(struct cnss_plat_data *plat_priv)
+{
+	return (plat_priv->mbox_chan || plat_priv->qmp);
+}
+
 /* cnss_pdc_reconfig: Send PDC init table as configured in DT for wlan device */
 int cnss_aop_pdc_reconfig(struct cnss_plat_data *plat_priv)
 {
@@ -1662,42 +1777,6 @@ end:
 	return ret;
 }
 
-#else
-int cnss_aop_interface_init(struct cnss_plat_data *plat_priv)
-{
-	return 0;
-}
-
-void cnss_aop_interface_deinit(struct cnss_plat_data *plat_priv)
-{
-}
-
-int cnss_aop_send_msg(struct cnss_plat_data *plat_priv, char *msg)
-{
-	return 0;
-}
-
-int cnss_aop_pdc_reconfig(struct cnss_plat_data *plat_priv)
-{
-	return 0;
-}
-
-static int cnss_aop_set_vreg_param(struct cnss_plat_data *plat_priv,
-				   const char *vreg_name,
-				   enum cnss_aop_vreg_param param,
-				   enum cnss_aop_tcs_seq_param seq_param,
-				   int val)
-{
-	return 0;
-}
-
-int cnss_aop_ol_cpr_cfg_setup(struct cnss_plat_data *plat_priv,
-			      struct wlfw_pmu_cfg_v01 *fw_pmu_cfg)
-{
-	return 0;
-}
-#endif
-
 void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 {
 	struct device *dev = &plat_priv->plat_dev->dev;
@@ -1820,8 +1899,8 @@ int cnss_update_cpr_info(struct cnss_plat_data *plat_priv)
 		return -EINVAL;
 
 	if (!plat_priv->vreg_ol_cpr ||
-	    (!plat_priv->mbox_chan && !plat_priv->use_direct_qmp)) {
-		cnss_pr_dbg("Mbox channel / QMP / OL CPR Vreg not configured\n");
+	    !cnss_aop_interface_ready(plat_priv)) {
+		cnss_pr_dbg("AOP interface / OL CPR Vreg not configured\n");
 	} else {
 		return cnss_aop_set_vreg_param(plat_priv,
 					       plat_priv->vreg_ol_cpr,
@@ -1901,8 +1980,8 @@ int cnss_enable_int_pow_amp_vreg(struct cnss_plat_data *plat_priv)
 	}
 
 	if (!plat_priv->vreg_ipa ||
-	    (!plat_priv->mbox_chan && !plat_priv->use_direct_qmp)) {
-		cnss_pr_dbg("Mbox channel / QMP / IPA Vreg not configured\n");
+	    !cnss_aop_interface_ready(plat_priv)) {
+		cnss_pr_dbg("AOP interface / IPA Vreg not configured\n");
 	} else {
 		ret = cnss_aop_set_vreg_param(plat_priv,
 					      plat_priv->vreg_ipa,

+ 7 - 0
cnss2/qmi.c

@@ -649,6 +649,13 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv)
 		}
 	}
 
+	if (resp->serial_id_valid) {
+		plat_priv->serial_id = resp->serial_id;
+		cnss_pr_info("serial id  0x%x 0x%x\n",
+			     resp->serial_id.serial_id_msb,
+			     resp->serial_id.serial_id_lsb);
+	}
+
 	cnss_pr_dbg("Target capability: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, otp_version: 0x%x\n",
 		    plat_priv->chip_info.chip_id,
 		    plat_priv->chip_info.chip_family,

+ 69 - 0
cnss_utils/wlan_firmware_service_v01.c

@@ -832,6 +832,34 @@ static struct qmi_elem_info wlfw_host_pcie_link_info_s_v01_ei[] = {
 	},
 };
 
+static struct qmi_elem_info wlchip_serial_id_v01_ei[] = {
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0,
+		.offset         = offsetof(struct
+					   wlchip_serial_id_v01,
+					   serial_id_msb),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0,
+		.offset         = offsetof(struct
+					   wlchip_serial_id_v01,
+					   serial_id_lsb),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type       = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
 struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
 	{
 		.data_type      = QMI_OPT_FLAG,
@@ -2290,6 +2318,27 @@ struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = {
 					   wlfw_cap_resp_msg_v01,
 					   phy_qam_cap),
 	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x28,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   serial_id_valid),
+	},
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct wlchip_serial_id_v01),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x28,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   serial_id),
+		.ei_array      = wlchip_serial_id_v01_ei,
+	},
 	{
 		.data_type      = QMI_EOTI,
 		.array_type       = NO_ARRAY,
@@ -4320,6 +4369,26 @@ struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = {
 					   wlfw_fw_init_done_ind_msg_v01,
 					   hang_data_length),
 	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x12,
+		.offset         = offsetof(struct
+					   wlfw_fw_init_done_ind_msg_v01,
+					   soft_sku_features_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_8_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u64),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x12,
+		.offset         = offsetof(struct
+					   wlfw_fw_init_done_ind_msg_v01,
+					   soft_sku_features),
+	},
 	{
 		.data_type      = QMI_EOTI,
 		.array_type       = NO_ARRAY,

+ 16 - 3
cnss_utils/wlan_firmware_service_v01.h

@@ -200,6 +200,7 @@ enum wlfw_mem_type_enum_v01 {
 	QMI_WLFW_PAGEABLE_MEM_V01 = 9,
 	QMI_WLFW_AFC_MEM_V01 = 10,
 	QMI_WLFW_MEM_LPASS_SHARED_V01 = 11,
+	QMI_WLFW_MEM_CALDB_SEG_V01 = 12,
 	WLFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
 };
 
@@ -330,7 +331,8 @@ enum wlfw_gpio_info_type_v01 {
 	BT_EN_GPIO_V01 = 1,
 	HOST_SOL_GPIO_V01 = 2,
 	TARGET_SOL_GPIO_V01 = 3,
-	GPIO_TYPE_MAX_V01 = 4,
+	WLAN_SW_CTRL_GPIO_V01 = 4,
+	GPIO_TYPE_MAX_V01 = 5,
 	WLFW_GPIO_INFO_TYPE_MAX_VAL_V01 = INT_MAX,
 };
 
@@ -408,6 +410,8 @@ enum wlfw_lpass_ssr_reason_v01 {
 #define QMI_WLFW_AUX_UC_SUPPORT_V01 ((u64)0x04ULL)
 #define QMI_WLFW_CALDB_SEG_DDR_SUPPORT_V01 ((u64)0x08ULL)
 
+#define QMI_WLFW_DIRECT_LINK_SKU_SUPPORT_V01 ((u64)0x01ULL)
+
 struct wlfw_ce_tgt_pipe_cfg_s_v01 {
 	u32 pipe_num;
 	enum wlfw_pipedir_enum_v01 pipe_dir;
@@ -543,6 +547,11 @@ struct wlfw_host_pcie_link_info_s_v01 {
 	u32 pci_link_width;
 };
 
+struct wlchip_serial_id_v01 {
+	u32 serial_id_msb;
+	u32 serial_id_lsb;
+};
+
 struct wlfw_ind_register_req_msg_v01 {
 	u8 fw_ready_enable_valid;
 	u8 fw_ready_enable;
@@ -731,8 +740,10 @@ struct wlfw_cap_resp_msg_v01 {
 	enum wlfw_he_channel_width_cap_v01 he_channel_width_cap;
 	u8 phy_qam_cap_valid;
 	enum wlfw_phy_qam_cap_v01 phy_qam_cap;
+	u8 serial_id_valid;
+	struct wlchip_serial_id_v01 serial_id;
 };
-#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 1160
+#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 1171
 extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[];
 
 struct wlfw_bdf_download_req_msg_v01 {
@@ -1061,8 +1072,10 @@ struct wlfw_fw_init_done_ind_msg_v01 {
 	u32 hang_data_addr_offset;
 	u8 hang_data_length_valid;
 	u16 hang_data_length;
+	u8 soft_sku_features_valid;
+	u64 soft_sku_features;
 };
-#define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 12
+#define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 23
 extern struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[];
 
 struct wlfw_rejuvenate_ind_msg_v01 {

+ 13 - 5
icnss2/main.c

@@ -2201,14 +2201,20 @@ static int icnss_wpss_notifier_nb(struct notifier_block *nb,
 	icnss_pr_vdbg("WPSS-Notify: event %s(%lu)\n",
 		      icnss_qcom_ssr_notify_state_to_str(code), code);
 
-	if (code == QCOM_SSR_AFTER_SHUTDOWN) {
-		icnss_pr_info("Collecting msa0 segment dump\n");
-		icnss_msa0_ramdump(priv);
+	switch (code) {
+	case QCOM_SSR_BEFORE_SHUTDOWN:
+		break;
+	case QCOM_SSR_AFTER_SHUTDOWN:
+		/* Collect ramdump only when there was a crash. */
+		if (notif->crashed) {
+			icnss_pr_info("Collecting msa0 segment dump\n");
+			icnss_msa0_ramdump(priv);
+		}
+		goto out;
+	default:
 		goto out;
 	}
 
-	if (code != QCOM_SSR_BEFORE_SHUTDOWN)
-		goto out;
 
 	if (priv->wpss_self_recovery_enabled)
 		del_timer(&priv->wpss_ssr_timer);
@@ -3400,6 +3406,8 @@ int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
 	info->rd_card_chain_cap = priv->rd_card_chain_cap;
 	info->phy_he_channel_width_cap = priv->phy_he_channel_width_cap;
 	info->phy_qam_cap = priv->phy_qam_cap;
+	memcpy(&info->dev_mem_info, &priv->dev_mem_info,
+	       sizeof(info->dev_mem_info));
 
 	return 0;
 }

+ 1 - 0
icnss2/main.h

@@ -433,6 +433,7 @@ struct icnss_priv {
 	uint32_t nr_mem_region;
 	struct icnss_mem_region_info
 		mem_region[WLFW_MAX_NUM_MEMORY_REGIONS];
+	struct icnss_dev_mem_info dev_mem_info[ICNSS_MAX_DEV_MEM_NUM];
 	struct dentry *root_dentry;
 	spinlock_t on_off_lock;
 	struct icnss_stats stats;

+ 13 - 1
icnss2/qmi.c

@@ -695,7 +695,7 @@ out:
 
 int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
 {
-	int ret;
+	int ret = 0, i = 0;
 	struct wlfw_cap_req_msg_v01 *req;
 	struct wlfw_cap_resp_msg_v01 *resp;
 	struct qmi_txn txn;
@@ -774,6 +774,18 @@ int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
 				WLFW_MAX_TIMESTAMP_LEN + 1);
 	}
 
+	if (resp->dev_mem_info_valid) {
+		for (i = 0; i < QMI_WLFW_MAX_DEV_MEM_NUM_V01; i++) {
+			priv->dev_mem_info[i].start =
+				resp->dev_mem_info[i].start;
+			priv->dev_mem_info[i].size =
+				resp->dev_mem_info[i].size;
+			icnss_pr_info("Device memory info[%d]: start = 0x%llx, size = 0x%llx\n",
+				      i, priv->dev_mem_info[i].start,
+				      priv->dev_mem_info[i].size);
+		}
+	}
+
 	if (resp->voltage_mv_valid) {
 		priv->cpr_info.voltage = resp->voltage_mv;
 		icnss_pr_dbg("Voltage for CPR: %dmV\n",

+ 8 - 1
inc/icnss2.h

@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #ifndef _ICNSS_WLAN_H_
 #define _ICNSS_WLAN_H_
@@ -13,6 +13,7 @@
 #define IWCN_MAX_IRQ_REGISTRATIONS    32
 #define ICNSS_MAX_TIMESTAMP_LEN        32
 #define ICNSS_WLFW_MAX_BUILD_ID_LEN    128
+#define ICNSS_MAX_DEV_MEM_NUM            4
 
 #define DEVICE_NAME_MAX		10
 enum icnss_uevent {
@@ -40,6 +41,11 @@ struct icnss_uevent_data {
 	void *data;
 };
 
+struct icnss_dev_mem_info {
+	u64 start;
+	u64 size;
+};
+
 /* Device information like supported device ids, etc*/
 struct device_info {
 	char name[DEVICE_NAME_MAX];
@@ -165,6 +171,7 @@ struct icnss_soc_info {
 	enum icnss_rd_card_chain_cap rd_card_chain_cap;
 	enum icnss_phy_he_channel_width_cap phy_he_channel_width_cap;
 	enum icnss_phy_qam_cap phy_qam_cap;
+	struct icnss_dev_mem_info dev_mem_info[ICNSS_MAX_DEV_MEM_NUM];
 };
 
 #define icnss_register_driver(ops)		\