Forráskód Böngészése

wlan_platform: Bring initial files for ICNSS family drivers

Bring ICNSS family drivers from msm-5.10 kernel as of commit
'cb298739ee51 ("icnss2: Download bdf file for helium targets")'
to WLAN platform project.

Updated wlan_firmware_service files and makefiles to support
ICNSS2 compilation.

Change-Id: I02cf792b61772f07ac0607be7bb6b3bfda7815e4
Naman Padhiar 3 éve
szülő
commit
3983bc4503
18 módosított fájl, 11552 hozzáadás és 2 törlés
  1. 9 0
      Android.mk
  2. 9 0
      Kbuild
  3. 3 0
      Makefile
  4. 219 0
      cnss_utils/wlan_firmware_service_v01.c
  5. 57 2
      cnss_utils/wlan_firmware_service_v01.h
  6. 38 0
      icnss2/Kconfig
  7. 16 0
      icnss2/Makefile
  8. 891 0
      icnss2/debug.c
  9. 108 0
      icnss2/debug.h
  10. 213 0
      icnss2/genl.c
  11. 17 0
      icnss2/genl.h
  12. 4559 0
      icnss2/main.c
  13. 512 0
      icnss2/main.h
  14. 942 0
      icnss2/power.c
  15. 19 0
      icnss2/power.h
  16. 3469 0
      icnss2/qmi.c
  17. 264 0
      icnss2/qmi.h
  18. 207 0
      inc/icnss2.h

+ 9 - 0
Android.mk

@@ -93,3 +93,12 @@ LOCAL_MODULE_TAGS         := optional
 LOCAL_MODULE_DEBUG_ENABLE := true
 LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
 include $(DLKM_DIR)/Build_external_kernelmodule.mk
+################################ icnss2 ################################
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES           := $(CNSS_SRC_FILES)
+LOCAL_MODULE              := icnss2.ko
+LOCAL_MODULE_KBUILD_NAME  := icnss2/icnss2.ko
+LOCAL_MODULE_TAGS         := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+include $(DLKM_DIR)/Build_external_kernelmodule.mk

+ 9 - 0
Kbuild

@@ -10,6 +10,14 @@ ifeq ($(CONFIG_CNSS2_QMI),y)
 KBUILD_CPPFLAGS += -DCONFIG_CNSS2_QMI
 endif
 
+ifeq ($(CONFIG_ICNSS2_DEBUG),y)
+KBUILD_CPPFLAGS += -DCONFIG_ICNSS2_DEBUG
+endif
+
+ifeq ($(CONFIG_ICNSS2_QMI),y)
+KBUILD_CPPFLAGS += -DCONFIG_ICNSS2_QMI
+endif
+
 # CONFIG_CNSS_PLAT_IPC_QMI_SVC should never be "y" here since it
 # can be only compiled as a module from out-of-kernel-tree source.
 ifeq ($(CONFIG_CNSS_PLAT_IPC_QMI_SVC),m)
@@ -17,6 +25,7 @@ KBUILD_CPPFLAGS += -DCONFIG_CNSS_PLAT_IPC_QMI_SVC
 endif
 
 obj-$(CONFIG_CNSS2) += cnss2/
+obj-$(CONFIG_ICNSS2) += icnss2/
 obj-$(CONFIG_CNSS_GENL) += cnss_genl/
 obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/
 obj-y += cnss_utils/

+ 3 - 0
Makefile

@@ -11,8 +11,11 @@ WLAN_PLATFORM_ROOT = $(shell pwd)
 KBUILD_OPTIONS := WLAN_PLATFORM_ROOT=$(WLAN_PLATFORM_ROOT)
 KBUILD_OPTIONS += CONFIG_CNSS_OUT_OF_TREE=y
 KBUILD_OPTIONS += CONFIG_CNSS2=m
+KBUILD_OPTIONS += CONFIG_ICNSS2=m
 KBUILD_OPTIONS += CONFIG_CNSS2_QMI=y
+KBUILD_OPTIONS += CONFIG_ICNSS2_QMI=y
 KBUILD_OPTIONS += CONFIG_CNSS2_DEBUG=y
+KBUILD_OPTIONS += CONFIG_ICNSS2_DEBUG=y
 KBUILD_OPTIONS += CONFIG_CNSS_QMI_SVC=m
 KBUILD_OPTIONS += CONFIG_CNSS_PLAT_IPC_QMI_SVC=m
 KBUILD_OPTIONS += CONFIG_CNSS_GENL=m

+ 219 - 0
cnss_utils/wlan_firmware_service_v01.c

@@ -1766,6 +1766,106 @@ struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = {
 					   dev_mem_info),
 		.ei_array      = wlfw_dev_mem_info_s_v01_ei,
 	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1D,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   foundry_name_valid),
+	},
+	{
+		.data_type      = QMI_STRING,
+		.elem_len       = QMI_WLFW_MAX_STR_LEN_V01 + 1,
+		.elem_size      = sizeof(char),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1D,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   foundry_name),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1E,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hang_data_addr_offset_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1E,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hang_data_addr_offset),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1F,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hang_data_length_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_2_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u16),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x1F,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hang_data_length),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x20,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   bdf_dnld_method_valid),
+	},
+	{
+		.data_type      = QMI_SIGNED_4_BYTE_ENUM,
+		.elem_len       = 1,
+		.elem_size      = sizeof(enum wlfw_bdf_dnld_method_v01),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x20,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   bdf_dnld_method),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x21,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hwid_bitmap_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x21,
+		.offset         = offsetof(struct
+					   wlfw_cap_resp_msg_v01,
+					   hwid_bitmap),
+	},
 	{
 		.data_type      = QMI_EOTI,
 		.array_type       = NO_ARRAY,
@@ -3475,6 +3575,76 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = {
 					   wlfw_host_cap_req_msg_v01,
 					   wake_msi_addr),
 	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2B,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   wlan_enable_delay_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2B,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   wlan_enable_delay),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2C,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   ddr_type_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u32),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2C,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   ddr_type),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2D,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   gpio_info_valid),
+	},
+	{
+		.data_type      = QMI_DATA_LEN,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x2D,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   gpio_info_len),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = QMI_WLFW_MAX_NUM_GPIO_INFO_V01,
+		.elem_size      = sizeof(u32),
+		.array_type       = VAR_LEN_ARRAY,
+		.tlv_type       = 0x2D,
+		.offset         = offsetof(struct
+					   wlfw_host_cap_req_msg_v01,
+					   gpio_info),
+	},
 	{
 		.data_type      = QMI_EOTI,
 		.array_type       = NO_ARRAY,
@@ -5458,6 +5628,55 @@ struct qmi_elem_info wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei[] = {
 };
 EXPORT_SYMBOL(wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei);
 
+struct qmi_elem_info wlfw_subsys_restart_level_req_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct
+					   wlfw_subsys_restart_level_req_msg_v01,
+					   restart_level_type_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(u8),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct
+					   wlfw_subsys_restart_level_req_msg_v01,
+					   restart_level_type),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type       = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+EXPORT_SYMBOL(wlfw_subsys_restart_level_req_msg_v01_ei);
+
+struct qmi_elem_info wlfw_subsys_restart_level_resp_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.array_type       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(struct
+					   wlfw_subsys_restart_level_resp_msg_v01,
+					   resp),
+		.ei_array      = qmi_response_type_v01_ei,
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.array_type       = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+	},
+};
+EXPORT_SYMBOL(wlfw_subsys_restart_level_resp_msg_v01_ei);
+
 /**
  * wlfw_is_valid_dt_node_found - Check if valid device tree node present
  *

+ 57 - 2
cnss_utils/wlan_firmware_service_v01.h

@@ -9,6 +9,8 @@
 #define WLFW_SERVICE_ID_V01 0x45
 #define WLFW_SERVICE_VERS_V01 0x01
 
+#define QMI_WLFW_SUBSYS_RESTART_LEVEL_RESP_V01 0x0055
+#define QMI_WLFW_SUBSYS_RESTART_LEVEL_REQ_V01 0x0055
 #define QMI_WLFW_POWER_SAVE_RESP_V01 0x0050
 #define QMI_WLFW_CAP_REQ_V01 0x0024
 #define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
@@ -107,6 +109,7 @@
 #define QMI_WLFW_MAX_NUM_SVC_V01 24
 #define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2
 #define QMI_WLFW_MAC_ADDR_SIZE_V01 6
+#define QMI_WLFW_MAX_NUM_GPIO_INFO_V01 20
 #define QMI_WLFW_MAX_NUM_MEM_CFG_V01 2
 #define QMI_WLFW_MAX_NUM_MEM_SEG_V01 52
 #define QMI_WLFW_MAX_WFC_CALL_STATUS_DATA_SIZE_V01 256
@@ -166,6 +169,7 @@ enum wlfw_mem_type_enum_v01 {
 	QMI_WLFW_MEM_HANG_DATA_V01 = 7,
 	QMI_WLFW_MLO_GLOBAL_MEM_V01 = 8,
 	QMI_WLFW_PAGEABLE_MEM_V01 = 9,
+	QMI_WLFW_AFC_MEM_V01 = 10,
 	WLFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
 };
 
@@ -248,10 +252,28 @@ enum cnss_feature_v01 {
 	CNSS_FEATURE_MIN_VAL_V01 = INT_MIN,
 	BOOTSTRAP_CLOCK_SELECT_V01 = 0,
 	CNSS_DRV_SUPPORT_V01 = 1,
+	CNSS_WLAN_EN_SUPPORT_V01 = 2,
 	CNSS_MAX_FEATURE_V01 = 64,
 	CNSS_FEATURE_MAX_VAL_V01 = INT_MAX,
 };
 
+enum wlfw_bdf_dnld_method_v01 {
+	WLFW_BDF_DNLD_METHOD_MIN_VAL_V01 = INT_MIN,
+	WLFW_DIRECT_BDF_COPY_V01 = 0,
+	WLFW_SEND_BDF_OVER_QMI_V01 = 1,
+	WLFW_BDF_DNLD_METHOD_MAX_VAL_V01 = INT_MAX,
+};
+
+enum wlfw_gpio_info_type_v01 {
+	WLFW_GPIO_INFO_TYPE_MIN_VAL_V01 = INT_MIN,
+	WLAN_EN_GPIO_V01 = 0,
+	BT_EN_GPIO_V01 = 1,
+	HOST_SOL_GPIO_V01 = 2,
+	TARGET_SOL_GPIO_V01 = 3,
+	GPIO_TYPE_MAX_V01 = 4,
+	WLFW_GPIO_INFO_TYPE_MAX_VAL_V01 = INT_MAX,
+};
+
 #define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00)
 #define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01)
 #define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02)
@@ -530,8 +552,19 @@ struct wlfw_cap_resp_msg_v01 {
 	enum wlfw_rd_card_chain_cap_v01 rd_card_chain_cap;
 	u8 dev_mem_info_valid;
 	struct wlfw_dev_mem_info_s_v01 dev_mem_info[QMI_WLFW_MAX_DEV_MEM_NUM_V01];
+	u8 foundry_name_valid;
+	char foundry_name[QMI_WLFW_MAX_STR_LEN_V01 + 1];
+	u8 hang_data_addr_offset_valid;
+	u32 hang_data_addr_offset;
+	u8 hang_data_length_valid;
+	u16 hang_data_length;
+	u8 bdf_dnld_method_valid;
+	enum wlfw_bdf_dnld_method_v01 bdf_dnld_method;
+	u8 hwid_bitmap_valid;
+	u8 hwid_bitmap;
 };
-#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 320
+
+#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 362
 extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[];
 
 struct wlfw_bdf_download_req_msg_v01 {
@@ -803,9 +836,16 @@ struct wlfw_host_cap_req_msg_v01 {
 	u8 num_wlan_vaps;
 	u8 wake_msi_addr_valid;
 	u32 wake_msi_addr;
+	u8 wlan_enable_delay_valid;
+	u32 wlan_enable_delay;
+	u8 ddr_type_valid;
+	u32 ddr_type;
+	u8 gpio_info_valid;
+	u32 gpio_info_len;
+	u32 gpio_info[QMI_WLFW_MAX_NUM_GPIO_INFO_V01];
 };
 
-#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 389
+#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 487
 extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[];
 
 struct wlfw_host_cap_resp_msg_v01 {
@@ -1268,4 +1308,19 @@ struct wlfw_m3_dump_upload_segments_req_ind_msg_v01 {
 #define WLFW_M3_DUMP_UPLOAD_SEGMENTS_REQ_IND_MSG_V01_MAX_MSG_LEN 387
 extern struct qmi_elem_info wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei[];
 
+struct wlfw_subsys_restart_level_req_msg_v01 {
+	u8 restart_level_type_valid;
+	u8 restart_level_type;
+};
+
+#define WLFW_SUBSYS_RESTART_LEVEL_REQ_MSG_V01_MAX_MSG_LEN 4
+extern struct qmi_elem_info wlfw_subsys_restart_level_req_msg_v01_ei[];
+
+struct wlfw_subsys_restart_level_resp_msg_v01 {
+	struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_SUBSYS_RESTART_LEVEL_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct qmi_elem_info wlfw_subsys_restart_level_resp_msg_v01_ei[];
+
 #endif

+ 38 - 0
icnss2/Kconfig

@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ICNSS2
+	tristate "Platform driver for Wi-Fi Module module"
+	select CNSS_UTILS
+	help
+	  This module adds support for Q6 integrated WLAN connectivity
+	  subsystem with iWCN architecture. This module is responsible for
+	  communicating WLAN on/off control messages to FW over QMI channel.
+	  It is also responsible for handling WLAN PD restart notifications.
+
+config ICNSS2_DEBUG
+	bool "ICNSS2 Platform Driver Debug Support"
+	depends on ICNSS2
+	help
+	  Say 'Y' here to enable ICNSS driver debug support. Debug support
+	  primarily consists of logs consisting of information related to
+	  hardware register access and enabling BUG_ON for certain cases to aid
+	  the debugging.
+
+config ICNSS2_QMI
+	bool "ICNSS2 Platform Driver QMI support"
+	depends on ICNSS2
+	select CNSS_QMI_SVC
+	help
+	  Say 'Y' here to enable ICNSS QMI support. ICNSS driver will use
+	  QMI framework to communicate with WLAN FW. It will send coldboot
+	  handshake messages to WLAN FW, which includes hardware capabilities
+	  and configurations. It also send WLAN on/off control message to FW
+	  over QMI channel.
+
+config CNSS_QCA6750
+	bool "Enable ICNSS QCA6750 chipset specific changes"
+	depends on ICNSS2
+	help
+	  This enables the changes from WLAN host driver that are specific to
+	  CNSS QCA6750 chipset.
+	  These changes are needed to support the new hardware architecture
+	  for CNSS QCA6750 chipset.

+ 16 - 0
icnss2/Makefile

@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+ifeq ($(CONFIG_CNSS_OUT_OF_TREE),y)
+ccflags-y += -I$(WLAN_PLATFORM_ROOT)/cnss_utils
+ccflags-y += -I$(WLAN_PLATFORM_ROOT)/inc
+else
+ccflags-y += -I$(srctree)/drivers/net/wireless/cnss_utils/
+endif
+
+obj-$(CONFIG_ICNSS2) += icnss2.o
+
+icnss2-y := main.o
+icnss2-y += debug.o
+icnss2-y += power.o
+icnss2-y += genl.o
+icnss2-$(CONFIG_ICNSS2_QMI) += qmi.o

+ 891 - 0
icnss2/debug.c

@@ -0,0 +1,891 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ */
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include "main.h"
+#include "debug.h"
+#include "qmi.h"
+#include "power.h"
+
+void *icnss_ipc_log_context;
+void *icnss_ipc_log_long_context;
+void *icnss_ipc_log_smp2p_context;
+void *icnss_ipc_soc_wake_context;
+
+static ssize_t icnss_regwrite_write(struct file *fp,
+				    const char __user *user_buf,
+				    size_t count, loff_t *off)
+{
+	struct icnss_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+	char buf[64];
+	char *sptr, *token;
+	unsigned int len = 0;
+	uint32_t reg_offset, mem_type, reg_val;
+	const char *delim = " ";
+	int ret = 0;
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+	    !test_bit(ICNSS_POWER_ON, &priv->state))
+		return -EINVAL;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	sptr = buf;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (!sptr)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &mem_type))
+		return -EINVAL;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (!sptr)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &reg_offset))
+		return -EINVAL;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &reg_val))
+		return -EINVAL;
+
+	ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
+					       sizeof(uint32_t),
+					       (uint8_t *)&reg_val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int icnss_regwrite_show(struct seq_file *s, void *data)
+{
+	struct icnss_priv *priv = s->private;
+
+	seq_puts(s, "Usage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state))
+		seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+	return 0;
+}
+
+static int icnss_regwrite_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, icnss_regwrite_show, inode->i_private);
+}
+
+static const struct file_operations icnss_regwrite_fops = {
+	.read		= seq_read,
+	.write          = icnss_regwrite_write,
+	.open           = icnss_regwrite_open,
+	.owner          = THIS_MODULE,
+	.llseek		= seq_lseek,
+};
+
+static int icnss_regread_show(struct seq_file *s, void *data)
+{
+	struct icnss_priv *priv = s->private;
+
+	mutex_lock(&priv->dev_lock);
+	if (!priv->diag_reg_read_buf) {
+		seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
+
+		if (!test_bit(ICNSS_FW_READY, &priv->state))
+			seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+		mutex_unlock(&priv->dev_lock);
+		return 0;
+	}
+
+	seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
+		   priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
+		   priv->diag_reg_read_len);
+
+	seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
+		     priv->diag_reg_read_len, false);
+
+	priv->diag_reg_read_len = 0;
+	kfree(priv->diag_reg_read_buf);
+	priv->diag_reg_read_buf = NULL;
+	mutex_unlock(&priv->dev_lock);
+
+	return 0;
+}
+
+static int icnss_regread_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, icnss_regread_show, inode->i_private);
+}
+
+static ssize_t icnss_reg_parse(const char __user *user_buf, size_t count,
+			       struct icnss_reg_info *reg_info_ptr)
+{
+	char buf[64] = {0};
+	char *sptr = NULL, *token = NULL;
+	const char *delim = " ";
+	unsigned int len = 0;
+
+	if (user_buf == NULL)
+		return -EFAULT;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+	sptr = buf;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (!sptr)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &reg_info_ptr->mem_type))
+		return -EINVAL;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (!sptr)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &reg_info_ptr->reg_offset))
+		return -EINVAL;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+
+	if (kstrtou32(token, 0, &reg_info_ptr->data_len))
+		return -EINVAL;
+
+	if (reg_info_ptr->data_len == 0 ||
+	    reg_info_ptr->data_len > WLFW_MAX_DATA_SIZE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
+				   size_t count, loff_t *off)
+{
+	struct icnss_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+	uint8_t *reg_buf = NULL;
+	int ret = 0;
+	struct icnss_reg_info reg_info;
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+	    !test_bit(ICNSS_POWER_ON, &priv->state))
+		return -EINVAL;
+
+	ret = icnss_reg_parse(user_buf, count, &reg_info);
+	if (ret)
+		return ret;
+
+	mutex_lock(&priv->dev_lock);
+	kfree(priv->diag_reg_read_buf);
+	priv->diag_reg_read_buf = NULL;
+
+	reg_buf = kzalloc(reg_info.data_len, GFP_KERNEL);
+	if (!reg_buf) {
+		mutex_unlock(&priv->dev_lock);
+		return -ENOMEM;
+	}
+
+	ret = wlfw_athdiag_read_send_sync_msg(priv, reg_info.reg_offset,
+					      reg_info.mem_type,
+					      reg_info.data_len,
+					      reg_buf);
+	if (ret) {
+		kfree(reg_buf);
+		mutex_unlock(&priv->dev_lock);
+		return ret;
+	}
+
+	priv->diag_reg_read_addr = reg_info.reg_offset;
+	priv->diag_reg_read_mem_type = reg_info.mem_type;
+	priv->diag_reg_read_len = reg_info.data_len;
+	priv->diag_reg_read_buf = reg_buf;
+	mutex_unlock(&priv->dev_lock);
+
+	return count;
+}
+
+static const struct file_operations icnss_regread_fops = {
+	.read           = seq_read,
+	.write          = icnss_regread_write,
+	.open           = icnss_regread_open,
+	.owner          = THIS_MODULE,
+	.llseek         = seq_lseek,
+};
+
+static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
+				size_t count, loff_t *off)
+{
+	struct icnss_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+	int ret;
+	u32 val;
+
+	ret = kstrtou32_from_user(buf, count, 0, &val);
+	if (ret)
+		return ret;
+
+	if (ret == 0)
+		memset(&priv->stats, 0, sizeof(priv->stats));
+
+	return count;
+}
+
+static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
+					    struct icnss_priv *priv)
+{
+	if (priv->stats.rejuvenate_ind)  {
+		seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
+		seq_printf(s, "Number of Rejuvenations: %u\n",
+			   priv->stats.rejuvenate_ind);
+		seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
+			   priv->cause_for_rejuvenation);
+		seq_printf(s, "Requesting Sub-System: 0x%x\n",
+			   priv->requesting_sub_system);
+		seq_printf(s, "Line Number: %u\n",
+			   priv->line_number);
+		seq_printf(s, "Function Name: %s\n",
+			   priv->function_name);
+	}
+
+	return 0;
+}
+
+static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
+{
+	int i;
+
+	seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
+	seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
+		   "Free", "Enable", "Disable");
+	for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
+		seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
+			   priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
+			   priv->stats.ce_irqs[i].free,
+			   priv->stats.ce_irqs[i].enable,
+			   priv->stats.ce_irqs[i].disable);
+
+	return 0;
+}
+
+static int icnss_stats_show_capability(struct seq_file *s,
+				       struct icnss_priv *priv)
+{
+	if (test_bit(ICNSS_FW_READY, &priv->state)) {
+		seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
+		seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
+		seq_printf(s, "Chip family: 0x%x\n",
+			  priv->chip_info.chip_family);
+		seq_printf(s, "Board ID: 0x%x\n", priv->board_id);
+		seq_printf(s, "SOC Info: 0x%x\n", priv->soc_id);
+		seq_printf(s, "Firmware Version: 0x%x\n",
+			   priv->fw_version_info.fw_version);
+		seq_printf(s, "Firmware Build Timestamp: %s\n",
+			   priv->fw_version_info.fw_build_timestamp);
+		seq_printf(s, "Firmware Build ID: %s\n",
+			   priv->fw_build_id);
+	}
+
+	return 0;
+}
+
+static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
+{
+	int i;
+
+	seq_puts(s, "\n<----------------- Events stats ------------------->\n");
+	seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
+	for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
+		seq_printf(s, "%24s %16u %16u\n",
+			   icnss_driver_event_to_str(i),
+			   priv->stats.events[i].posted,
+			   priv->stats.events[i].processed);
+
+	return 0;
+}
+
+static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
+{
+	enum icnss_driver_state i;
+	int skip = 0;
+	unsigned long state;
+
+	seq_printf(s, "\nState: 0x%lx(", priv->state);
+	for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
+
+		if (!(state & 0x1))
+			continue;
+
+		if (skip++)
+			seq_puts(s, " | ");
+
+		switch (i) {
+		case ICNSS_WLFW_CONNECTED:
+			seq_puts(s, "FW CONN");
+			continue;
+		case ICNSS_POWER_ON:
+			seq_puts(s, "POWER ON");
+			continue;
+		case ICNSS_FW_READY:
+			seq_puts(s, "FW READY");
+			continue;
+		case ICNSS_DRIVER_PROBED:
+			seq_puts(s, "DRIVER PROBED");
+			continue;
+		case ICNSS_FW_TEST_MODE:
+			seq_puts(s, "FW TEST MODE");
+			continue;
+		case ICNSS_PM_SUSPEND:
+			seq_puts(s, "PM SUSPEND");
+			continue;
+		case ICNSS_PM_SUSPEND_NOIRQ:
+			seq_puts(s, "PM SUSPEND NOIRQ");
+			continue;
+		case ICNSS_SSR_REGISTERED:
+			seq_puts(s, "SSR REGISTERED");
+			continue;
+		case ICNSS_PDR_REGISTERED:
+			seq_puts(s, "PDR REGISTERED");
+			continue;
+		case ICNSS_PD_RESTART:
+			seq_puts(s, "PD RESTART");
+			continue;
+		case ICNSS_WLFW_EXISTS:
+			seq_puts(s, "WLAN FW EXISTS");
+			continue;
+		case ICNSS_SHUTDOWN_DONE:
+			seq_puts(s, "SHUTDOWN DONE");
+			continue;
+		case ICNSS_HOST_TRIGGERED_PDR:
+			seq_puts(s, "HOST TRIGGERED PDR");
+			continue;
+		case ICNSS_FW_DOWN:
+			seq_puts(s, "FW DOWN");
+			continue;
+		case ICNSS_DRIVER_UNLOADING:
+			seq_puts(s, "DRIVER UNLOADING");
+			continue;
+		case ICNSS_REJUVENATE:
+			seq_puts(s, "FW REJUVENATE");
+			continue;
+		case ICNSS_MODE_ON:
+			seq_puts(s, "MODE ON DONE");
+			continue;
+		case ICNSS_BLOCK_SHUTDOWN:
+			seq_puts(s, "BLOCK SHUTDOWN");
+			continue;
+		case ICNSS_PDR:
+			seq_puts(s, "PDR TRIGGERED");
+			continue;
+		case ICNSS_DEL_SERVER:
+			seq_puts(s, "DEL SERVER");
+			continue;
+		case ICNSS_COLD_BOOT_CAL:
+			seq_puts(s, "COLD BOOT CALIBRATION");
+			continue;
+		case ICNSS_QMI_DMS_CONNECTED:
+			seq_puts(s, "DMS_CONNECTED");
+		}
+
+		seq_printf(s, "UNKNOWN-%d", i);
+		}
+	seq_puts(s, ")\n");
+
+	return 0;
+}
+
+#define ICNSS_STATS_DUMP(_s, _priv, _x) \
+	seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
+
+static int icnss_stats_show(struct seq_file *s, void *data)
+{
+
+	struct icnss_priv *priv = s->private;
+
+	ICNSS_STATS_DUMP(s, priv, ind_register_req);
+	ICNSS_STATS_DUMP(s, priv, ind_register_resp);
+	ICNSS_STATS_DUMP(s, priv, ind_register_err);
+	ICNSS_STATS_DUMP(s, priv, cap_req);
+	ICNSS_STATS_DUMP(s, priv, cap_resp);
+	ICNSS_STATS_DUMP(s, priv, cap_err);
+	ICNSS_STATS_DUMP(s, priv, pin_connect_result);
+	ICNSS_STATS_DUMP(s, priv, cfg_req);
+	ICNSS_STATS_DUMP(s, priv, cfg_resp);
+	ICNSS_STATS_DUMP(s, priv, cfg_req_err);
+	ICNSS_STATS_DUMP(s, priv, mode_req);
+	ICNSS_STATS_DUMP(s, priv, mode_resp);
+	ICNSS_STATS_DUMP(s, priv, mode_req_err);
+	ICNSS_STATS_DUMP(s, priv, ini_req);
+	ICNSS_STATS_DUMP(s, priv, ini_resp);
+	ICNSS_STATS_DUMP(s, priv, ini_req_err);
+	ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
+	ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
+	ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
+	ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
+
+	seq_puts(s, "\n<------------------ PM stats ------------------->\n");
+	ICNSS_STATS_DUMP(s, priv, pm_suspend);
+	ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
+	ICNSS_STATS_DUMP(s, priv, pm_resume);
+	ICNSS_STATS_DUMP(s, priv, pm_resume_err);
+	ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
+	ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
+	ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
+	ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
+	ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
+	ICNSS_STATS_DUMP(s, priv, pm_relax);
+
+	if (priv->device_id != WCN6750_DEVICE_ID) {
+		seq_puts(s, "\n<------------------ MSA stats ------------------->\n");
+		ICNSS_STATS_DUMP(s, priv, msa_info_req);
+		ICNSS_STATS_DUMP(s, priv, msa_info_resp);
+		ICNSS_STATS_DUMP(s, priv, msa_info_err);
+		ICNSS_STATS_DUMP(s, priv, msa_ready_req);
+		ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
+		ICNSS_STATS_DUMP(s, priv, msa_ready_err);
+		ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
+
+		seq_puts(s, "\n<------------------ Rejuvenate stats ------------------->\n");
+		ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
+		ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
+		ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
+		ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
+		icnss_stats_show_rejuvenate_info(s, priv);
+
+	}
+
+	icnss_stats_show_irqs(s, priv);
+
+	icnss_stats_show_capability(s, priv);
+
+	icnss_stats_show_events(s, priv);
+
+	icnss_stats_show_state(s, priv);
+
+	return 0;
+}
+
+static int icnss_stats_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, icnss_stats_show, inode->i_private);
+}
+
+static const struct file_operations icnss_stats_fops = {
+	.read		= seq_read,
+	.write		= icnss_stats_write,
+	.release	= single_release,
+	.open		= icnss_stats_open,
+	.owner		= THIS_MODULE,
+	.llseek		= seq_lseek,
+};
+
+static int icnss_fw_debug_show(struct seq_file *s, void *data)
+{
+	struct icnss_priv *priv = s->private;
+
+	seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
+
+	seq_puts(s, "\nCMD: test_mode\n");
+	seq_puts(s, "  VAL: 0 (Test mode disable)\n");
+	seq_puts(s, "  VAL: 1 (WLAN FW test)\n");
+	seq_puts(s, "  VAL: 2 (CCPM test)\n");
+	seq_puts(s, "  VAL: 3 (Trigger Recovery)\n");
+	seq_puts(s, "  VAL: 4 (allow recursive recovery)\n");
+	seq_puts(s, "  VAL: 3 (Disallow recursive recovery)\n");
+
+	seq_puts(s, "\nCMD: dynamic_feature_mask\n");
+	seq_puts(s, "  VAL: (64 bit feature mask)\n");
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+		seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
+		goto out;
+	}
+
+	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+		seq_puts(s, "Machine mode is running, can't run test_mode!\n");
+		goto out;
+	}
+
+	if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+		seq_puts(s, "test_mode is running, can't run test_mode!\n");
+		goto out;
+	}
+
+out:
+	seq_puts(s, "\n");
+	return 0;
+}
+
+static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
+{
+	int ret;
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+		icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
+			     priv->state);
+			ret = -ENODEV;
+			goto out;
+	}
+
+	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+		icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
+			     priv->state);
+			ret = -EINVAL;
+			goto out;
+	}
+
+	if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+		icnss_pr_err("Test mode not started, state: 0x%lx\n",
+			     priv->state);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
+
+	ret = icnss_hw_power_off(priv);
+
+	clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+out:
+	return ret;
+}
+
+static int icnss_test_mode_fw_test(struct icnss_priv *priv,
+				   enum icnss_driver_mode mode)
+{
+	int ret;
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+		icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
+			     priv->state);
+			ret = -ENODEV;
+			goto out;
+	}
+
+	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+		icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
+			     priv->state);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
+		icnss_pr_err("Test mode already started, state: 0x%lx\n",
+			     priv->state);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = icnss_hw_power_on(priv);
+	if (ret)
+		goto out;
+
+	set_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+	ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
+	if (ret)
+		goto power_off;
+
+	return 0;
+
+power_off:
+	icnss_hw_power_off(priv);
+	clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
+
+out:
+	return ret;
+}
+
+
+static ssize_t icnss_fw_debug_write(struct file *fp,
+				    const char __user *user_buf,
+				    size_t count, loff_t *off)
+{
+	struct icnss_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+	char buf[64];
+	char *sptr, *token;
+	unsigned int len = 0;
+	char *cmd;
+	uint64_t val;
+	const char *delim = " ";
+	int ret = 0;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EINVAL;
+
+	buf[len] = '\0';
+	sptr = buf;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (!sptr)
+		return -EINVAL;
+	cmd = token;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (kstrtou64(token, 0, &val))
+		return -EINVAL;
+
+	if (strcmp(cmd, "test_mode") == 0) {
+		switch (val) {
+		case 0:
+			ret = icnss_test_mode_fw_test_off(priv);
+			break;
+		case 1:
+			ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
+			break;
+		case 2:
+			ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
+			break;
+		case 3:
+			ret = icnss_trigger_recovery(&priv->pdev->dev);
+			break;
+		case 4:
+			icnss_allow_recursive_recovery(&priv->pdev->dev);
+			break;
+		case 5:
+			icnss_disallow_recursive_recovery(&priv->pdev->dev);
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
+		ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
+	} else {
+		return -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int icnss_fw_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, icnss_fw_debug_show, inode->i_private);
+}
+
+static const struct file_operations icnss_fw_debug_fops = {
+	.read		= seq_read,
+	.write		= icnss_fw_debug_write,
+	.release	= single_release,
+	.open		= icnss_fw_debug_open,
+	.owner		= THIS_MODULE,
+	.llseek		= seq_lseek,
+};
+
+static ssize_t icnss_control_params_debug_write(struct file *fp,
+					       const char __user *user_buf,
+					       size_t count, loff_t *off)
+{
+	struct icnss_priv *priv =
+		((struct seq_file *)fp->private_data)->private;
+
+	char buf[64];
+	char *sptr, *token;
+	char *cmd;
+	u32 val;
+	unsigned int len = 0;
+	const char *delim = " ";
+
+	if (!priv)
+		return -ENODEV;
+
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EINVAL;
+
+	buf[len] = '\0';
+	sptr = buf;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (!sptr)
+		return -EINVAL;
+	cmd = token;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (kstrtou32(token, 0, &val))
+		return -EINVAL;
+
+	if (strcmp(cmd, "qmi_timeout") == 0)
+		priv->ctrl_params.qmi_timeout = msecs_to_jiffies(val);
+	else
+		return -EINVAL;
+
+	return count;
+}
+
+static int icnss_control_params_debug_show(struct seq_file *s, void *data)
+{
+	struct icnss_priv *priv = s->private;
+
+	seq_puts(s, "\nUsage: echo <params_name> <value> > <debugfs>/icnss/control_params\n");
+	seq_puts(s, "<params_name> can be from below:\n");
+	seq_puts(s, "qmi_timeout: Timeout for QMI message in milliseconds\n");
+
+	seq_puts(s, "\nCurrent value:\n");
+
+	seq_printf(s, "qmi_timeout: %u\n", jiffies_to_msecs(priv->ctrl_params.qmi_timeout));
+
+	return 0;
+}
+
+static int icnss_control_params_debug_open(struct inode *inode,
+					  struct file *file)
+{
+	return single_open(file, icnss_control_params_debug_show,
+			   inode->i_private);
+}
+
+static const struct file_operations icnss_control_params_debug_fops = {
+	.read		= seq_read,
+	.write		= icnss_control_params_debug_write,
+	.release	= single_release,
+	.open		= icnss_control_params_debug_open,
+	.owner		= THIS_MODULE,
+	.llseek		= seq_lseek,
+};
+
+#ifdef CONFIG_ICNSS2_DEBUG
+int icnss_debugfs_create(struct icnss_priv *priv)
+{
+	int ret = 0;
+	struct dentry *root_dentry;
+
+	root_dentry = debugfs_create_dir("icnss", NULL);
+
+	if (IS_ERR(root_dentry)) {
+		ret = PTR_ERR(root_dentry);
+		icnss_pr_err("Unable to create debugfs %d\n", ret);
+		goto out;
+		}
+
+		priv->root_dentry = root_dentry;
+
+		debugfs_create_file("fw_debug", 0600, root_dentry, priv,
+					&icnss_fw_debug_fops);
+		debugfs_create_file("stats", 0600, root_dentry, priv,
+						&icnss_stats_fops);
+		debugfs_create_file("reg_read", 0600, root_dentry, priv,
+						&icnss_regread_fops);
+		debugfs_create_file("reg_write", 0600, root_dentry, priv,
+						&icnss_regwrite_fops);
+		debugfs_create_file("control_params", 0600, root_dentry, priv,
+					&icnss_control_params_debug_fops);
+out:
+		return ret;
+}
+#else
+int icnss_debugfs_create(struct icnss_priv *priv)
+{
+	int ret = 0;
+	struct dentry *root_dentry;
+
+	root_dentry = debugfs_create_dir("icnss", NULL);
+
+	if (IS_ERR(root_dentry)) {
+		ret = PTR_ERR(root_dentry);
+		icnss_pr_err("Unable to create debugfs %d\n", ret);
+		return ret;
+	}
+
+	priv->root_dentry = root_dentry;
+
+	debugfs_create_file("stats", 0600, root_dentry, priv,
+							     &icnss_stats_fops);
+	return 0;
+}
+#endif
+
+void icnss_debugfs_destroy(struct icnss_priv *priv)
+{
+	debugfs_remove_recursive(priv->root_dentry);
+}
+
+void icnss_debug_init(void)
+{
+	icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
+						       "icnss", 0);
+	if (!icnss_ipc_log_context)
+		icnss_pr_err("Unable to create log context\n");
+
+	icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
+						       "icnss_long", 0);
+	if (!icnss_ipc_log_long_context)
+		icnss_pr_err("Unable to create log long context\n");
+
+	icnss_ipc_log_smp2p_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
+						       "icnss_smp2p", 0);
+	if (!icnss_ipc_log_smp2p_context)
+		icnss_pr_err("Unable to create log smp2p context\n");
+
+	icnss_ipc_soc_wake_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
+						       "icnss_soc_wake", 0);
+	if (!icnss_ipc_soc_wake_context)
+		icnss_pr_err("Unable to create log soc_wake context\n");
+
+}
+
+void icnss_debug_deinit(void)
+{
+	if (icnss_ipc_log_context) {
+		ipc_log_context_destroy(icnss_ipc_log_context);
+		icnss_ipc_log_context = NULL;
+	}
+
+	if (icnss_ipc_log_long_context) {
+		ipc_log_context_destroy(icnss_ipc_log_long_context);
+		icnss_ipc_log_long_context = NULL;
+	}
+
+	if (icnss_ipc_log_smp2p_context) {
+		ipc_log_context_destroy(icnss_ipc_log_smp2p_context);
+		icnss_ipc_log_smp2p_context = NULL;
+	}
+
+	if (icnss_ipc_soc_wake_context) {
+		ipc_log_context_destroy(icnss_ipc_soc_wake_context);
+		icnss_ipc_soc_wake_context = NULL;
+	}
+}

+ 108 - 0
icnss2/debug.h

@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ICNSS_DEBUG_H
+#define _ICNSS_DEBUG_H
+
+#include <linux/ipc_logging.h>
+#include <linux/printk.h>
+
+#define NUM_LOG_PAGES			10
+#define NUM_LOG_LONG_PAGES		4
+
+extern void *icnss_ipc_log_context;
+extern void *icnss_ipc_log_long_context;
+extern void *icnss_ipc_log_smp2p_context;
+extern void *icnss_ipc_soc_wake_context;
+
+#if IS_ENABLED(CONFIG_IPC_LOGGING)
+#define icnss_ipc_log_string(_x...)                                     \
+	ipc_log_string(icnss_ipc_log_context, _x)
+
+#define icnss_ipc_log_long_string(_x...)                                \
+	ipc_log_string(icnss_ipc_log_long_context, _x)
+
+#define icnss_ipc_log_smp2p_string(_x...)                                \
+	ipc_log_string(icnss_ipc_log_smp2p_context, _x)
+
+#define icnss_ipc_soc_wake_string(_x...)                                \
+	ipc_log_string(icnss_ipc_soc_wake_context, _x)
+#else
+#define icnss_ipc_log_string(_x...)
+
+#define icnss_ipc_log_long_string(_x...)
+
+#define icnss_ipc_log_smp2p_string(_x...)
+
+#define icnss_ipc_soc_wake_string(_x...)
+#endif
+
+#define icnss_pr_err(_fmt, ...) do {                                    \
+	printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__);             \
+	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",                     \
+			     ##__VA_ARGS__);                            \
+	} while (0)
+
+#define icnss_pr_warn(_fmt, ...) do {                                   \
+	printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__);         \
+	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",                     \
+			     ##__VA_ARGS__);                            \
+	} while (0)
+
+#define icnss_pr_info(_fmt, ...) do {                                   \
+	printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__);            \
+	icnss_ipc_log_string("%s" pr_fmt(_fmt), "",                     \
+			     ##__VA_ARGS__);                            \
+	} while (0)
+
+#define icnss_pr_dbg(_fmt, ...) do {                                    \
+	pr_debug(_fmt, ##__VA_ARGS__);                                  \
+	icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__);              \
+	} while (0)
+
+#define icnss_pr_vdbg(_fmt, ...) do {                                   \
+	pr_debug(_fmt, ##__VA_ARGS__);                                  \
+	icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__);         \
+	} while (0)
+
+#define icnss_pr_smp2p(_fmt, ...) do {                                  \
+	pr_debug(_fmt, ##__VA_ARGS__);                                  \
+	icnss_ipc_log_smp2p_string(pr_fmt(_fmt), ##__VA_ARGS__);        \
+	} while (0)
+
+#define icnss_pr_soc_wake(_fmt, ...) do {                               \
+	pr_debug(_fmt, ##__VA_ARGS__);                                  \
+	icnss_ipc_soc_wake_string(pr_fmt(_fmt), ##__VA_ARGS__);         \
+	} while (0)
+
+#ifdef CONFIG_ICNSS2_DEBUG
+#define ICNSS_ASSERT(_condition) do {                                   \
+		if (!(_condition)) {                                    \
+			icnss_pr_err("ASSERT at line %d\n", __LINE__);  \
+			BUG();                                          \
+		}                                                       \
+	} while (0)
+#else
+#define ICNSS_ASSERT(_condition) do { } while (0)
+#endif
+
+#define icnss_fatal_err(_fmt, ...)                                      \
+	icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__)
+
+enum icnss_debug_quirks {
+	HW_ALWAYS_ON,
+	HW_DEBUG_ENABLE,
+	SKIP_QMI,
+	RECOVERY_DISABLE,
+	SSR_ONLY,
+	PDR_ONLY,
+	FW_REJUVENATE_ENABLE,
+};
+
+void icnss_debug_init(void);
+void icnss_debug_deinit(void);
+int icnss_debugfs_create(struct icnss_priv *priv);
+void icnss_debugfs_destroy(struct icnss_priv *priv);
+#endif /* _ICNSS_DEBUG_H */

+ 213 - 0
icnss2/genl.c

@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+
+#define pr_fmt(fmt) "cnss_genl: " fmt
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "main.h"
+#include "debug.h"
+
+#define ICNSS_GENL_FAMILY_NAME "cnss-genl"
+#define ICNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp"
+#define ICNSS_GENL_VERSION 1
+#define ICNSS_GENL_DATA_LEN_MAX (15 * 1024)
+#define ICNSS_GENL_STR_LEN_MAX 16
+
+enum {
+	ICNSS_GENL_ATTR_MSG_UNSPEC,
+	ICNSS_GENL_ATTR_MSG_TYPE,
+	ICNSS_GENL_ATTR_MSG_FILE_NAME,
+	ICNSS_GENL_ATTR_MSG_TOTAL_SIZE,
+	ICNSS_GENL_ATTR_MSG_SEG_ID,
+	ICNSS_GENL_ATTR_MSG_END,
+	ICNSS_GENL_ATTR_MSG_DATA_LEN,
+	ICNSS_GENL_ATTR_MSG_DATA,
+	__ICNSS_GENL_ATTR_MAX,
+};
+
+#define ICNSS_GENL_ATTR_MAX (__ICNSS_GENL_ATTR_MAX - 1)
+
+enum {
+	ICNSS_GENL_CMD_UNSPEC,
+	ICNSS_GENL_CMD_MSG,
+	__ICNSS_GENL_CMD_MAX,
+};
+
+#define ICNSS_GENL_CMD_MAX (__ICNSS_GENL_CMD_MAX - 1)
+
+static struct nla_policy icnss_genl_msg_policy[ICNSS_GENL_ATTR_MAX + 1] = {
+	[ICNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 },
+	[ICNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING,
+					   .len = ICNSS_GENL_STR_LEN_MAX },
+	[ICNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 },
+	[ICNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 },
+	[ICNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 },
+	[ICNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 },
+	[ICNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY,
+				      .len = ICNSS_GENL_DATA_LEN_MAX },
+};
+
+static int icnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info)
+{
+	return 0;
+}
+
+static struct genl_ops icnss_genl_ops[] = {
+	{
+		.cmd = ICNSS_GENL_CMD_MSG,
+		.doit = icnss_genl_process_msg,
+	},
+};
+
+static struct genl_multicast_group icnss_genl_mcast_grp[] = {
+	{
+		.name = ICNSS_GENL_MCAST_GROUP_NAME,
+	},
+};
+
+static struct genl_family icnss_genl_family = {
+	.id = 0,
+	.hdrsize = 0,
+	.name = ICNSS_GENL_FAMILY_NAME,
+	.version = ICNSS_GENL_VERSION,
+	.maxattr = ICNSS_GENL_ATTR_MAX,
+	.policy = icnss_genl_msg_policy,
+	.module = THIS_MODULE,
+	.ops = icnss_genl_ops,
+	.n_ops = ARRAY_SIZE(icnss_genl_ops),
+	.mcgrps = icnss_genl_mcast_grp,
+	.n_mcgrps = ARRAY_SIZE(icnss_genl_mcast_grp),
+};
+
+static int icnss_genl_send_data(u8 type, char *file_name, u32 total_size,
+				u32 seg_id, u8 end, u32 data_len, u8 *msg_buff)
+{
+	struct sk_buff *skb = NULL;
+	void *msg_header = NULL;
+	int ret = 0;
+	char filename[ICNSS_GENL_STR_LEN_MAX + 1];
+
+	icnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n",
+		     type, file_name, total_size, seg_id, end, data_len);
+
+	if (!file_name)
+		strlcpy(filename, "default", sizeof(filename));
+	else
+		strlcpy(filename, file_name, sizeof(filename));
+
+	skb = genlmsg_new(NLMSG_HDRLEN +
+			  nla_total_size(sizeof(type)) +
+			  nla_total_size(strlen(filename) + 1) +
+			  nla_total_size(sizeof(total_size)) +
+			  nla_total_size(sizeof(seg_id)) +
+			  nla_total_size(sizeof(end)) +
+			  nla_total_size(sizeof(data_len)) +
+			  nla_total_size(data_len), GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	msg_header = genlmsg_put(skb, 0, 0,
+				 &icnss_genl_family, 0,
+				 ICNSS_GENL_CMD_MSG);
+	if (!msg_header) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_TYPE, type);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put_string(skb, ICNSS_GENL_ATTR_MSG_FILE_NAME, filename);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_SEG_ID, seg_id);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_END, end);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_DATA_LEN, data_len);
+	if (ret < 0)
+		goto fail;
+	ret = nla_put(skb, ICNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff);
+	if (ret < 0)
+		goto fail;
+
+	genlmsg_end(skb, msg_header);
+	ret = genlmsg_multicast(&icnss_genl_family, skb, 0, 0, GFP_KERNEL);
+	if (ret < 0)
+		icnss_pr_err("Fail to send genl msg: %d\n", ret);
+
+	return ret;
+fail:
+	icnss_pr_err("Fail to generate genl msg: %d\n", ret);
+	if (skb)
+		nlmsg_free(skb);
+	return ret;
+}
+
+int icnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size)
+{
+	int ret = 0;
+	u8 *msg_buff = buff;
+	u32 remaining = total_size;
+	u32 seg_id = 0;
+	u32 data_len = 0;
+	u8 end = 0;
+	u8 retry;
+
+	icnss_pr_dbg("type: %u, total_size: %x\n", type, total_size);
+
+	while (remaining) {
+		if (remaining > ICNSS_GENL_DATA_LEN_MAX) {
+			data_len = ICNSS_GENL_DATA_LEN_MAX;
+		} else {
+			data_len = remaining;
+			end = 1;
+		}
+
+		for (retry = 0; retry < 2; retry++) {
+			ret = icnss_genl_send_data(type, file_name, total_size,
+						   seg_id, end, data_len,
+						   msg_buff);
+			if (ret >= 0)
+				break;
+			msleep(100);
+		}
+
+		if (ret < 0) {
+			icnss_pr_err("fail to send genl data, ret %d\n", ret);
+			return ret;
+		}
+
+		remaining -= data_len;
+		msg_buff += data_len;
+		seg_id++;
+	}
+
+	return ret;
+}
+
+int icnss_genl_init(void)
+{
+	int ret = 0;
+
+	ret = genl_register_family(&icnss_genl_family);
+	if (ret != 0)
+		icnss_pr_err("genl_register_family fail: %d\n", ret);
+
+	return ret;
+}
+
+void icnss_genl_exit(void)
+{
+	genl_unregister_family(&icnss_genl_family);
+}

+ 17 - 0
icnss2/genl.h

@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
+
+#ifndef __ICNSS_GENL_H__
+#define __ICNSS_GENL_H__
+
+enum icnss_genl_msg_type {
+	ICNSS_GENL_MSG_TYPE_UNSPEC,
+	ICNSS_GENL_MSG_TYPE_QDSS,
+};
+
+int icnss_genl_init(void);
+void icnss_genl_exit(void);
+int icnss_genl_send_msg(void *buff, u8 type,
+			char *file_name, u32 total_size);
+
+#endif

+ 4559 - 0
icnss2/main.c

@@ -0,0 +1,4559 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2020, 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "icnss2: " fmt
+
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/iommu.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/thread_info.h>
+#include <linux/uaccess.h>
+#include <linux/adc-tm-clients.h>
+#include <linux/iio/consumer.h>
+#include <linux/etherdevice.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <soc/qcom/memory_dump.h>
+#include <soc/qcom/secure_buffer.h>
+#include <soc/qcom/socinfo.h>
+#include <soc/qcom/qcom_ramdump.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
+#include <linux/remoteproc.h>
+#include <linux/remoteproc/qcom_rproc.h>
+#include <linux/soc/qcom/pdr.h>
+#include <linux/remoteproc.h>
+#include <trace/hooks/remoteproc.h>
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+#include "power.h"
+#include "genl.h"
+
+#define MAX_PROP_SIZE			32
+#define NUM_LOG_PAGES			10
+#define NUM_LOG_LONG_PAGES		4
+#define ICNSS_MAGIC			0x5abc5abc
+
+#define ICNSS_WLAN_SERVICE_NAME					"wlan/fw"
+#define ICNSS_WLANPD_NAME					"msm/modem/wlan_pd"
+#define ICNSS_DEFAULT_FEATURE_MASK 0x01
+
+#define ICNSS_M3_SEGMENT(segment)		"wcnss_"segment
+#define ICNSS_M3_SEGMENT_PHYAREG		"phyareg"
+#define ICNSS_M3_SEGMENT_PHYA			"phydbg"
+#define ICNSS_M3_SEGMENT_WMACREG		"wmac0reg"
+#define ICNSS_M3_SEGMENT_WCSSDBG		"WCSSDBG"
+#define ICNSS_M3_SEGMENT_PHYAM3			"PHYAPDMEM"
+
+#define ICNSS_QUIRKS_DEFAULT		BIT(FW_REJUVENATE_ENABLE)
+#define ICNSS_MAX_PROBE_CNT		2
+
+#define ICNSS_BDF_TYPE_DEFAULT         ICNSS_BDF_ELF
+
+#define PROBE_TIMEOUT                 15000
+#define SMP2P_SOC_WAKE_TIMEOUT        500
+#ifdef CONFIG_ICNSS2_DEBUG
+static unsigned long qmi_timeout = 3000;
+module_param(qmi_timeout, ulong, 0600);
+#define WLFW_TIMEOUT                    msecs_to_jiffies(qmi_timeout)
+#else
+#define WLFW_TIMEOUT                    msecs_to_jiffies(3000)
+#endif
+
+static struct icnss_priv *penv;
+static struct work_struct wpss_loader;
+uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK;
+
+#define ICNSS_EVENT_PENDING			2989
+
+#define ICNSS_EVENT_SYNC			BIT(0)
+#define ICNSS_EVENT_UNINTERRUPTIBLE		BIT(1)
+#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE	(ICNSS_EVENT_UNINTERRUPTIBLE | \
+						 ICNSS_EVENT_SYNC)
+#define ICNSS_DMS_QMI_CONNECTION_WAIT_MS 50
+#define ICNSS_DMS_QMI_CONNECTION_WAIT_RETRY 200
+
+#define SMP2P_GET_MAX_RETRY		4
+#define SMP2P_GET_RETRY_DELAY_MS	500
+
+#define RAMDUMP_NUM_DEVICES		256
+#define ICNSS_RAMDUMP_NAME		"icnss_ramdump"
+
+#define ICNSS_RPROC_LEN			10
+static DEFINE_IDA(rd_minor_id);
+
+enum icnss_pdr_cause_index {
+	ICNSS_FW_CRASH,
+	ICNSS_ROOT_PD_CRASH,
+	ICNSS_ROOT_PD_SHUTDOWN,
+	ICNSS_HOST_ERROR,
+};
+
+static const char * const icnss_pdr_cause[] = {
+	[ICNSS_FW_CRASH] = "FW crash",
+	[ICNSS_ROOT_PD_CRASH] = "Root PD crashed",
+	[ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown",
+	[ICNSS_HOST_ERROR] = "Host error",
+};
+
+static void icnss_set_plat_priv(struct icnss_priv *priv)
+{
+	penv = priv;
+}
+
+static struct icnss_priv *icnss_get_plat_priv()
+{
+	return penv;
+}
+
+static ssize_t icnss_sysfs_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct icnss_priv *priv = icnss_get_plat_priv();
+
+	atomic_set(&priv->is_shutdown, true);
+	icnss_pr_dbg("Received shutdown indication");
+	return count;
+}
+
+static struct kobj_attribute icnss_sysfs_attribute =
+__ATTR(shutdown, 0660, NULL, icnss_sysfs_store);
+
+static void icnss_pm_stay_awake(struct icnss_priv *priv)
+{
+	if (atomic_inc_return(&priv->pm_count) != 1)
+		return;
+
+	icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
+		     atomic_read(&priv->pm_count));
+
+	pm_stay_awake(&priv->pdev->dev);
+
+	priv->stats.pm_stay_awake++;
+}
+
+static void icnss_pm_relax(struct icnss_priv *priv)
+{
+	int r = atomic_dec_return(&priv->pm_count);
+
+	WARN_ON(r < 0);
+
+	if (r != 0)
+		return;
+
+	icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
+		     atomic_read(&priv->pm_count));
+
+	pm_relax(&priv->pdev->dev);
+	priv->stats.pm_relax++;
+}
+
+char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
+{
+	switch (type) {
+	case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
+		return "SERVER_ARRIVE";
+	case ICNSS_DRIVER_EVENT_SERVER_EXIT:
+		return "SERVER_EXIT";
+	case ICNSS_DRIVER_EVENT_FW_READY_IND:
+		return "FW_READY";
+	case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
+		return "REGISTER_DRIVER";
+	case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
+		return "UNREGISTER_DRIVER";
+	case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
+		return "PD_SERVICE_DOWN";
+	case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+		return "FW_EARLY_CRASH_IND";
+	case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+		return "IDLE_SHUTDOWN";
+	case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+		return "IDLE_RESTART";
+	case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
+		return "FW_INIT_DONE";
+	case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM:
+		return "QDSS_TRACE_REQ_MEM";
+	case ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE:
+		return "QDSS_TRACE_SAVE";
+	case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
+		return "QDSS_TRACE_FREE";
+	case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ:
+		return "M3_DUMP_UPLOAD";
+	case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA:
+		return "QDSS_TRACE_REQ_DATA";
+	case ICNSS_DRIVER_EVENT_SUBSYS_RESTART_LEVEL:
+		return "SUBSYS_RESTART_LEVEL";
+	case ICNSS_DRIVER_EVENT_MAX:
+		return "EVENT_MAX";
+	}
+
+	return "UNKNOWN";
+};
+
+char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type)
+{
+	switch (type) {
+	case ICNSS_SOC_WAKE_REQUEST_EVENT:
+		return "SOC_WAKE_REQUEST";
+	case ICNSS_SOC_WAKE_RELEASE_EVENT:
+		return "SOC_WAKE_RELEASE";
+	case ICNSS_SOC_WAKE_EVENT_MAX:
+		return "SOC_EVENT_MAX";
+	}
+
+	return "UNKNOWN";
+};
+
+int icnss_driver_event_post(struct icnss_priv *priv,
+			    enum icnss_driver_event_type type,
+			    u32 flags, void *data)
+{
+	struct icnss_driver_event *event;
+	unsigned long irq_flags;
+	int gfp = GFP_KERNEL;
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
+		     icnss_driver_event_to_str(type), type, current->comm,
+		     flags, priv->state);
+
+	if (type >= ICNSS_DRIVER_EVENT_MAX) {
+		icnss_pr_err("Invalid Event type: %d, can't post", type);
+		return -EINVAL;
+	}
+
+	if (in_interrupt() || irqs_disabled())
+		gfp = GFP_ATOMIC;
+
+	event = kzalloc(sizeof(*event), gfp);
+	if (event == NULL)
+		return -ENOMEM;
+
+	icnss_pm_stay_awake(priv);
+
+	event->type = type;
+	event->data = data;
+	init_completion(&event->complete);
+	event->ret = ICNSS_EVENT_PENDING;
+	event->sync = !!(flags & ICNSS_EVENT_SYNC);
+
+	spin_lock_irqsave(&priv->event_lock, irq_flags);
+	list_add_tail(&event->list, &priv->event_list);
+	spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+
+	priv->stats.events[type].posted++;
+	queue_work(priv->event_wq, &priv->event_work);
+
+	if (!(flags & ICNSS_EVENT_SYNC))
+		goto out;
+
+	if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
+		wait_for_completion(&event->complete);
+	else
+		ret = wait_for_completion_interruptible(&event->complete);
+
+	icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
+		     icnss_driver_event_to_str(type), type, priv->state, ret,
+		     event->ret);
+
+	spin_lock_irqsave(&priv->event_lock, irq_flags);
+	if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
+		event->sync = false;
+		spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+		ret = -EINTR;
+		goto out;
+	}
+	spin_unlock_irqrestore(&priv->event_lock, irq_flags);
+
+	ret = event->ret;
+	kfree(event);
+
+out:
+	icnss_pm_relax(priv);
+	return ret;
+}
+
+int icnss_soc_wake_event_post(struct icnss_priv *priv,
+			      enum icnss_soc_wake_event_type type,
+			      u32 flags, void *data)
+{
+	struct icnss_soc_wake_event *event;
+	unsigned long irq_flags;
+	int gfp = GFP_KERNEL;
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_soc_wake("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n",
+			  icnss_soc_wake_event_to_str(type),
+			  type, current->comm, flags, priv->state);
+
+	if (type >= ICNSS_SOC_WAKE_EVENT_MAX) {
+		icnss_pr_err("Invalid Event type: %d, can't post", type);
+		return -EINVAL;
+	}
+
+	if (in_interrupt() || irqs_disabled())
+		gfp = GFP_ATOMIC;
+
+	event = kzalloc(sizeof(*event), gfp);
+	if (!event)
+		return -ENOMEM;
+
+	icnss_pm_stay_awake(priv);
+
+	event->type = type;
+	event->data = data;
+	init_completion(&event->complete);
+	event->ret = ICNSS_EVENT_PENDING;
+	event->sync = !!(flags & ICNSS_EVENT_SYNC);
+
+	spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags);
+	list_add_tail(&event->list, &priv->soc_wake_msg_list);
+	spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags);
+
+	priv->stats.soc_wake_events[type].posted++;
+	queue_work(priv->soc_wake_wq, &priv->soc_wake_msg_work);
+
+	if (!(flags & ICNSS_EVENT_SYNC))
+		goto out;
+
+	if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
+		wait_for_completion(&event->complete);
+	else
+		ret = wait_for_completion_interruptible(&event->complete);
+
+	icnss_pr_soc_wake("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
+			  icnss_soc_wake_event_to_str(type),
+			  type, priv->state, ret, event->ret);
+
+	spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags);
+	if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
+		event->sync = false;
+		spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags);
+		ret = -EINTR;
+		goto out;
+	}
+	spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags);
+
+	ret = event->ret;
+	kfree(event);
+
+out:
+	icnss_pm_relax(priv);
+	return ret;
+}
+
+bool icnss_is_fw_ready(void)
+{
+	if (!penv)
+		return false;
+	else
+		return test_bit(ICNSS_FW_READY, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_fw_ready);
+
+void icnss_block_shutdown(bool status)
+{
+	if (!penv)
+		return;
+
+	if (status) {
+		set_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state);
+		reinit_completion(&penv->unblock_shutdown);
+	} else {
+		clear_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state);
+		complete(&penv->unblock_shutdown);
+	}
+}
+EXPORT_SYMBOL(icnss_block_shutdown);
+
+bool icnss_is_fw_down(void)
+{
+
+	struct icnss_priv *priv = icnss_get_plat_priv();
+
+	if (!priv)
+		return false;
+
+	return test_bit(ICNSS_FW_DOWN, &priv->state) ||
+		test_bit(ICNSS_PD_RESTART, &priv->state) ||
+		test_bit(ICNSS_REJUVENATE, &priv->state);
+}
+EXPORT_SYMBOL(icnss_is_fw_down);
+
+bool icnss_is_rejuvenate(void)
+{
+	if (!penv)
+		return false;
+	else
+		return test_bit(ICNSS_REJUVENATE, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_rejuvenate);
+
+bool icnss_is_pdr(void)
+{
+	if (!penv)
+		return false;
+	else
+		return test_bit(ICNSS_PDR, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_pdr);
+
+static int icnss_send_smp2p(struct icnss_priv *priv,
+			    enum icnss_smp2p_msg_id msg_id,
+			    enum smp2p_out_entry smp2p_entry)
+{
+	unsigned int value = 0;
+	int ret;
+
+	if (IS_ERR(priv->smp2p_info[smp2p_entry].smem_state))
+		return -EINVAL;
+
+	/* No Need to check FW_DOWN for ICNSS_RESET_MSG */
+	if (msg_id == ICNSS_RESET_MSG) {
+		priv->smp2p_info[smp2p_entry].seq = 0;
+		ret = qcom_smem_state_update_bits(
+				priv->smp2p_info[smp2p_entry].smem_state,
+				ICNSS_SMEM_VALUE_MASK,
+				0);
+		if (ret)
+			icnss_pr_err("Error in SMP2P sent. ret: %d, %s\n",
+				     ret, icnss_smp2p_str[smp2p_entry]);
+
+		return ret;
+	}
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -ENODEV;
+
+	value |= priv->smp2p_info[smp2p_entry].seq++;
+	value <<= ICNSS_SMEM_SEQ_NO_POS;
+	value |= msg_id;
+
+	icnss_pr_smp2p("Sending SMP2P value: 0x%X\n", value);
+
+	if (msg_id == ICNSS_SOC_WAKE_REQ || msg_id == ICNSS_SOC_WAKE_REL)
+		reinit_completion(&penv->smp2p_soc_wake_wait);
+
+	ret = qcom_smem_state_update_bits(
+			priv->smp2p_info[smp2p_entry].smem_state,
+			ICNSS_SMEM_VALUE_MASK,
+			value);
+	if (ret) {
+		icnss_pr_smp2p("Error in SMP2P send ret: %d, %s\n", ret,
+			       icnss_smp2p_str[smp2p_entry]);
+	} else {
+		if (msg_id == ICNSS_SOC_WAKE_REQ ||
+		    msg_id == ICNSS_SOC_WAKE_REL) {
+			if (!wait_for_completion_timeout(
+					&priv->smp2p_soc_wake_wait,
+					msecs_to_jiffies(SMP2P_SOC_WAKE_TIMEOUT))) {
+				icnss_pr_err("SMP2P Soc Wake timeout msg %d, %s\n", msg_id,
+					     icnss_smp2p_str[smp2p_entry]);
+				ICNSS_ASSERT(0);
+			}
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
+{
+	struct icnss_priv *priv = ctx;
+
+	if (priv)
+		priv->force_err_fatal = true;
+
+	icnss_pr_err("Received force error fatal request from FW\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
+{
+	struct icnss_priv *priv = ctx;
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+	icnss_pr_err("Received early crash indication from FW\n");
+
+	if (priv) {
+		set_bit(ICNSS_FW_DOWN, &priv->state);
+		icnss_ignore_fw_timeout(true);
+
+		if (test_bit(ICNSS_FW_READY, &priv->state)) {
+			clear_bit(ICNSS_FW_READY, &priv->state);
+			fw_down_data.crashed = true;
+			icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+						 &fw_down_data);
+		}
+	}
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+				0, NULL);
+
+	return IRQ_HANDLED;
+}
+
+static void register_fw_error_notifications(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct device_node *dev_node;
+	int irq = 0, ret = 0;
+
+	if (!priv)
+		return;
+
+	dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in");
+	if (!dev_node) {
+		icnss_pr_err("Failed to get smp2p node for force-fatal-error\n");
+		return;
+	}
+
+	icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name);
+
+	if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) {
+		ret = irq = of_irq_get_byname(dev_node,
+					      "qcom,smp2p-force-fatal-error");
+		if (ret < 0) {
+			icnss_pr_err("Unable to get force-fatal-error irq %d\n",
+				     irq);
+			return;
+		}
+	}
+
+	ret = devm_request_threaded_irq(dev, irq, NULL, fw_error_fatal_handler,
+					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+					"wlanfw-err", priv);
+	if (ret < 0) {
+		icnss_pr_err("Unable to register for error fatal IRQ handler %d ret = %d",
+			     irq, ret);
+		return;
+	}
+	icnss_pr_dbg("FW force error fatal handler registered irq = %d\n", irq);
+	priv->fw_error_fatal_irq = irq;
+}
+
+static void register_early_crash_notifications(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct device_node *dev_node;
+	int irq = 0, ret = 0;
+
+	if (!priv)
+		return;
+
+	dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_1_in");
+	if (!dev_node) {
+		icnss_pr_err("Failed to get smp2p node for early-crash-ind\n");
+		return;
+	}
+
+	icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name);
+
+	if (strcmp("qcom,smp2p_map_wlan_1_in", dev_node->name) == 0) {
+		ret = irq = of_irq_get_byname(dev_node,
+					      "qcom,smp2p-early-crash-ind");
+		if (ret < 0) {
+			icnss_pr_err("Unable to get early-crash-ind irq %d\n",
+				     irq);
+			return;
+		}
+	}
+
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+					fw_crash_indication_handler,
+					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+					"wlanfw-early-crash-ind", priv);
+	if (ret < 0) {
+		icnss_pr_err("Unable to register for early crash indication IRQ handler %d ret = %d",
+			     irq, ret);
+		return;
+	}
+	icnss_pr_dbg("FW crash indication handler registered irq = %d\n", irq);
+	priv->fw_early_crash_irq = irq;
+}
+
+static irqreturn_t fw_soc_wake_ack_handler(int irq, void *ctx)
+{
+	struct icnss_priv *priv = ctx;
+
+	if (priv)
+		complete(&priv->smp2p_soc_wake_wait);
+
+	return IRQ_HANDLED;
+}
+
+static void register_soc_wake_notif(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct device_node *dev_node;
+	int irq = 0, ret = 0;
+
+	if (!priv)
+		return;
+
+	dev_node = of_find_node_by_name(NULL, "qcom,smp2p_map_wlan_2_in");
+	if (!dev_node) {
+		icnss_pr_err("Failed to get smp2p node for soc-wake-ack\n");
+		return;
+	}
+
+	icnss_pr_dbg("smp2p node->name=%s\n", dev_node->name);
+
+	if (strcmp("qcom,smp2p_map_wlan_2_in", dev_node->name) == 0) {
+		ret = irq = of_irq_get_byname(dev_node,
+					      "qcom,smp2p-soc-wake-ack");
+		if (ret < 0) {
+			icnss_pr_err("Unable to get soc wake ack irq %d\n",
+				     irq);
+			return;
+		}
+	}
+
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+					fw_soc_wake_ack_handler,
+					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					"wlanfw-soc-wake-ack", priv);
+	if (ret < 0) {
+		icnss_pr_err("Unable to register for SOC Wake ACK IRQ handler %d ret = %d",
+			     irq, ret);
+		return;
+	}
+	icnss_pr_dbg("FW SOC Wake ACK handler registered irq = %d\n", irq);
+	priv->fw_soc_wake_ack_irq = irq;
+}
+
+
+int icnss_call_driver_uevent(struct icnss_priv *priv,
+				    enum icnss_uevent uevent, void *data)
+{
+	struct icnss_uevent_data uevent_data;
+
+	if (!priv->ops || !priv->ops->uevent)
+		return 0;
+
+	icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
+		     priv->state, uevent);
+
+	uevent_data.uevent = uevent;
+	uevent_data.data = data;
+
+	return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
+}
+
+static int icnss_setup_dms_mac(struct icnss_priv *priv)
+{
+	int i;
+	int ret = 0;
+
+	ret = icnss_qmi_get_dms_mac(priv);
+	if (ret == 0 && priv->dms.mac_valid)
+		goto qmi_send;
+
+	/* DTSI property use-nv-mac is used to force DMS MAC address for WLAN.
+	 * Thus assert on failure to get MAC from DMS even after retries
+	 */
+	if (priv->use_nv_mac) {
+		for (i = 0; i < ICNSS_DMS_QMI_CONNECTION_WAIT_RETRY; i++) {
+			if (priv->dms.mac_valid)
+				break;
+
+			ret = icnss_qmi_get_dms_mac(priv);
+			if (ret != -EAGAIN)
+				break;
+			msleep(ICNSS_DMS_QMI_CONNECTION_WAIT_MS);
+		}
+		if (!priv->dms.nv_mac_not_prov && !priv->dms.mac_valid) {
+			icnss_pr_err("Unable to get MAC from DMS after retries\n");
+			ICNSS_ASSERT(0);
+			return -EINVAL;
+		}
+	}
+qmi_send:
+	if (priv->dms.mac_valid)
+		ret =
+		icnss_wlfw_wlan_mac_req_send_sync(priv, priv->dms.mac,
+						  ARRAY_SIZE(priv->dms.mac));
+	return ret;
+}
+
+static void icnss_get_smp2p_info(struct icnss_priv *priv,
+				 enum smp2p_out_entry smp2p_entry)
+{
+	int retry = 0;
+	int error;
+
+	if (priv->smp2p_info[smp2p_entry].smem_state)
+		return;
+retry:
+	priv->smp2p_info[smp2p_entry].smem_state =
+		qcom_smem_state_get(&priv->pdev->dev,
+				    icnss_smp2p_str[smp2p_entry],
+				    &priv->smp2p_info[smp2p_entry].smem_bit);
+	if (IS_ERR(priv->smp2p_info[smp2p_entry].smem_state)) {
+		if (retry++ < SMP2P_GET_MAX_RETRY) {
+			error = PTR_ERR(priv->smp2p_info[smp2p_entry].smem_state);
+			icnss_pr_err("Failed to get smem state, ret: %d Entry: %s",
+				     error, icnss_smp2p_str[smp2p_entry]);
+			msleep(SMP2P_GET_RETRY_DELAY_MS);
+			goto retry;
+		}
+		ICNSS_ASSERT(0);
+		return;
+	}
+
+	icnss_pr_dbg("smem state, Entry: %s", icnss_smp2p_str[smp2p_entry]);
+}
+
+static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
+						 void *data)
+{
+	int ret = 0;
+	bool ignore_assert = false;
+
+	if (!priv)
+		return -ENODEV;
+
+	set_bit(ICNSS_WLFW_EXISTS, &priv->state);
+	clear_bit(ICNSS_FW_DOWN, &priv->state);
+	clear_bit(ICNSS_FW_READY, &priv->state);
+
+	icnss_ignore_fw_timeout(false);
+
+	if (test_bit(ICNSS_WLFW_CONNECTED, &priv->state)) {
+		icnss_pr_err("QMI Server already in Connected State\n");
+		ICNSS_ASSERT(0);
+	}
+
+	ret = icnss_connect_to_fw_server(priv, data);
+	if (ret)
+		goto fail;
+
+	set_bit(ICNSS_WLFW_CONNECTED, &priv->state);
+
+	ret = wlfw_ind_register_send_sync_msg(priv);
+	if (ret < 0) {
+		if (ret == -EALREADY) {
+			ret = 0;
+			goto qmi_registered;
+		}
+		ignore_assert = true;
+		goto fail;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		ret = wlfw_host_cap_send_sync(priv);
+		if (ret < 0)
+			goto fail;
+	}
+
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		if (!priv->msa_va) {
+			icnss_pr_err("Invalid MSA address\n");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		ret = wlfw_msa_mem_info_send_sync_msg(priv);
+		if (ret < 0) {
+			ignore_assert = true;
+			goto fail;
+		}
+
+		ret = wlfw_msa_ready_send_sync_msg(priv);
+		if (ret < 0) {
+			ignore_assert = true;
+			goto fail;
+		}
+	}
+
+	ret = wlfw_cap_send_sync_msg(priv);
+	if (ret < 0) {
+		ignore_assert = true;
+		goto fail;
+	}
+
+	ret = icnss_hw_power_on(priv);
+	if (ret)
+		goto fail;
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		ret = wlfw_device_info_send_msg(priv);
+		if (ret < 0) {
+			ignore_assert = true;
+			goto  device_info_failure;
+		}
+
+		priv->mem_base_va = devm_ioremap(&priv->pdev->dev,
+						 priv->mem_base_pa,
+						 priv->mem_base_size);
+		if (!priv->mem_base_va) {
+			icnss_pr_err("Ioremap failed for bar address\n");
+			goto device_info_failure;
+		}
+
+		icnss_pr_dbg("Non-Secured Bar Address pa: %pa, va: 0x%pK\n",
+			     &priv->mem_base_pa,
+			     priv->mem_base_va);
+
+		if (priv->mhi_state_info_pa)
+			priv->mhi_state_info_va = devm_ioremap(&priv->pdev->dev,
+						priv->mhi_state_info_pa,
+						PAGE_SIZE);
+		if (!priv->mhi_state_info_va)
+			icnss_pr_err("Ioremap failed for MHI info address\n");
+
+		icnss_pr_dbg("MHI state info Address pa: %pa, va: 0x%pK\n",
+			     &priv->mhi_state_info_pa,
+			     priv->mhi_state_info_va);
+	}
+
+	if (priv->bdf_download_support) {
+		icnss_wlfw_bdf_dnld_send_sync(priv, ICNSS_BDF_REGDB);
+
+		ret = icnss_wlfw_bdf_dnld_send_sync(priv,
+						    priv->ctrl_params.bdf_type);
+		if (ret < 0)
+			goto device_info_failure;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		if (!priv->fw_soc_wake_ack_irq)
+			register_soc_wake_notif(&priv->pdev->dev);
+
+		icnss_get_smp2p_info(priv, ICNSS_SMP2P_OUT_POWER_SAVE);
+		icnss_get_smp2p_info(priv, ICNSS_SMP2P_OUT_SOC_WAKE);
+		icnss_get_smp2p_info(priv, ICNSS_SMP2P_OUT_EP_POWER_SAVE);
+	}
+
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		if (priv->bdf_download_support) {
+			ret = wlfw_cal_report_req(priv);
+			if (ret < 0)
+				goto device_info_failure;
+		}
+
+		wlfw_dynamic_feature_mask_send_sync_msg(priv,
+							dynamic_feature_mask);
+	}
+
+	if (!priv->fw_error_fatal_irq)
+		register_fw_error_notifications(&priv->pdev->dev);
+
+	if (!priv->fw_early_crash_irq)
+		register_early_crash_notifications(&priv->pdev->dev);
+
+	if (priv->vbatt_supported)
+		icnss_init_vph_monitor(priv);
+
+	return ret;
+
+device_info_failure:
+	icnss_hw_power_off(priv);
+fail:
+	ICNSS_ASSERT(ignore_assert);
+qmi_registered:
+	return ret;
+}
+
+static int icnss_driver_event_server_exit(struct icnss_priv *priv)
+{
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_info("WLAN FW Service Disconnected: 0x%lx\n", priv->state);
+
+	icnss_clear_server(priv);
+
+	if (priv->adc_tm_dev && priv->vbatt_supported)
+		adc_tm_disable_chan_meas(priv->adc_tm_dev,
+					  &priv->vph_monitor_params);
+
+	return 0;
+}
+
+static int icnss_call_driver_probe(struct icnss_priv *priv)
+{
+	int ret = 0;
+	int probe_cnt = 0;
+
+	if (!priv->ops || !priv->ops->probe)
+		return 0;
+
+	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
+
+	icnss_hw_power_on(priv);
+
+	icnss_block_shutdown(true);
+	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+		ret = priv->ops->probe(&priv->pdev->dev);
+		probe_cnt++;
+		if (ret != -EPROBE_DEFER)
+			break;
+	}
+	if (ret < 0) {
+		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+			     ret, priv->state, probe_cnt);
+		icnss_block_shutdown(false);
+		goto out;
+	}
+
+	icnss_block_shutdown(false);
+	set_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+	return 0;
+
+out:
+	icnss_hw_power_off(priv);
+	return ret;
+}
+
+static int icnss_call_driver_shutdown(struct icnss_priv *priv)
+{
+	if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		goto out;
+
+	if (!priv->ops || !priv->ops->shutdown)
+		goto out;
+
+	if (test_bit(ICNSS_SHUTDOWN_DONE, &priv->state))
+		goto out;
+
+	icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
+
+	priv->ops->shutdown(&priv->pdev->dev);
+	set_bit(ICNSS_SHUTDOWN_DONE, &priv->state);
+
+out:
+	return 0;
+}
+
+static int icnss_pd_restart_complete(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	icnss_pm_relax(priv);
+
+	icnss_call_driver_shutdown(priv);
+
+	clear_bit(ICNSS_PDR, &priv->state);
+	clear_bit(ICNSS_REJUVENATE, &priv->state);
+	clear_bit(ICNSS_PD_RESTART, &priv->state);
+	priv->early_crash_ind = false;
+	priv->is_ssr = false;
+
+	if (!priv->ops || !priv->ops->reinit)
+		goto out;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+		icnss_pr_err("FW is in bad state, state: 0x%lx\n",
+			     priv->state);
+		goto out;
+	}
+
+	if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		goto call_probe;
+
+	icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
+
+	icnss_hw_power_on(priv);
+
+	icnss_block_shutdown(true);
+
+	ret = priv->ops->reinit(&priv->pdev->dev);
+	if (ret < 0) {
+		icnss_fatal_err("Driver reinit failed: %d, state: 0x%lx\n",
+				ret, priv->state);
+		if (!priv->allow_recursive_recovery)
+			ICNSS_ASSERT(false);
+		icnss_block_shutdown(false);
+		goto out_power_off;
+	}
+
+	icnss_block_shutdown(false);
+	clear_bit(ICNSS_SHUTDOWN_DONE, &priv->state);
+	return 0;
+
+call_probe:
+	return icnss_call_driver_probe(priv);
+
+out_power_off:
+	icnss_hw_power_off(priv);
+
+out:
+	return ret;
+}
+
+
+static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data)
+{
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	set_bit(ICNSS_FW_READY, &priv->state);
+	clear_bit(ICNSS_MODE_ON, &priv->state);
+	atomic_set(&priv->soc_wake_ref_count, 0);
+
+	if (priv->device_id == WCN6750_DEVICE_ID)
+		icnss_free_qdss_mem(priv);
+
+	icnss_pr_info("WLAN FW is ready: 0x%lx\n", priv->state);
+
+	icnss_hw_power_off(priv);
+
+	if (!priv->pdev) {
+		icnss_pr_err("Device is not ready\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+		ret = icnss_pd_restart_complete(priv);
+	} else {
+		if (priv->device_id == WCN6750_DEVICE_ID)
+			icnss_setup_dms_mac(priv);
+		ret = icnss_call_driver_probe(priv);
+	}
+
+	icnss_vreg_unvote(priv);
+
+out:
+	return ret;
+}
+
+static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data)
+{
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_info("WLAN FW Initialization done: 0x%lx\n", priv->state);
+
+	if (icnss_wlfw_qdss_dnld_send_sync(priv))
+		icnss_pr_info("Failed to download qdss configuration file");
+
+	if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state))
+		ret = wlfw_wlan_mode_send_sync_msg(priv,
+			(enum wlfw_driver_mode_enum_v01)ICNSS_CALIBRATION);
+	else
+		icnss_driver_event_fw_ready_ind(priv, NULL);
+
+	return ret;
+}
+
+int icnss_alloc_qdss_mem(struct icnss_priv *priv)
+{
+	struct platform_device *pdev = priv->pdev;
+	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
+	int i, j;
+
+	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
+		if (!qdss_mem[i].va && qdss_mem[i].size) {
+			qdss_mem[i].va =
+				dma_alloc_coherent(&pdev->dev,
+						   qdss_mem[i].size,
+						   &qdss_mem[i].pa,
+						   GFP_KERNEL);
+			if (!qdss_mem[i].va) {
+				icnss_pr_err("Failed to allocate QDSS memory for FW, size: 0x%zx, type: %u, chuck-ID: %d\n",
+					     qdss_mem[i].size,
+					     qdss_mem[i].type, i);
+				break;
+			}
+		}
+	}
+
+	/* Best-effort allocation for QDSS trace */
+	if (i < priv->qdss_mem_seg_len) {
+		for (j = i; j < priv->qdss_mem_seg_len; j++) {
+			qdss_mem[j].type = 0;
+			qdss_mem[j].size = 0;
+		}
+		priv->qdss_mem_seg_len = i;
+	}
+
+	return 0;
+}
+
+void icnss_free_qdss_mem(struct icnss_priv *priv)
+{
+	struct platform_device *pdev = priv->pdev;
+	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
+	int i;
+
+	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
+		if (qdss_mem[i].va && qdss_mem[i].size) {
+			icnss_pr_dbg("Freeing memory for QDSS: pa: %pa, size: 0x%zx, type: %u\n",
+				     &qdss_mem[i].pa, qdss_mem[i].size,
+				     qdss_mem[i].type);
+			dma_free_coherent(&pdev->dev,
+					  qdss_mem[i].size, qdss_mem[i].va,
+					  qdss_mem[i].pa);
+			qdss_mem[i].va = NULL;
+			qdss_mem[i].pa = 0;
+			qdss_mem[i].size = 0;
+			qdss_mem[i].type = 0;
+		}
+	}
+	priv->qdss_mem_seg_len = 0;
+}
+
+static int icnss_qdss_trace_req_mem_hdlr(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	ret = icnss_alloc_qdss_mem(priv);
+	if (ret < 0)
+		return ret;
+
+	return wlfw_qdss_trace_mem_info_send_sync(priv);
+}
+
+static void *icnss_qdss_trace_pa_to_va(struct icnss_priv *priv,
+				       u64 pa, u32 size, int *seg_id)
+{
+	int i = 0;
+	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
+	u64 offset = 0;
+	void *va = NULL;
+	u64 local_pa;
+	u32 local_size;
+
+	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
+		local_pa = (u64)qdss_mem[i].pa;
+		local_size = (u32)qdss_mem[i].size;
+		if (pa == local_pa && size <= local_size) {
+			va = qdss_mem[i].va;
+			break;
+		}
+		if (pa > local_pa &&
+		    pa < local_pa + local_size &&
+		    pa + size <= local_pa + local_size) {
+			offset = pa - local_pa;
+			va = qdss_mem[i].va + offset;
+			break;
+		}
+	}
+
+	*seg_id = i;
+	return va;
+}
+
+static int icnss_qdss_trace_save_hdlr(struct icnss_priv *priv,
+				      void *data)
+{
+	struct icnss_qmi_event_qdss_trace_save_data *event_data = data;
+	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
+	int ret = 0;
+	int i;
+	void *va = NULL;
+	u64 pa;
+	u32 size;
+	int seg_id = 0;
+
+	if (!priv->qdss_mem_seg_len) {
+		icnss_pr_err("Memory for QDSS trace is not available\n");
+		return -ENOMEM;
+	}
+
+	if (event_data->mem_seg_len == 0) {
+		for (i = 0; i < priv->qdss_mem_seg_len; i++) {
+			ret = icnss_genl_send_msg(qdss_mem[i].va,
+						  ICNSS_GENL_MSG_TYPE_QDSS,
+						  event_data->file_name,
+						  qdss_mem[i].size);
+			if (ret < 0) {
+				icnss_pr_err("Fail to save QDSS data: %d\n",
+					     ret);
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < event_data->mem_seg_len; i++) {
+			pa = event_data->mem_seg[i].addr;
+			size = event_data->mem_seg[i].size;
+			va = icnss_qdss_trace_pa_to_va(priv, pa,
+						       size, &seg_id);
+			if (!va) {
+				icnss_pr_err("Fail to find matching va for pa %pa\n",
+					     &pa);
+				ret = -EINVAL;
+				break;
+			}
+			ret = icnss_genl_send_msg(va, ICNSS_GENL_MSG_TYPE_QDSS,
+						  event_data->file_name, size);
+			if (ret < 0) {
+				icnss_pr_err("Fail to save QDSS data: %d\n",
+					     ret);
+				break;
+			}
+		}
+	}
+
+	kfree(data);
+	return ret;
+}
+
+static inline int icnss_atomic_dec_if_greater_one(atomic_t *v)
+{
+	int dec, c = atomic_read(v);
+
+	do {
+		dec = c - 1;
+		if (unlikely(dec < 1))
+			break;
+	} while (!atomic_try_cmpxchg(v, &c, dec));
+
+	return dec;
+}
+
+static int icnss_qdss_trace_req_data_hdlr(struct icnss_priv *priv,
+					  void *data)
+{
+	int ret = 0;
+	struct icnss_qmi_event_qdss_trace_save_data *event_data = data;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (!data)
+		return -EINVAL;
+
+	ret = icnss_wlfw_qdss_data_send_sync(priv, event_data->file_name,
+					     event_data->total_size);
+
+	kfree(data);
+	return ret;
+}
+
+static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data)
+{
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (atomic_inc_not_zero(&priv->soc_wake_ref_count)) {
+		icnss_pr_soc_wake("SOC awake after posting work, Ref count: %d",
+				  atomic_read(&priv->soc_wake_ref_count));
+		return 0;
+	}
+
+	ret = icnss_send_smp2p(priv, ICNSS_SOC_WAKE_REQ,
+			       ICNSS_SMP2P_OUT_SOC_WAKE);
+	if (!ret)
+		atomic_inc(&priv->soc_wake_ref_count);
+
+	return ret;
+}
+
+static int icnss_event_soc_wake_release(struct icnss_priv *priv, void *data)
+{
+	int ret = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (atomic_dec_if_positive(&priv->soc_wake_ref_count)) {
+		icnss_pr_soc_wake("Wake release not called. Ref count: %d",
+				  priv->soc_wake_ref_count);
+		return 0;
+	}
+
+	ret = icnss_send_smp2p(priv, ICNSS_SOC_WAKE_REL,
+			       ICNSS_SMP2P_OUT_SOC_WAKE);
+	return ret;
+}
+
+static int icnss_driver_event_register_driver(struct icnss_priv *priv,
+							 void *data)
+{
+	int ret = 0;
+	int probe_cnt = 0;
+
+	if (priv->ops)
+		return -EEXIST;
+
+	priv->ops = data;
+
+	if (test_bit(SKIP_QMI, &priv->ctrl_params.quirks))
+		set_bit(ICNSS_FW_READY, &priv->state);
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+		icnss_pr_err("FW is in bad state, state: 0x%lx\n",
+			     priv->state);
+		return -ENODEV;
+	}
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state)) {
+		icnss_pr_dbg("FW is not ready yet, state: 0x%lx\n",
+			     priv->state);
+		goto out;
+	}
+
+	ret = icnss_hw_power_on(priv);
+	if (ret)
+		goto out;
+
+	icnss_block_shutdown(true);
+	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+		ret = priv->ops->probe(&priv->pdev->dev);
+		probe_cnt++;
+		if (ret != -EPROBE_DEFER)
+			break;
+	}
+	if (ret) {
+		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+			     ret, priv->state, probe_cnt);
+		icnss_block_shutdown(false);
+		goto power_off;
+	}
+
+	icnss_block_shutdown(false);
+	set_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+	return 0;
+
+power_off:
+	icnss_hw_power_off(priv);
+out:
+	return ret;
+}
+
+static int icnss_driver_event_unregister_driver(struct icnss_priv *priv,
+							 void *data)
+{
+	if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
+		priv->ops = NULL;
+		goto out;
+	}
+
+	set_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+
+	icnss_block_shutdown(true);
+
+	if (priv->ops)
+		priv->ops->remove(&priv->pdev->dev);
+
+	icnss_block_shutdown(false);
+
+	clear_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
+	clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+	priv->ops = NULL;
+
+	icnss_hw_power_off(priv);
+
+out:
+	return 0;
+}
+
+static int icnss_fw_crashed(struct icnss_priv *priv,
+			    struct icnss_event_pd_service_down_data *event_data)
+{
+	icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);
+
+	set_bit(ICNSS_PD_RESTART, &priv->state);
+	clear_bit(ICNSS_FW_READY, &priv->state);
+
+	icnss_pm_stay_awake(priv);
+
+	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
+
+	if (event_data && event_data->fw_rejuvenate)
+		wlfw_rejuvenate_ack_send_sync_msg(priv);
+
+	return 0;
+}
+
+int icnss_update_hang_event_data(struct icnss_priv *priv,
+				 struct icnss_uevent_hang_data *hang_data)
+{
+	if (!priv->hang_event_data_va)
+		return -EINVAL;
+
+	priv->hang_event_data = kmemdup(priv->hang_event_data_va,
+					priv->hang_event_data_len,
+					GFP_ATOMIC);
+	if (!priv->hang_event_data)
+		return -ENOMEM;
+
+	// Update the hang event params
+	hang_data->hang_event_data = priv->hang_event_data;
+	hang_data->hang_event_data_len = priv->hang_event_data_len;
+
+	return 0;
+}
+
+int icnss_send_hang_event_data(struct icnss_priv *priv)
+{
+	struct icnss_uevent_hang_data hang_data = {0};
+	int ret = 0xFF;
+
+	if (priv->early_crash_ind) {
+		ret = icnss_update_hang_event_data(priv, &hang_data);
+		if (ret)
+			icnss_pr_err("Unable to allocate memory for Hang event data\n");
+	}
+	icnss_call_driver_uevent(priv, ICNSS_UEVENT_HANG_DATA,
+				 &hang_data);
+
+	if (!ret) {
+		kfree(priv->hang_event_data);
+		priv->hang_event_data = NULL;
+	}
+
+	return 0;
+}
+
+static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
+					      void *data)
+{
+	struct icnss_event_pd_service_down_data *event_data = data;
+
+	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+		icnss_ignore_fw_timeout(false);
+		goto out;
+	}
+
+	if (priv->force_err_fatal)
+		ICNSS_ASSERT(0);
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		icnss_send_smp2p(priv, ICNSS_RESET_MSG,
+				 ICNSS_SMP2P_OUT_POWER_SAVE);
+		icnss_send_smp2p(priv, ICNSS_RESET_MSG,
+				 ICNSS_SMP2P_OUT_SOC_WAKE);
+		icnss_send_smp2p(priv, ICNSS_RESET_MSG,
+				 ICNSS_SMP2P_OUT_EP_POWER_SAVE);
+	}
+
+	icnss_send_hang_event_data(priv);
+
+	if (priv->early_crash_ind) {
+		icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
+			     event_data->crashed, priv->state);
+		goto out;
+	}
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
+		icnss_fatal_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
+				event_data->crashed, priv->state);
+		if (!priv->allow_recursive_recovery)
+			ICNSS_ASSERT(0);
+		goto out;
+	}
+
+	if (!test_bit(ICNSS_PD_RESTART, &priv->state))
+		icnss_fw_crashed(priv, event_data);
+
+out:
+	kfree(data);
+
+	return 0;
+}
+
+static int icnss_driver_event_early_crash_ind(struct icnss_priv *priv,
+					      void *data)
+{
+	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+		icnss_ignore_fw_timeout(false);
+		goto out;
+	}
+
+	priv->early_crash_ind = true;
+	icnss_fw_crashed(priv, NULL);
+
+out:
+	kfree(data);
+
+	return 0;
+}
+
+static int icnss_driver_event_idle_shutdown(struct icnss_priv *priv,
+					    void *data)
+{
+	int ret = 0;
+
+	if (!priv->ops || !priv->ops->idle_shutdown)
+		return 0;
+
+	if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+	    test_bit(ICNSS_REJUVENATE, &priv->state)) {
+		icnss_pr_err("SSR/PDR is already in-progress during idle shutdown callback\n");
+		ret = -EBUSY;
+	} else {
+		icnss_pr_dbg("Calling driver idle shutdown, state: 0x%lx\n",
+								priv->state);
+		icnss_block_shutdown(true);
+		ret = priv->ops->idle_shutdown(&priv->pdev->dev);
+		icnss_block_shutdown(false);
+	}
+
+	return ret;
+}
+
+static int icnss_driver_event_idle_restart(struct icnss_priv *priv,
+					   void *data)
+{
+	int ret = 0;
+
+	if (!priv->ops || !priv->ops->idle_restart)
+		return 0;
+
+	if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+	    test_bit(ICNSS_REJUVENATE, &priv->state)) {
+		icnss_pr_err("SSR/PDR is already in-progress during idle restart callback\n");
+		ret = -EBUSY;
+	} else {
+		icnss_pr_dbg("Calling driver idle restart, state: 0x%lx\n",
+								priv->state);
+		icnss_block_shutdown(true);
+		ret = priv->ops->idle_restart(&priv->pdev->dev);
+		icnss_block_shutdown(false);
+	}
+
+	return ret;
+}
+
+static int icnss_qdss_trace_free_hdlr(struct icnss_priv *priv)
+{
+	icnss_free_qdss_mem(priv);
+
+	return 0;
+}
+
+static int icnss_m3_dump_upload_req_hdlr(struct icnss_priv *priv,
+					 void *data)
+{
+	struct icnss_m3_upload_segments_req_data *event_data = data;
+	struct qcom_dump_segment segment;
+	int i, status = 0, ret = 0;
+	struct list_head head;
+
+	if (!dump_enabled()) {
+		icnss_pr_info("Dump collection is not enabled\n");
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&head);
+
+	for (i = 0; i < event_data->no_of_valid_segments; i++) {
+		memset(&segment, 0, sizeof(segment));
+
+		segment.va = devm_ioremap(&priv->pdev->dev,
+					  event_data->m3_segment[i].addr,
+					  event_data->m3_segment[i].size);
+		if (!segment.va) {
+			icnss_pr_err("Failed to ioremap M3 Dump region");
+			ret = -ENOMEM;
+			goto send_resp;
+		}
+
+		segment.size = event_data->m3_segment[i].size;
+
+		list_add(&segment.node, &head);
+		icnss_pr_dbg("Started Dump colletcion for %s segment",
+			     event_data->m3_segment[i].name);
+
+		switch (event_data->m3_segment[i].type) {
+		case QMI_M3_SEGMENT_PHYAREG_V01:
+			ret = qcom_dump(&head, priv->m3_dump_phyareg->dev);
+			break;
+		case QMI_M3_SEGMENT_PHYDBG_V01:
+			ret = qcom_dump(&head, priv->m3_dump_phydbg->dev);
+			break;
+		case QMI_M3_SEGMENT_WMAC0_REG_V01:
+			ret = qcom_dump(&head, priv->m3_dump_wmac0reg->dev);
+			break;
+		case QMI_M3_SEGMENT_WCSSDBG_V01:
+			ret = qcom_dump(&head, priv->m3_dump_wcssdbg->dev);
+			break;
+		case QMI_M3_SEGMENT_PHYAPDMEM_V01:
+			ret = qcom_dump(&head, priv->m3_dump_phyapdmem->dev);
+			break;
+		default:
+			icnss_pr_err("Invalid Segment type: %d",
+				     event_data->m3_segment[i].type);
+		}
+
+		if (ret) {
+			status = ret;
+			icnss_pr_err("Failed to dump m3 %s segment, err = %d\n",
+				     event_data->m3_segment[i].name, ret);
+		}
+		list_del(&segment.node);
+	}
+send_resp:
+	icnss_wlfw_m3_dump_upload_done_send_sync(priv, event_data->pdev_id,
+						 status);
+
+	return ret;
+}
+
+static int icnss_subsys_restart_level(struct icnss_priv *priv, void *data)
+{
+	int ret = 0;
+	struct icnss_subsys_restart_level_data *event_data = data;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (!data)
+		return -EINVAL;
+
+	ret = wlfw_subsys_restart_level_msg(priv, event_data->restart_level);
+
+	kfree(data);
+
+	return ret;
+}
+
+static void icnss_driver_event_work(struct work_struct *work)
+{
+	struct icnss_priv *priv =
+		container_of(work, struct icnss_priv, event_work);
+	struct icnss_driver_event *event;
+	unsigned long flags;
+	int ret;
+
+	icnss_pm_stay_awake(priv);
+
+	spin_lock_irqsave(&priv->event_lock, flags);
+
+	while (!list_empty(&priv->event_list)) {
+		event = list_first_entry(&priv->event_list,
+					 struct icnss_driver_event, list);
+		list_del(&event->list);
+		spin_unlock_irqrestore(&priv->event_lock, flags);
+
+		icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n",
+			     icnss_driver_event_to_str(event->type),
+			     event->sync ? "-sync" : "", event->type,
+			     priv->state);
+
+		switch (event->type) {
+		case ICNSS_DRIVER_EVENT_SERVER_ARRIVE:
+			ret = icnss_driver_event_server_arrive(priv,
+								 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_SERVER_EXIT:
+			ret = icnss_driver_event_server_exit(priv);
+			break;
+		case ICNSS_DRIVER_EVENT_FW_READY_IND:
+			ret = icnss_driver_event_fw_ready_ind(priv,
+								 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_REGISTER_DRIVER:
+			ret = icnss_driver_event_register_driver(priv,
+								 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER:
+			ret = icnss_driver_event_unregister_driver(priv,
+								   event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
+			ret = icnss_driver_event_pd_service_down(priv,
+								 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND:
+			ret = icnss_driver_event_early_crash_ind(priv,
+								 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN:
+			ret = icnss_driver_event_idle_shutdown(priv,
+							       event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_IDLE_RESTART:
+			ret = icnss_driver_event_idle_restart(priv,
+							      event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND:
+			ret = icnss_driver_event_fw_init_done(priv,
+							      event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM:
+			ret = icnss_qdss_trace_req_mem_hdlr(priv);
+			break;
+		case ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE:
+			ret = icnss_qdss_trace_save_hdlr(priv,
+							 event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
+			ret = icnss_qdss_trace_free_hdlr(priv);
+			break;
+		case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ:
+			ret = icnss_m3_dump_upload_req_hdlr(priv, event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA:
+			ret = icnss_qdss_trace_req_data_hdlr(priv,
+							     event->data);
+			break;
+		case ICNSS_DRIVER_EVENT_SUBSYS_RESTART_LEVEL:
+			ret = icnss_subsys_restart_level(priv, event->data);
+			break;
+		default:
+			icnss_pr_err("Invalid Event type: %d", event->type);
+			kfree(event);
+			continue;
+		}
+
+		priv->stats.events[event->type].processed++;
+
+		icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
+			     icnss_driver_event_to_str(event->type),
+			     event->sync ? "-sync" : "", event->type, ret,
+			     priv->state);
+
+		spin_lock_irqsave(&priv->event_lock, flags);
+		if (event->sync) {
+			event->ret = ret;
+			complete(&event->complete);
+			continue;
+		}
+		spin_unlock_irqrestore(&priv->event_lock, flags);
+
+		kfree(event);
+
+		spin_lock_irqsave(&priv->event_lock, flags);
+	}
+	spin_unlock_irqrestore(&priv->event_lock, flags);
+
+	icnss_pm_relax(priv);
+}
+
+static void icnss_soc_wake_msg_work(struct work_struct *work)
+{
+	struct icnss_priv *priv =
+		container_of(work, struct icnss_priv, soc_wake_msg_work);
+	struct icnss_soc_wake_event *event;
+	unsigned long flags;
+	int ret;
+
+	icnss_pm_stay_awake(priv);
+
+	spin_lock_irqsave(&priv->soc_wake_msg_lock, flags);
+
+	while (!list_empty(&priv->soc_wake_msg_list)) {
+		event = list_first_entry(&priv->soc_wake_msg_list,
+					 struct icnss_soc_wake_event, list);
+		list_del(&event->list);
+		spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags);
+
+		icnss_pr_soc_wake("Processing event: %s%s(%d), state: 0x%lx\n",
+				  icnss_soc_wake_event_to_str(event->type),
+				  event->sync ? "-sync" : "", event->type,
+				  priv->state);
+
+		switch (event->type) {
+		case ICNSS_SOC_WAKE_REQUEST_EVENT:
+			ret = icnss_event_soc_wake_request(priv,
+							   event->data);
+			break;
+		case ICNSS_SOC_WAKE_RELEASE_EVENT:
+			ret = icnss_event_soc_wake_release(priv,
+							   event->data);
+			break;
+		default:
+			icnss_pr_err("Invalid Event type: %d", event->type);
+			kfree(event);
+			continue;
+		}
+
+		priv->stats.soc_wake_events[event->type].processed++;
+
+		icnss_pr_soc_wake("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
+				  icnss_soc_wake_event_to_str(event->type),
+				  event->sync ? "-sync" : "", event->type, ret,
+				  priv->state);
+
+		spin_lock_irqsave(&priv->soc_wake_msg_lock, flags);
+		if (event->sync) {
+			event->ret = ret;
+			complete(&event->complete);
+			continue;
+		}
+		spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags);
+
+		kfree(event);
+
+		spin_lock_irqsave(&priv->soc_wake_msg_lock, flags);
+	}
+	spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags);
+
+	icnss_pm_relax(priv);
+}
+
+static int icnss_msa0_ramdump(struct icnss_priv *priv)
+{
+	int ret = 0;
+	struct qcom_dump_segment segment;
+	struct icnss_ramdump_info *msa0_dump_dev = priv->msa0_dump_dev;
+	struct list_head head;
+
+	if (!dump_enabled()) {
+		icnss_pr_info("Dump collection is not enabled\n");
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&head);
+
+	memset(&segment, 0, sizeof(segment));
+
+	segment.va = priv->msa_va;
+	segment.size = priv->msa_mem_size;
+
+	list_add(&segment.node, &head);
+
+	if (!msa0_dump_dev->dev) {
+		icnss_pr_err("Created Dump Device not found\n");
+		return 0;
+	}
+
+	ret = qcom_dump(&head, msa0_dump_dev->dev);
+	if (ret) {
+		icnss_pr_err("Failed to dump msa0, err = %d\n", ret);
+		return ret;
+	}
+
+	list_del(&segment.node);
+	return ret;
+}
+
+static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv,
+							void *data)
+{
+	struct qcom_ssr_notify_data *notif = data;
+	int ret = 0;
+
+	if (!notif->crashed) {
+		if (atomic_read(&priv->is_shutdown)) {
+			atomic_set(&priv->is_shutdown, false);
+			if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
+				!test_bit(ICNSS_SHUTDOWN_DONE, &priv->state) &&
+				!test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
+				clear_bit(ICNSS_FW_READY, &priv->state);
+				icnss_driver_event_post(priv,
+					  ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+					  ICNSS_EVENT_SYNC_UNINTERRUPTIBLE,
+					  NULL);
+			}
+		}
+
+		if (test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
+			if (!wait_for_completion_timeout(
+					&priv->unblock_shutdown,
+					msecs_to_jiffies(PROBE_TIMEOUT)))
+				icnss_pr_err("modem block shutdown timeout\n");
+		}
+
+		ret = wlfw_send_modem_shutdown_msg(priv);
+		if (ret < 0)
+			icnss_pr_err("Fail to send modem shutdown Indication %d\n",
+				     ret);
+	}
+}
+
+static char *icnss_qcom_ssr_notify_state_to_str(enum qcom_ssr_notify_type code)
+{
+	switch (code) {
+	case QCOM_SSR_BEFORE_POWERUP:
+		return "BEFORE_POWERUP";
+	case QCOM_SSR_AFTER_POWERUP:
+		return "AFTER_POWERUP";
+	case QCOM_SSR_BEFORE_SHUTDOWN:
+		return "BEFORE_SHUTDOWN";
+	case QCOM_SSR_AFTER_SHUTDOWN:
+		return "AFTER_SHUTDOWN";
+	default:
+		return "UNKNOWN";
+	}
+};
+
+static int icnss_wpss_notifier_nb(struct notifier_block *nb,
+				  unsigned long code,
+				  void *data)
+{
+	struct icnss_event_pd_service_down_data *event_data;
+	struct qcom_ssr_notify_data *notif = data;
+	struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+					       wpss_ssr_nb);
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+	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);
+		goto out;
+	}
+
+	if (code != QCOM_SSR_BEFORE_SHUTDOWN)
+		goto out;
+
+	priv->is_ssr = true;
+
+	icnss_pr_info("WPSS went down, state: 0x%lx, crashed: %d\n",
+		      priv->state, notif->crashed);
+
+	set_bit(ICNSS_FW_DOWN, &priv->state);
+
+	if (notif->crashed)
+		priv->stats.recovery.root_pd_crash++;
+	else
+		priv->stats.recovery.root_pd_shutdown++;
+
+	icnss_ignore_fw_timeout(true);
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+
+	if (event_data == NULL)
+		return notifier_from_errno(-ENOMEM);
+
+	event_data->crashed = notif->crashed;
+
+	fw_down_data.crashed = !!notif->crashed;
+	if (test_bit(ICNSS_FW_READY, &priv->state)) {
+		clear_bit(ICNSS_FW_READY, &priv->state);
+		fw_down_data.crashed = !!notif->crashed;
+		icnss_call_driver_uevent(priv,
+					 ICNSS_UEVENT_FW_DOWN,
+					 &fw_down_data);
+	}
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+				ICNSS_EVENT_SYNC, event_data);
+out:
+	icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state);
+	return NOTIFY_OK;
+}
+
+static int icnss_modem_notifier_nb(struct notifier_block *nb,
+				  unsigned long code,
+				  void *data)
+{
+	struct icnss_event_pd_service_down_data *event_data;
+	struct qcom_ssr_notify_data *notif = data;
+	struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+					       modem_ssr_nb);
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+	icnss_pr_vdbg("Modem-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);
+		goto out;
+	}
+
+	if (code != QCOM_SSR_BEFORE_SHUTDOWN)
+		goto out;
+
+	priv->is_ssr = true;
+
+	if (notif->crashed) {
+		priv->stats.recovery.root_pd_crash++;
+		priv->root_pd_shutdown = false;
+	} else {
+		priv->stats.recovery.root_pd_shutdown++;
+		priv->root_pd_shutdown = true;
+	}
+
+	icnss_update_state_send_modem_shutdown(priv, data);
+
+	if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
+		set_bit(ICNSS_FW_DOWN, &priv->state);
+		icnss_ignore_fw_timeout(true);
+
+		if (test_bit(ICNSS_FW_READY, &priv->state)) {
+			clear_bit(ICNSS_FW_READY, &priv->state);
+			fw_down_data.crashed = !!notif->crashed;
+			icnss_call_driver_uevent(priv,
+						 ICNSS_UEVENT_FW_DOWN,
+						 &fw_down_data);
+		}
+		goto out;
+	}
+
+	icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
+		      priv->state, notif->crashed);
+
+	set_bit(ICNSS_FW_DOWN, &priv->state);
+
+	icnss_ignore_fw_timeout(true);
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+
+	if (event_data == NULL)
+		return notifier_from_errno(-ENOMEM);
+
+	event_data->crashed = notif->crashed;
+
+	fw_down_data.crashed = !!notif->crashed;
+	if (test_bit(ICNSS_FW_READY, &priv->state)) {
+		clear_bit(ICNSS_FW_READY, &priv->state);
+		fw_down_data.crashed = !!notif->crashed;
+		icnss_call_driver_uevent(priv,
+					 ICNSS_UEVENT_FW_DOWN,
+					 &fw_down_data);
+	}
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+				ICNSS_EVENT_SYNC, event_data);
+out:
+	icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state);
+	return NOTIFY_OK;
+}
+
+static int icnss_wpss_ssr_register_notifier(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	priv->wpss_ssr_nb.notifier_call = icnss_wpss_notifier_nb;
+	/*
+	 * Assign priority of icnss wpss notifier callback over IPA
+	 * modem notifier callback which is 0
+	 */
+	priv->wpss_ssr_nb.priority = 1;
+
+	priv->wpss_notify_handler =
+		qcom_register_ssr_notifier("wpss", &priv->wpss_ssr_nb);
+
+	if (IS_ERR(priv->wpss_notify_handler)) {
+		ret = PTR_ERR(priv->wpss_notify_handler);
+		icnss_pr_err("WPSS register notifier failed: %d\n", ret);
+	}
+
+	set_bit(ICNSS_SSR_REGISTERED, &priv->state);
+
+	return ret;
+}
+
+static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	priv->modem_ssr_nb.notifier_call = icnss_modem_notifier_nb;
+	/*
+	 * Assign priority of icnss modem notifier callback over IPA
+	 * modem notifier callback which is 0
+	 */
+	priv->modem_ssr_nb.priority = 1;
+
+	priv->modem_notify_handler =
+		qcom_register_ssr_notifier("modem", &priv->modem_ssr_nb);
+
+	if (IS_ERR(priv->modem_notify_handler)) {
+		ret = PTR_ERR(priv->modem_notify_handler);
+		icnss_pr_err("Modem register notifier failed: %d\n", ret);
+	}
+
+	set_bit(ICNSS_SSR_REGISTERED, &priv->state);
+
+	return ret;
+}
+
+static int icnss_wpss_ssr_unregister_notifier(struct icnss_priv *priv)
+{
+	if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
+		return 0;
+
+	qcom_unregister_ssr_notifier(priv->wpss_notify_handler,
+				     &priv->wpss_ssr_nb);
+	priv->wpss_notify_handler = NULL;
+
+	return 0;
+}
+
+static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv)
+{
+	if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state))
+		return 0;
+
+	qcom_unregister_ssr_notifier(priv->modem_notify_handler,
+				     &priv->modem_ssr_nb);
+	priv->modem_notify_handler = NULL;
+
+	return 0;
+}
+
+static void icnss_pdr_notifier_cb(int state, char *service_path, void *priv_cb)
+{
+	struct icnss_priv *priv = priv_cb;
+	struct icnss_event_pd_service_down_data *event_data;
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+	enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
+
+	icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
+		     state, priv->state);
+
+	switch (state) {
+	case SERVREG_SERVICE_STATE_DOWN:
+		event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+
+		if (!event_data)
+			return;
+
+		event_data->crashed = true;
+
+		if (!priv->is_ssr) {
+			set_bit(ICNSS_PDR, &penv->state);
+			if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) {
+				cause = ICNSS_HOST_ERROR;
+				priv->stats.recovery.pdr_host_error++;
+			} else {
+				cause = ICNSS_FW_CRASH;
+				priv->stats.recovery.pdr_fw_crash++;
+			}
+		} else if (priv->root_pd_shutdown) {
+			cause = ICNSS_ROOT_PD_SHUTDOWN;
+			event_data->crashed = false;
+		}
+
+		icnss_pr_info("PD service down, state: 0x%lx: cause: %s\n",
+			      priv->state, icnss_pdr_cause[cause]);
+
+		if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
+			set_bit(ICNSS_FW_DOWN, &priv->state);
+			icnss_ignore_fw_timeout(true);
+
+			if (test_bit(ICNSS_FW_READY, &priv->state)) {
+				clear_bit(ICNSS_FW_READY, &priv->state);
+				fw_down_data.crashed = event_data->crashed;
+				icnss_call_driver_uevent(priv,
+							 ICNSS_UEVENT_FW_DOWN,
+							 &fw_down_data);
+			}
+		}
+		clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+		icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+					ICNSS_EVENT_SYNC, event_data);
+		break;
+	case SERVREG_SERVICE_STATE_UP:
+		clear_bit(ICNSS_FW_DOWN, &priv->state);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static int icnss_pd_restart_enable(struct icnss_priv *priv)
+{
+	struct pdr_handle *handle = NULL;
+	struct pdr_service *service = NULL;
+	int err = 0;
+
+	handle = pdr_handle_alloc(icnss_pdr_notifier_cb, priv);
+	if (IS_ERR_OR_NULL(handle)) {
+		err = PTR_ERR(handle);
+		icnss_pr_err("Failed to alloc pdr handle, err %d", err);
+		goto out;
+	}
+	service = pdr_add_lookup(handle, ICNSS_WLAN_SERVICE_NAME, ICNSS_WLANPD_NAME);
+	if (IS_ERR_OR_NULL(service)) {
+		err = PTR_ERR(service);
+		icnss_pr_err("Failed to add lookup, err %d", err);
+		goto out;
+	}
+	priv->pdr_handle = handle;
+	priv->pdr_service = service;
+	set_bit(ICNSS_PDR_REGISTERED, &priv->state);
+
+	icnss_pr_info("PDR registration happened");
+out:
+	return err;
+}
+
+static void icnss_pdr_unregister_notifier(struct icnss_priv *priv)
+{
+	if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state))
+		return;
+
+	pdr_handle_release(priv->pdr_handle);
+}
+
+static int icnss_ramdump_devnode_init(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	priv->icnss_ramdump_class = class_create(THIS_MODULE, ICNSS_RAMDUMP_NAME);
+	if (IS_ERR_OR_NULL(priv->icnss_ramdump_class)) {
+		ret = PTR_ERR(priv->icnss_ramdump_class);
+		icnss_pr_err("%s:Class create failed for ramdump devices (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	ret = alloc_chrdev_region(&priv->icnss_ramdump_dev, 0, RAMDUMP_NUM_DEVICES,
+				  ICNSS_RAMDUMP_NAME);
+	if (ret < 0) {
+		icnss_pr_err("%s: Unable to allocate major\n", __func__);
+		goto fail_alloc_major;
+	}
+	return 0;
+
+fail_alloc_major:
+	class_destroy(priv->icnss_ramdump_class);
+	return ret;
+}
+
+void *icnss_create_ramdump_device(struct icnss_priv *priv, const char *dev_name)
+{
+	int ret = 0;
+	struct icnss_ramdump_info *ramdump_info;
+
+	ramdump_info = kzalloc(sizeof(*ramdump_info), GFP_KERNEL);
+
+	if (!dev_name) {
+		icnss_pr_err("%s: Invalid device name.\n", __func__);
+		return NULL;
+	}
+
+	snprintf(ramdump_info->name, ARRAY_SIZE(ramdump_info->name), "icnss_%s", dev_name);
+
+	ramdump_info->minor = ida_simple_get(&rd_minor_id, 0, RAMDUMP_NUM_DEVICES, GFP_KERNEL);
+	if (ramdump_info->minor < 0) {
+		icnss_pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
+			     ramdump_info->minor);
+		ret = -ENODEV;
+		goto fail_out_of_minors;
+	}
+
+	ramdump_info->dev = device_create(priv->icnss_ramdump_class, NULL,
+					  MKDEV(MAJOR(priv->icnss_ramdump_dev),
+					  ramdump_info->minor),
+					  ramdump_info, ramdump_info->name);
+	if (IS_ERR_OR_NULL(ramdump_info->dev)) {
+		ret = PTR_ERR(ramdump_info->dev);
+		icnss_pr_err("%s: Device create failed for %s (%d)\n", __func__,
+			     ramdump_info->name, ret);
+		goto fail_device_create;
+	}
+	return (void *)ramdump_info;
+
+fail_device_create:
+	ida_simple_remove(&rd_minor_id, ramdump_info->minor);
+fail_out_of_minors:
+	kfree(ramdump_info);
+	return ERR_PTR(ret);
+}
+
+static int icnss_register_ramdump_devices(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	if (!priv || !priv->pdev) {
+		icnss_pr_err("Platform priv or pdev is NULL\n");
+		return -EINVAL;
+	}
+
+	ret = icnss_ramdump_devnode_init(priv);
+	if (ret)
+		return ret;
+
+	priv->msa0_dump_dev = icnss_create_ramdump_device(priv, "wcss_msa0");
+
+	if (!priv->msa0_dump_dev->dev) {
+		icnss_pr_err("Failed to create msa0 dump device!");
+		return -ENOMEM;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		priv->m3_dump_phyareg = icnss_create_ramdump_device(priv,
+						ICNSS_M3_SEGMENT(
+						ICNSS_M3_SEGMENT_PHYAREG));
+
+		if (!priv->m3_dump_phyareg->dev) {
+			icnss_pr_err("Failed to create m3 dump for Phyareg segment device!");
+			return -ENOMEM;
+		}
+
+		priv->m3_dump_phydbg = icnss_create_ramdump_device(priv,
+						ICNSS_M3_SEGMENT(
+						ICNSS_M3_SEGMENT_PHYA));
+
+		if (!priv->m3_dump_phydbg->dev) {
+			icnss_pr_err("Failed to create m3 dump for Phydbg segment device!");
+			return -ENOMEM;
+		}
+
+		priv->m3_dump_wmac0reg = icnss_create_ramdump_device(priv,
+						ICNSS_M3_SEGMENT(
+						ICNSS_M3_SEGMENT_WMACREG));
+
+		if (!priv->m3_dump_wmac0reg->dev) {
+			icnss_pr_err("Failed to create m3 dump for Wmac0reg segment device!");
+			return -ENOMEM;
+		}
+
+		priv->m3_dump_wcssdbg = icnss_create_ramdump_device(priv,
+						ICNSS_M3_SEGMENT(
+						ICNSS_M3_SEGMENT_WCSSDBG));
+
+		if (!priv->m3_dump_wcssdbg->dev) {
+			icnss_pr_err("Failed to create m3 dump for Wcssdbg segment device!");
+			return -ENOMEM;
+		}
+
+		priv->m3_dump_phyapdmem = icnss_create_ramdump_device(priv,
+						ICNSS_M3_SEGMENT(
+						ICNSS_M3_SEGMENT_PHYAM3));
+
+		if (!priv->m3_dump_phyapdmem->dev) {
+			icnss_pr_err("Failed to create m3 dump for Phyapdmem segment device!");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static int icnss_enable_recovery(struct icnss_priv *priv)
+{
+	int ret;
+
+	if (test_bit(RECOVERY_DISABLE, &priv->ctrl_params.quirks)) {
+		icnss_pr_dbg("Recovery disabled through module parameter\n");
+		return 0;
+	}
+
+	if (test_bit(PDR_ONLY, &priv->ctrl_params.quirks)) {
+		icnss_pr_dbg("SSR disabled through module parameter\n");
+		goto enable_pdr;
+	}
+
+	ret = icnss_register_ramdump_devices(priv);
+	if (ret)
+		return ret;
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		icnss_wpss_ssr_register_notifier(priv);
+		return 0;
+	}
+
+	icnss_modem_ssr_register_notifier(priv);
+	if (test_bit(SSR_ONLY, &priv->ctrl_params.quirks)) {
+		icnss_pr_dbg("PDR disabled through module parameter\n");
+		return 0;
+	}
+
+enable_pdr:
+	ret = icnss_pd_restart_enable(priv);
+
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int icnss_dev_id_match(struct icnss_priv *priv,
+			      struct device_info *dev_info)
+{
+	if (!dev_info) {
+		icnss_pr_info("WLAN driver devinfo is null, Continue driver loading");
+		return 1;
+	}
+
+	while (dev_info->device_id) {
+		if (priv->device_id == dev_info->device_id)
+			return 1;
+		dev_info++;
+	}
+	return 0;
+}
+
+static int icnss_tcdev_get_max_state(struct thermal_cooling_device *tcdev,
+					unsigned long *thermal_state)
+{
+	struct icnss_thermal_cdev *icnss_tcdev = tcdev->devdata;
+
+	*thermal_state = icnss_tcdev->max_thermal_state;
+
+	return 0;
+}
+
+static int icnss_tcdev_get_cur_state(struct thermal_cooling_device *tcdev,
+					unsigned long *thermal_state)
+{
+	struct icnss_thermal_cdev *icnss_tcdev = tcdev->devdata;
+
+	*thermal_state = icnss_tcdev->curr_thermal_state;
+
+	return 0;
+}
+
+static int icnss_tcdev_set_cur_state(struct thermal_cooling_device *tcdev,
+					unsigned long thermal_state)
+{
+	struct icnss_thermal_cdev *icnss_tcdev = tcdev->devdata;
+	struct device *dev = &penv->pdev->dev;
+	int ret = 0;
+
+
+	if (!penv->ops || !penv->ops->set_therm_cdev_state)
+		return 0;
+
+	if (thermal_state > icnss_tcdev->max_thermal_state)
+		return -EINVAL;
+
+	icnss_pr_vdbg("Cooling device set current state: %ld,for cdev id %d",
+		      thermal_state, icnss_tcdev->tcdev_id);
+
+	mutex_lock(&penv->tcdev_lock);
+	ret = penv->ops->set_therm_cdev_state(dev, thermal_state,
+					      icnss_tcdev->tcdev_id);
+	if (!ret)
+		icnss_tcdev->curr_thermal_state = thermal_state;
+	mutex_unlock(&penv->tcdev_lock);
+	if (ret) {
+		icnss_pr_err("Setting Current Thermal State Failed: %d,for cdev id %d",
+			     ret, icnss_tcdev->tcdev_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct thermal_cooling_device_ops icnss_cooling_ops = {
+	.get_max_state = icnss_tcdev_get_max_state,
+	.get_cur_state = icnss_tcdev_get_cur_state,
+	.set_cur_state = icnss_tcdev_set_cur_state,
+};
+
+int icnss_thermal_cdev_register(struct device *dev, unsigned long max_state,
+			   int tcdev_id)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct icnss_thermal_cdev *icnss_tcdev = NULL;
+	char cdev_node_name[THERMAL_NAME_LENGTH] = "";
+	struct device_node *dev_node;
+	int ret = 0;
+
+	icnss_tcdev = kzalloc(sizeof(*icnss_tcdev), GFP_KERNEL);
+	if (!icnss_tcdev)
+		return -ENOMEM;
+
+	icnss_tcdev->tcdev_id = tcdev_id;
+	icnss_tcdev->max_thermal_state = max_state;
+
+	snprintf(cdev_node_name, THERMAL_NAME_LENGTH,
+		 "qcom,icnss_cdev%d", tcdev_id);
+
+	dev_node = of_find_node_by_name(NULL, cdev_node_name);
+	if (!dev_node) {
+		icnss_pr_err("Failed to get cooling device node\n");
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("tcdev node->name=%s\n", dev_node->name);
+
+	if (of_find_property(dev_node, "#cooling-cells", NULL)) {
+		icnss_tcdev->tcdev = thermal_of_cooling_device_register(
+						dev_node,
+						cdev_node_name, icnss_tcdev,
+						&icnss_cooling_ops);
+		if (IS_ERR_OR_NULL(icnss_tcdev->tcdev)) {
+			ret = PTR_ERR(icnss_tcdev->tcdev);
+			icnss_pr_err("Cooling device register failed: %d, for cdev id %d\n",
+				     ret, icnss_tcdev->tcdev_id);
+		} else {
+			icnss_pr_dbg("Cooling device registered for cdev id %d",
+				     icnss_tcdev->tcdev_id);
+			list_add(&icnss_tcdev->tcdev_list,
+				 &priv->icnss_tcdev_list);
+		}
+	} else {
+		icnss_pr_dbg("Cooling device registration not supported");
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(icnss_thermal_cdev_register);
+
+void icnss_thermal_cdev_unregister(struct device *dev, int tcdev_id)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct icnss_thermal_cdev *icnss_tcdev = NULL;
+
+	while (!list_empty(&priv->icnss_tcdev_list)) {
+		icnss_tcdev = list_first_entry(&priv->icnss_tcdev_list,
+					       struct icnss_thermal_cdev,
+					       tcdev_list);
+		thermal_cooling_device_unregister(icnss_tcdev->tcdev);
+		list_del(&icnss_tcdev->tcdev_list);
+		kfree(icnss_tcdev);
+	}
+}
+EXPORT_SYMBOL(icnss_thermal_cdev_unregister);
+
+int icnss_get_curr_therm_cdev_state(struct device *dev,
+				    unsigned long *thermal_state,
+				    int tcdev_id)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct icnss_thermal_cdev *icnss_tcdev = NULL;
+
+	mutex_lock(&priv->tcdev_lock);
+	list_for_each_entry(icnss_tcdev, &priv->icnss_tcdev_list, tcdev_list) {
+		if (icnss_tcdev->tcdev_id != tcdev_id)
+			continue;
+
+		*thermal_state = icnss_tcdev->curr_thermal_state;
+		mutex_unlock(&priv->tcdev_lock);
+		icnss_pr_dbg("Cooling device current state: %ld, for cdev id %d",
+			     icnss_tcdev->curr_thermal_state, tcdev_id);
+		return 0;
+	}
+	mutex_unlock(&priv->tcdev_lock);
+	icnss_pr_dbg("Cooling device ID not found: %d", tcdev_id);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(icnss_get_curr_therm_cdev_state);
+
+int icnss_qmi_send(struct device *dev, int type, void *cmd,
+		  int cmd_len, void *cb_ctx,
+		  int (*cb)(void *ctx, void *event, int event_len))
+{
+	struct icnss_priv *priv = icnss_get_plat_priv();
+	int ret;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (!test_bit(ICNSS_WLFW_CONNECTED, &priv->state))
+		return -EINVAL;
+
+	priv->get_info_cb = cb;
+	priv->get_info_cb_ctx = cb_ctx;
+
+	ret = icnss_wlfw_get_info_send_sync(priv, type, cmd, cmd_len);
+	if (ret) {
+		priv->get_info_cb = NULL;
+		priv->get_info_cb_ctx = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(icnss_qmi_send);
+
+int __icnss_register_driver(struct icnss_driver_ops *ops,
+			    struct module *owner, const char *mod_name)
+{
+	int ret = 0;
+	struct icnss_priv *priv = icnss_get_plat_priv();
+
+	if (!priv || !priv->pdev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	icnss_pr_dbg("Registering driver, state: 0x%lx\n", priv->state);
+
+	if (priv->ops) {
+		icnss_pr_err("Driver already registered\n");
+		ret = -EEXIST;
+		goto out;
+	}
+
+	if (!icnss_dev_id_match(priv, ops->dev_info)) {
+		icnss_pr_err("WLAN driver dev name is %s, not supported by platform driver\n",
+			     ops->dev_info->name);
+		return -ENODEV;
+	}
+
+	if (!ops->probe || !ops->remove) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
+				      0, ops);
+
+	if (ret == -EINTR)
+		ret = 0;
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(__icnss_register_driver);
+
+int icnss_unregister_driver(struct icnss_driver_ops *ops)
+{
+	int ret;
+	struct icnss_priv *priv = icnss_get_plat_priv();
+
+	if (!priv || !priv->pdev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", priv->state);
+
+	if (!priv->ops) {
+		icnss_pr_err("Driver not registered\n");
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ret = icnss_driver_event_post(priv,
+					 ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+				      ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_unregister_driver);
+
+static struct icnss_msi_config msi_config = {
+	.total_vectors = 28,
+	.total_users = 2,
+	.users = (struct icnss_msi_user[]) {
+		{ .name = "CE", .num_vectors = 10, .base_vector = 0 },
+		{ .name = "DP", .num_vectors = 18, .base_vector = 10 },
+	},
+};
+
+static int icnss_get_msi_assignment(struct icnss_priv *priv)
+{
+	priv->msi_config = &msi_config;
+
+	return 0;
+}
+
+int icnss_get_user_msi_assignment(struct device *dev, char *user_name,
+				 int *num_vectors, u32 *user_base_data,
+				 u32 *base_vector)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct icnss_msi_config *msi_config;
+	int idx;
+
+	if (!priv)
+		return -ENODEV;
+
+	msi_config = priv->msi_config;
+	if (!msi_config) {
+		icnss_pr_err("MSI is not supported.\n");
+		return -EINVAL;
+	}
+
+	for (idx = 0; idx < msi_config->total_users; idx++) {
+		if (strcmp(user_name, msi_config->users[idx].name) == 0) {
+			*num_vectors = msi_config->users[idx].num_vectors;
+			*user_base_data = msi_config->users[idx].base_vector
+				+ priv->msi_base_data;
+			*base_vector = msi_config->users[idx].base_vector;
+
+			icnss_pr_dbg("Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
+				    user_name, *num_vectors, *user_base_data,
+				    *base_vector);
+
+			return 0;
+		}
+	}
+
+	icnss_pr_err("Failed to find MSI assignment for %s!\n", user_name);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(icnss_get_user_msi_assignment);
+
+int icnss_get_msi_irq(struct device *dev, unsigned int vector)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int irq_num;
+
+	irq_num = priv->srng_irqs[vector];
+	icnss_pr_dbg("Get IRQ number %d for vector index %d\n",
+		     irq_num, vector);
+
+	return irq_num;
+}
+EXPORT_SYMBOL(icnss_get_msi_irq);
+
+void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low,
+			   u32 *msi_addr_high)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	*msi_addr_low = lower_32_bits(priv->msi_addr_iova);
+	*msi_addr_high = upper_32_bits(priv->msi_addr_iova);
+
+}
+EXPORT_SYMBOL(icnss_get_msi_address);
+
+int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
+	irqreturn_t (*handler)(int, void *),
+		unsigned long flags, const char *name, void *ctx)
+{
+	int ret = 0;
+	unsigned int irq;
+	struct ce_irq_list *irq_entry;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv || !priv->pdev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, priv->state);
+
+	if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+		icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
+		ret = -EINVAL;
+		goto out;
+	}
+	irq = priv->ce_irqs[ce_id];
+	irq_entry = &priv->ce_irq_list[ce_id];
+
+	if (irq_entry->handler || irq_entry->irq) {
+		icnss_pr_err("IRQ already requested: %d, ce_id: %d\n",
+			     irq, ce_id);
+		ret = -EEXIST;
+		goto out;
+	}
+
+	ret = request_irq(irq, handler, flags, name, ctx);
+	if (ret) {
+		icnss_pr_err("IRQ request failed: %d, ce_id: %d, ret: %d\n",
+			     irq, ce_id, ret);
+		goto out;
+	}
+	irq_entry->irq = irq;
+	irq_entry->handler = handler;
+
+	icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
+
+	penv->stats.ce_irqs[ce_id].request++;
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_ce_request_irq);
+
+int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx)
+{
+	int ret = 0;
+	unsigned int irq;
+	struct ce_irq_list *irq_entry;
+
+	if (!penv || !penv->pdev || !dev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
+
+	if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+		icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	irq = penv->ce_irqs[ce_id];
+	irq_entry = &penv->ce_irq_list[ce_id];
+	if (!irq_entry->handler || !irq_entry->irq) {
+		icnss_pr_err("IRQ not requested: %d, ce_id: %d\n", irq, ce_id);
+		ret = -EEXIST;
+		goto out;
+	}
+	free_irq(irq, ctx);
+	irq_entry->irq = 0;
+	irq_entry->handler = NULL;
+
+	penv->stats.ce_irqs[ce_id].free++;
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_ce_free_irq);
+
+void icnss_enable_irq(struct device *dev, unsigned int ce_id)
+{
+	unsigned int irq;
+
+	if (!penv || !penv->pdev || !dev) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return;
+	}
+
+	icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+		     penv->state);
+
+	if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+		icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id);
+		return;
+	}
+
+	penv->stats.ce_irqs[ce_id].enable++;
+
+	irq = penv->ce_irqs[ce_id];
+	enable_irq(irq);
+}
+EXPORT_SYMBOL(icnss_enable_irq);
+
+void icnss_disable_irq(struct device *dev, unsigned int ce_id)
+{
+	unsigned int irq;
+
+	if (!penv || !penv->pdev || !dev) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return;
+	}
+
+	icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+		     penv->state);
+
+	if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
+		icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n",
+			     ce_id);
+		return;
+	}
+
+	irq = penv->ce_irqs[ce_id];
+	disable_irq(irq);
+
+	penv->stats.ce_irqs[ce_id].disable++;
+}
+EXPORT_SYMBOL(icnss_disable_irq);
+
+int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info)
+{
+	char *fw_build_timestamp = NULL;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	info->v_addr = priv->mem_base_va;
+	info->p_addr = priv->mem_base_pa;
+	info->chip_id = priv->chip_info.chip_id;
+	info->chip_family = priv->chip_info.chip_family;
+	info->board_id = priv->board_id;
+	info->soc_id = priv->soc_id;
+	info->fw_version = priv->fw_version_info.fw_version;
+	fw_build_timestamp = priv->fw_version_info.fw_build_timestamp;
+	fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN] = '\0';
+	strlcpy(info->fw_build_timestamp,
+		priv->fw_version_info.fw_build_timestamp,
+		WLFW_MAX_TIMESTAMP_LEN + 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(icnss_get_soc_info);
+
+int icnss_get_mhi_state(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	if (!priv->mhi_state_info_va)
+		return -ENOMEM;
+
+	return ioread32(priv->mhi_state_info_va);
+}
+EXPORT_SYMBOL(icnss_get_mhi_state);
+
+int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode)
+{
+	int ret;
+	struct icnss_priv *priv;
+
+	if (!dev)
+		return -ENODEV;
+
+	priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+	    !test_bit(ICNSS_FW_READY, &penv->state)) {
+		icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
+			     priv->state);
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
+
+	ret = wlfw_ini_send_sync_msg(priv, fw_log_mode);
+	if (ret)
+		icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
+			     ret, fw_log_mode);
+	return ret;
+}
+EXPORT_SYMBOL(icnss_set_fw_log_mode);
+
+int icnss_force_wake_request(struct device *dev)
+{
+	struct icnss_priv *priv;
+
+	if (!dev)
+		return -ENODEV;
+
+	priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	if (atomic_inc_not_zero(&priv->soc_wake_ref_count)) {
+		icnss_pr_soc_wake("SOC already awake, Ref count: %d",
+				  atomic_read(&priv->soc_wake_ref_count));
+		return 0;
+	}
+
+	icnss_pr_soc_wake("Calling SOC Wake request");
+
+	icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_REQUEST_EVENT,
+				  0, NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL(icnss_force_wake_request);
+
+int icnss_force_wake_release(struct device *dev)
+{
+	struct icnss_priv *priv;
+
+	if (!dev)
+		return -ENODEV;
+
+	priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	icnss_pr_soc_wake("Calling SOC Wake response");
+
+	if (atomic_read(&priv->soc_wake_ref_count) &&
+	    icnss_atomic_dec_if_greater_one(&priv->soc_wake_ref_count)) {
+		icnss_pr_soc_wake("SOC previous release pending, Ref count: %d",
+				  atomic_read(&priv->soc_wake_ref_count));
+		return 0;
+	}
+
+	icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_RELEASE_EVENT,
+				  0, NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL(icnss_force_wake_release);
+
+int icnss_is_device_awake(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	return atomic_read(&priv->soc_wake_ref_count);
+}
+EXPORT_SYMBOL(icnss_is_device_awake);
+
+int icnss_is_pci_ep_awake(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Platform driver not initialized\n");
+		return -EINVAL;
+	}
+
+	if (!priv->mhi_state_info_va)
+		return -ENOMEM;
+
+	return ioread32(priv->mhi_state_info_va + ICNSS_PCI_EP_WAKE_OFFSET);
+}
+EXPORT_SYMBOL(icnss_is_pci_ep_awake);
+
+int icnss_athdiag_read(struct device *dev, uint32_t offset,
+		       uint32_t mem_type, uint32_t data_len,
+		       uint8_t *output)
+{
+	int ret = 0;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for diag read: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	if (!output || data_len == 0
+	    || data_len > WLFW_MAX_DATA_SIZE) {
+		icnss_pr_err("Invalid parameters for diag read: output %pK, data_len %u\n",
+			     output, data_len);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+	    !test_bit(ICNSS_POWER_ON, &priv->state)) {
+		icnss_pr_err("Invalid state for diag read: 0x%lx\n",
+			     priv->state);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
+					      data_len, output);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_read);
+
+int icnss_athdiag_write(struct device *dev, uint32_t offset,
+			uint32_t mem_type, uint32_t data_len,
+			uint8_t *input)
+{
+	int ret = 0;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for diag write: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	if (!input || data_len == 0
+	    || data_len > WLFW_MAX_DATA_SIZE) {
+		icnss_pr_err("Invalid parameters for diag write: input %pK, data_len %u\n",
+			     input, data_len);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+	    !test_bit(ICNSS_POWER_ON, &priv->state)) {
+		icnss_pr_err("Invalid state for diag write: 0x%lx\n",
+			     priv->state);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
+					       data_len, input);
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_write);
+
+int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
+		      enum icnss_driver_mode mode,
+		      const char *host_version)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state) ||
+	    !test_bit(ICNSS_FW_READY, &priv->state)) {
+		icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
+			     priv->state);
+		return -EINVAL;
+	}
+
+	if (test_bit(ICNSS_MODE_ON, &priv->state)) {
+		icnss_pr_err("Already Mode on, ignoring wlan_enable state: 0x%lx\n",
+			     priv->state);
+		return -EINVAL;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID &&
+	    !priv->dms.nv_mac_not_prov && !priv->dms.mac_valid)
+		icnss_setup_dms_mac(priv);
+
+	return icnss_send_wlan_enable_to_fw(priv, config, mode, host_version);
+}
+EXPORT_SYMBOL(icnss_wlan_enable);
+
+int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state)) {
+		icnss_pr_dbg("FW down, ignoring wlan_disable state: 0x%lx\n",
+			     priv->state);
+		return 0;
+	}
+
+	return icnss_send_wlan_disable_to_fw(priv);
+}
+EXPORT_SYMBOL(icnss_wlan_disable);
+
+bool icnss_is_qmi_disable(struct device *dev)
+{
+	return test_bit(SKIP_QMI, &penv->ctrl_params.quirks) ? true : false;
+}
+EXPORT_SYMBOL(icnss_is_qmi_disable);
+
+int icnss_get_ce_id(struct device *dev, int irq)
+{
+	int i;
+
+	if (!penv || !penv->pdev || !dev)
+		return -ENODEV;
+
+	for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
+		if (penv->ce_irqs[i] == irq)
+			return i;
+	}
+
+	icnss_pr_err("No matching CE id for irq %d\n", irq);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(icnss_get_ce_id);
+
+int icnss_get_irq(struct device *dev, int ce_id)
+{
+	int irq;
+
+	if (!penv || !penv->pdev || !dev)
+		return -ENODEV;
+
+	if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS)
+		return -EINVAL;
+
+	irq = penv->ce_irqs[ce_id];
+
+	return irq;
+}
+EXPORT_SYMBOL(icnss_get_irq);
+
+struct iommu_domain *icnss_smmu_get_domain(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK\n", dev);
+		return NULL;
+	}
+	return priv->iommu_domain;
+}
+EXPORT_SYMBOL(icnss_smmu_get_domain);
+
+int icnss_smmu_map(struct device *dev,
+		   phys_addr_t paddr, uint32_t *iova_addr, size_t size)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int flag = IOMMU_READ | IOMMU_WRITE;
+	bool dma_coherent = false;
+	unsigned long iova;
+	int prop_len = 0;
+	size_t len;
+	int ret = 0;
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+			     dev, priv);
+		return -EINVAL;
+	}
+
+	if (!iova_addr) {
+		icnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n",
+			     &paddr, size);
+		return -EINVAL;
+	}
+
+	len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
+	iova = roundup(priv->smmu_iova_ipa_current, PAGE_SIZE);
+
+	if (of_get_property(dev->of_node, "qcom,iommu-geometry", &prop_len) &&
+	    iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
+		icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
+			     iova,
+			     &priv->smmu_iova_ipa_start,
+			     priv->smmu_iova_ipa_len);
+		return -ENOMEM;
+	}
+
+	dma_coherent = of_property_read_bool(dev->of_node, "dma-coherent");
+	icnss_pr_dbg("dma-coherent is %s\n",
+		     dma_coherent ? "enabled" : "disabled");
+	if (dma_coherent)
+		flag |= IOMMU_CACHE;
+
+	icnss_pr_dbg("IOMMU Map: iova %lx, len %zu\n", iova, len);
+
+	ret = iommu_map(priv->iommu_domain, iova,
+			rounddown(paddr, PAGE_SIZE), len,
+			flag);
+	if (ret) {
+		icnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret);
+		return ret;
+	}
+
+	priv->smmu_iova_ipa_current = iova + len;
+	*iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));
+
+	icnss_pr_dbg("IOVA addr mapped to physical addr %lx\n", *iova_addr);
+	return 0;
+}
+EXPORT_SYMBOL(icnss_smmu_map);
+
+int icnss_smmu_unmap(struct device *dev,
+		     uint32_t iova_addr, size_t size)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	unsigned long iova;
+	size_t len, unmapped_len;
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+			     dev, priv);
+		return -EINVAL;
+	}
+
+	if (!iova_addr) {
+		icnss_pr_err("iova_addr is NULL, size %zu\n",
+			     size);
+		return -EINVAL;
+	}
+
+	len = roundup(size + iova_addr - rounddown(iova_addr, PAGE_SIZE),
+		      PAGE_SIZE);
+	iova = rounddown(iova_addr, PAGE_SIZE);
+
+	if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
+		icnss_pr_err("Out of IOVA space during unmap, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
+			     iova,
+			     &priv->smmu_iova_ipa_start,
+			     priv->smmu_iova_ipa_len);
+		return -ENOMEM;
+	}
+
+	icnss_pr_dbg("IOMMU Unmap: iova %lx, len %zu\n",
+		     iova, len);
+
+	unmapped_len = iommu_unmap(priv->iommu_domain, iova, len);
+	if (unmapped_len != len) {
+		icnss_pr_err("Failed to unmap, %zu\n", unmapped_len);
+		return -EINVAL;
+	}
+
+	priv->smmu_iova_ipa_current = iova;
+	return 0;
+}
+EXPORT_SYMBOL(icnss_smmu_unmap);
+
+unsigned int icnss_socinfo_get_serial_number(struct device *dev)
+{
+	return socinfo_get_serial_number();
+}
+EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
+
+int icnss_trigger_recovery(struct device *dev)
+{
+	int ret = 0;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+		icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
+			     priv->state);
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		icnss_pr_vdbg("Initiate Root PD restart");
+		ret = icnss_send_smp2p(priv, ICNSS_TRIGGER_SSR,
+				       ICNSS_SMP2P_OUT_POWER_SAVE);
+		if (!ret)
+			set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+		return ret;
+	}
+
+	if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
+		icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
+			     priv->state);
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+
+	icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
+		      priv->state);
+
+	ret = pdr_restart_pd(priv->pdr_handle, priv->pdr_service);
+
+	if (!ret)
+		set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_trigger_recovery);
+
+int icnss_idle_shutdown(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK", dev);
+		return -EINVAL;
+	}
+
+	if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+	    test_bit(ICNSS_REJUVENATE, &priv->state)) {
+		icnss_pr_err("SSR/PDR is already in-progress during idle shutdown\n");
+		return -EBUSY;
+	}
+
+	return icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+					ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_shutdown);
+
+int icnss_idle_restart(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK", dev);
+		return -EINVAL;
+	}
+
+	if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
+	    test_bit(ICNSS_REJUVENATE, &priv->state)) {
+		icnss_pr_err("SSR/PDR is already in-progress during idle restart\n");
+		return -EBUSY;
+	}
+
+	return icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_RESTART,
+					ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+}
+EXPORT_SYMBOL(icnss_idle_restart);
+
+int icnss_exit_power_save(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	icnss_pr_vdbg("Calling Exit Power Save\n");
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+	    !test_bit(ICNSS_MODE_ON, &priv->state))
+		return 0;
+
+	return icnss_send_smp2p(priv, ICNSS_POWER_SAVE_EXIT,
+				ICNSS_SMP2P_OUT_POWER_SAVE);
+}
+EXPORT_SYMBOL(icnss_exit_power_save);
+
+int icnss_prevent_l1(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+	    !test_bit(ICNSS_MODE_ON, &priv->state))
+		return 0;
+
+	return icnss_send_smp2p(priv, ICNSS_PCI_EP_POWER_SAVE_EXIT,
+				ICNSS_SMP2P_OUT_EP_POWER_SAVE);
+}
+EXPORT_SYMBOL(icnss_prevent_l1);
+
+void icnss_allow_l1(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+	    !test_bit(ICNSS_MODE_ON, &priv->state))
+		return;
+
+	icnss_send_smp2p(priv, ICNSS_PCI_EP_POWER_SAVE_ENTER,
+			 ICNSS_SMP2P_OUT_EP_POWER_SAVE);
+}
+EXPORT_SYMBOL(icnss_allow_l1);
+
+void icnss_allow_recursive_recovery(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	priv->allow_recursive_recovery = true;
+
+	icnss_pr_info("Recursive recovery allowed for WLAN\n");
+}
+
+void icnss_disallow_recursive_recovery(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	priv->allow_recursive_recovery = false;
+
+	icnss_pr_info("Recursive recovery disallowed for WLAN\n");
+}
+
+static int icnss_create_shutdown_sysfs(struct icnss_priv *priv)
+{
+	struct kobject *icnss_kobject;
+	int ret = 0;
+
+	atomic_set(&priv->is_shutdown, false);
+
+	icnss_kobject = kobject_create_and_add("shutdown_wlan", kernel_kobj);
+	if (!icnss_kobject) {
+		icnss_pr_err("Unable to create shutdown_wlan kernel object");
+		return -EINVAL;
+	}
+
+	priv->icnss_kobject = icnss_kobject;
+
+	ret = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr);
+	if (ret) {
+		icnss_pr_err("Unable to create icnss sysfs file err:%d", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void icnss_destroy_shutdown_sysfs(struct icnss_priv *priv)
+{
+	struct kobject *icnss_kobject;
+
+	icnss_kobject = priv->icnss_kobject;
+	if (icnss_kobject)
+		kobject_put(icnss_kobject);
+}
+
+static ssize_t qdss_tr_start_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	wlfw_qdss_trace_start(priv);
+	icnss_pr_dbg("Received QDSS start command\n");
+	return count;
+}
+
+static ssize_t qdss_tr_stop_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *user_buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	u32 option = 0;
+
+	if (sscanf(user_buf, "%du", &option) != 1)
+		return -EINVAL;
+
+	wlfw_qdss_trace_stop(priv, option);
+	icnss_pr_dbg("Received QDSS stop command\n");
+	return count;
+}
+
+static ssize_t qdss_conf_download_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	icnss_wlfw_qdss_dnld_send_sync(priv);
+	icnss_pr_dbg("Received QDSS download config command\n");
+	return count;
+}
+
+static ssize_t hw_trc_override_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int tmp = 0;
+
+	if (sscanf(buf, "%du", &tmp) != 1)
+		return -EINVAL;
+
+	priv->hw_trc_override = tmp;
+	icnss_pr_dbg("Received QDSS hw_trc_override indication\n");
+	return count;
+}
+
+static void icnss_wpss_load(struct work_struct *wpss_load_work)
+{
+	struct icnss_priv *priv = icnss_get_plat_priv();
+	phandle rproc_phandle;
+	int ret;
+
+	if (of_property_read_u32(priv->pdev->dev.of_node, "qcom,rproc-handle",
+				 &rproc_phandle)) {
+		icnss_pr_err("error reading rproc phandle\n");
+		return;
+	}
+
+	priv->rproc = rproc_get_by_phandle(rproc_phandle);
+	if (IS_ERR_OR_NULL(priv->rproc)) {
+		icnss_pr_err("rproc not found");
+		return;
+	}
+
+	ret = rproc_boot(priv->rproc);
+	if (ret) {
+		icnss_pr_err("Failed to boot wpss rproc, ret: %d", ret);
+		rproc_put(priv->rproc);
+	}
+}
+
+static inline void icnss_wpss_unload(struct icnss_priv *priv)
+{
+	if (priv && priv->rproc) {
+		rproc_shutdown(priv->rproc);
+		rproc_put(priv->rproc);
+		priv->rproc = NULL;
+	}
+}
+
+static ssize_t wpss_boot_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int wpss_rproc = 0;
+
+	if (priv->device_id != WCN6750_DEVICE_ID)
+		return count;
+
+	if (sscanf(buf, "%du", &wpss_rproc) != 1) {
+		icnss_pr_err("Failed to read wpss rproc info");
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("WPSS Remote Processor: %s", wpss_rproc ? "GET" : "PUT");
+
+	if (wpss_rproc == 1)
+		schedule_work(&wpss_loader);
+	else if (wpss_rproc == 0)
+		icnss_wpss_unload(priv);
+
+	return count;
+}
+
+static ssize_t wlan_en_delay_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	uint32_t wlan_en_delay  = 0;
+
+	if (priv->device_id != WCN6750_DEVICE_ID)
+		return count;
+
+	if (sscanf(buf, "%du", &wlan_en_delay) != 1) {
+		icnss_pr_err("Failed to read wlan_en_delay");
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("WLAN_EN delay: %dms", wlan_en_delay);
+	priv->wlan_en_delay_ms = wlan_en_delay;
+
+	return count;
+}
+
+static DEVICE_ATTR_WO(qdss_tr_start);
+static DEVICE_ATTR_WO(qdss_tr_stop);
+static DEVICE_ATTR_WO(qdss_conf_download);
+static DEVICE_ATTR_WO(hw_trc_override);
+static DEVICE_ATTR_WO(wpss_boot);
+static DEVICE_ATTR_WO(wlan_en_delay);
+
+static struct attribute *icnss_attrs[] = {
+	&dev_attr_qdss_tr_start.attr,
+	&dev_attr_qdss_tr_stop.attr,
+	&dev_attr_qdss_conf_download.attr,
+	&dev_attr_hw_trc_override.attr,
+	&dev_attr_wpss_boot.attr,
+	&dev_attr_wlan_en_delay.attr,
+	NULL,
+};
+
+static struct attribute_group icnss_attr_group = {
+	.attrs = icnss_attrs,
+};
+
+static int icnss_create_sysfs_link(struct icnss_priv *priv)
+{
+	struct device *dev = &priv->pdev->dev;
+	int ret;
+
+	ret = sysfs_create_link(kernel_kobj, &dev->kobj, "icnss");
+	if (ret) {
+		icnss_pr_err("Failed to create icnss link, err = %d\n",
+			     ret);
+		goto out;
+	}
+
+	return 0;
+out:
+	return ret;
+}
+
+static void icnss_remove_sysfs_link(struct icnss_priv *priv)
+{
+	sysfs_remove_link(kernel_kobj, "icnss");
+}
+
+static int icnss_sysfs_create(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	ret = devm_device_add_group(&priv->pdev->dev,
+				    &icnss_attr_group);
+	if (ret) {
+		icnss_pr_err("Failed to create icnss device group, err = %d\n",
+			     ret);
+		goto out;
+	}
+
+	icnss_create_sysfs_link(priv);
+
+	ret = icnss_create_shutdown_sysfs(priv);
+	if (ret)
+		goto remove_icnss_group;
+
+	return 0;
+remove_icnss_group:
+	devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group);
+out:
+	return ret;
+}
+
+static void icnss_sysfs_destroy(struct icnss_priv *priv)
+{
+	icnss_destroy_shutdown_sysfs(priv);
+	icnss_remove_sysfs_link(priv);
+	devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group);
+}
+
+static int icnss_get_vbatt_info(struct icnss_priv *priv)
+{
+	struct adc_tm_chip *adc_tm_dev = NULL;
+	struct iio_channel *channel = NULL;
+	int ret = 0;
+
+	adc_tm_dev = get_adc_tm(&priv->pdev->dev, "icnss");
+	if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
+		icnss_pr_err("adc_tm_dev probe defer\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (IS_ERR(adc_tm_dev)) {
+		ret = PTR_ERR(adc_tm_dev);
+		icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
+			     ret);
+		return ret;
+	}
+
+	channel = devm_iio_channel_get(&priv->pdev->dev, "icnss");
+	if (PTR_ERR(channel) == -EPROBE_DEFER) {
+		icnss_pr_err("channel probe defer\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (IS_ERR(channel)) {
+		ret = PTR_ERR(channel);
+		icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
+			     ret);
+		return ret;
+	}
+
+	priv->adc_tm_dev = adc_tm_dev;
+	priv->channel = channel;
+
+	return 0;
+}
+
+static int icnss_resource_parse(struct icnss_priv *priv)
+{
+	int ret = 0, i = 0;
+	struct platform_device *pdev = priv->pdev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	u32 int_prop;
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,icnss-adc_tm")) {
+		ret = icnss_get_vbatt_info(priv);
+		if (ret == -EPROBE_DEFER)
+			goto out;
+		priv->vbatt_supported = true;
+	}
+
+	ret = icnss_get_vreg(priv);
+	if (ret) {
+		icnss_pr_err("Failed to get vreg, err = %d\n", ret);
+		goto out;
+	}
+
+	ret = icnss_get_clk(priv);
+	if (ret) {
+		icnss_pr_err("Failed to get clocks, err = %d\n", ret);
+		goto put_vreg;
+	}
+
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   "membase");
+		if (!res) {
+			icnss_pr_err("Memory base not found in DT\n");
+			ret = -EINVAL;
+			goto put_clk;
+		}
+
+		priv->mem_base_pa = res->start;
+		priv->mem_base_va = devm_ioremap(dev, priv->mem_base_pa,
+						 resource_size(res));
+		if (!priv->mem_base_va) {
+			icnss_pr_err("Memory base ioremap failed: phy addr: %pa\n",
+				     &priv->mem_base_pa);
+			ret = -EINVAL;
+			goto put_clk;
+		}
+		icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%pK\n",
+			     &priv->mem_base_pa,
+			     priv->mem_base_va);
+
+		for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
+			res = platform_get_resource(priv->pdev,
+						    IORESOURCE_IRQ, i);
+			if (!res) {
+				icnss_pr_err("Fail to get IRQ-%d\n", i);
+				ret = -ENODEV;
+				goto put_clk;
+			} else {
+				priv->ce_irqs[i] = res->start;
+			}
+		}
+	} else if (priv->device_id == WCN6750_DEVICE_ID) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   "msi_addr");
+		if (!res) {
+			icnss_pr_err("MSI address not found in DT\n");
+			ret = -EINVAL;
+			goto put_clk;
+		}
+
+		priv->msi_addr_pa = res->start;
+		priv->msi_addr_iova = dma_map_resource(dev, priv->msi_addr_pa,
+						       PAGE_SIZE,
+						       DMA_FROM_DEVICE, 0);
+		if (dma_mapping_error(dev, priv->msi_addr_iova)) {
+			icnss_pr_err("MSI: failed to map msi address\n");
+			priv->msi_addr_iova = 0;
+			ret = -ENOMEM;
+			goto put_clk;
+		}
+		icnss_pr_dbg("MSI Addr pa: %pa, iova: 0x%pK\n",
+			     &priv->msi_addr_pa,
+			     priv->msi_addr_iova);
+
+		ret = of_property_read_u32_index(dev->of_node,
+						 "interrupts",
+						 1,
+						 &int_prop);
+		if (ret) {
+			icnss_pr_dbg("Read interrupt prop failed");
+			goto put_clk;
+		}
+
+		priv->msi_base_data = int_prop + 32;
+		icnss_pr_dbg(" MSI Base Data: %d, IRQ Index: %d\n",
+			     priv->msi_base_data, int_prop);
+
+		icnss_get_msi_assignment(priv);
+		for (i = 0; i < msi_config.total_vectors; i++) {
+			res = platform_get_resource(priv->pdev,
+						    IORESOURCE_IRQ, i);
+			if (!res) {
+				icnss_pr_err("Fail to get IRQ-%d\n", i);
+				ret = -ENODEV;
+				goto put_clk;
+			} else {
+				priv->srng_irqs[i] = res->start;
+			}
+		}
+	}
+
+	return 0;
+
+put_clk:
+	icnss_put_clk(priv);
+put_vreg:
+	icnss_put_vreg(priv);
+out:
+	return ret;
+}
+
+static int icnss_msa_dt_parse(struct icnss_priv *priv)
+{
+	int ret = 0;
+	struct platform_device *pdev = priv->pdev;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = NULL;
+	u64 prop_size = 0;
+	const __be32 *addrp = NULL;
+
+	np = of_parse_phandle(dev->of_node,
+			      "qcom,wlan-msa-fixed-region", 0);
+	if (np) {
+		addrp = of_get_address(np, 0, &prop_size, NULL);
+		if (!addrp) {
+			icnss_pr_err("Failed to get assigned-addresses or property\n");
+			ret = -EINVAL;
+			of_node_put(np);
+			goto out;
+		}
+
+		priv->msa_pa = of_translate_address(np, addrp);
+		if (priv->msa_pa == OF_BAD_ADDR) {
+			icnss_pr_err("Failed to translate MSA PA from device-tree\n");
+			ret = -EINVAL;
+			of_node_put(np);
+			goto out;
+		}
+
+		of_node_put(np);
+
+		priv->msa_va = memremap(priv->msa_pa,
+					(unsigned long)prop_size, MEMREMAP_WT);
+		if (!priv->msa_va) {
+			icnss_pr_err("MSA PA ioremap failed: phy addr: %pa\n",
+				     &priv->msa_pa);
+			ret = -EINVAL;
+			goto out;
+		}
+		priv->msa_mem_size = prop_size;
+	} else {
+		ret = of_property_read_u32(dev->of_node, "qcom,wlan-msa-memory",
+					   &priv->msa_mem_size);
+		if (ret || priv->msa_mem_size == 0) {
+			icnss_pr_err("Fail to get MSA Memory Size: %u ret: %d\n",
+				     priv->msa_mem_size, ret);
+			goto out;
+		}
+
+		priv->msa_va = dmam_alloc_coherent(&pdev->dev,
+				priv->msa_mem_size, &priv->msa_pa, GFP_KERNEL);
+
+		if (!priv->msa_va) {
+			icnss_pr_err("DMA alloc failed for MSA\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+
+	icnss_pr_dbg("MSA pa: %pa, MSA va: 0x%pK MSA Memory Size: 0x%x\n",
+		     &priv->msa_pa, (void *)priv->msa_va, priv->msa_mem_size);
+
+	priv->use_prefix_path = of_property_read_bool(priv->pdev->dev.of_node,
+						      "qcom,fw-prefix");
+	return 0;
+
+out:
+	return ret;
+}
+
+static int icnss_smmu_fault_handler(struct iommu_domain *domain,
+				    struct device *dev, unsigned long iova,
+				    int flags, void *handler_token)
+{
+	struct icnss_priv *priv = handler_token;
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+	icnss_fatal_err("SMMU fault happened with IOVA 0x%lx\n", iova);
+
+	if (!priv) {
+		icnss_pr_err("priv is NULL\n");
+		return -ENODEV;
+	}
+
+	if (test_bit(ICNSS_FW_READY, &priv->state)) {
+		fw_down_data.crashed = true;
+		icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+					 &fw_down_data);
+	}
+
+	icnss_trigger_recovery(&priv->pdev->dev);
+
+	/* IOMMU driver requires non-zero return value to print debug info. */
+	return -EINVAL;
+}
+
+static int icnss_smmu_dt_parse(struct icnss_priv *priv)
+{
+	int ret = 0;
+	struct platform_device *pdev = priv->pdev;
+	struct device *dev = &pdev->dev;
+	const char *iommu_dma_type;
+	struct resource *res;
+	u32 addr_win[2];
+
+	ret = of_property_read_u32_array(dev->of_node,
+					 "qcom,iommu-dma-addr-pool",
+					 addr_win,
+					 ARRAY_SIZE(addr_win));
+
+	if (ret) {
+		icnss_pr_err("SMMU IOVA base not found\n");
+	} else {
+		priv->smmu_iova_start = addr_win[0];
+		priv->smmu_iova_len = addr_win[1];
+		icnss_pr_dbg("SMMU IOVA start: %pa, len: %zx\n",
+			     &priv->smmu_iova_start,
+			     priv->smmu_iova_len);
+
+		priv->iommu_domain =
+			iommu_get_domain_for_dev(&pdev->dev);
+
+		ret = of_property_read_string(dev->of_node, "qcom,iommu-dma",
+					      &iommu_dma_type);
+		if (!ret && !strcmp("fastmap", iommu_dma_type)) {
+			icnss_pr_dbg("SMMU S1 stage enabled\n");
+			priv->smmu_s1_enable = true;
+			if (priv->device_id == WCN6750_DEVICE_ID)
+				iommu_set_fault_handler(priv->iommu_domain,
+						icnss_smmu_fault_handler,
+						priv);
+		}
+
+		res = platform_get_resource_byname(pdev,
+						   IORESOURCE_MEM,
+						   "smmu_iova_ipa");
+		if (!res) {
+			icnss_pr_err("SMMU IOVA IPA not found\n");
+		} else {
+			priv->smmu_iova_ipa_start = res->start;
+			priv->smmu_iova_ipa_current = res->start;
+			priv->smmu_iova_ipa_len = resource_size(res);
+			icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zx\n",
+				     &priv->smmu_iova_ipa_start,
+				     priv->smmu_iova_ipa_len);
+		}
+	}
+
+	return 0;
+}
+
+int icnss_get_iova(struct icnss_priv *priv, u64 *addr, u64 *size)
+{
+	if (!priv)
+		return -ENODEV;
+
+	if (!priv->smmu_iova_len)
+		return -EINVAL;
+
+	*addr = priv->smmu_iova_start;
+	*size = priv->smmu_iova_len;
+
+	return 0;
+}
+
+int icnss_get_iova_ipa(struct icnss_priv *priv, u64 *addr, u64 *size)
+{
+	if (!priv)
+		return -ENODEV;
+
+	if (!priv->smmu_iova_ipa_len)
+		return -EINVAL;
+
+	*addr = priv->smmu_iova_ipa_start;
+	*size = priv->smmu_iova_ipa_len;
+
+	return 0;
+}
+
+void icnss_add_fw_prefix_name(struct icnss_priv *priv, char *prefix_name,
+			      char *name)
+{
+	if (!priv)
+		return;
+
+	if (!priv->use_prefix_path) {
+		scnprintf(prefix_name, ICNSS_MAX_FILE_NAME, "%s", name);
+		return;
+	}
+
+	scnprintf(prefix_name, ICNSS_MAX_FILE_NAME,
+		  QCA6750_PATH_PREFIX "%s", name);
+
+	icnss_pr_dbg("File added with prefix: %s\n", prefix_name);
+}
+
+static const struct platform_device_id icnss_platform_id_table[] = {
+	{ .name = "wcn6750", .driver_data = WCN6750_DEVICE_ID, },
+	{ .name = "adrastea", .driver_data = ADRASTEA_DEVICE_ID, },
+	{ },
+};
+
+static const struct of_device_id icnss_dt_match[] = {
+	{
+		.compatible = "qcom,wcn6750",
+		.data = (void *)&icnss_platform_id_table[0]},
+	{
+		.compatible = "qcom,icnss",
+		.data = (void *)&icnss_platform_id_table[1]},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, icnss_dt_match);
+
+static void icnss_init_control_params(struct icnss_priv *priv)
+{
+	priv->ctrl_params.qmi_timeout = WLFW_TIMEOUT;
+	priv->ctrl_params.quirks = ICNSS_QUIRKS_DEFAULT;
+	priv->ctrl_params.bdf_type = ICNSS_BDF_TYPE_DEFAULT;
+
+	if (of_property_read_bool(priv->pdev->dev.of_node,
+				  "bdf-download-support"))
+		priv->bdf_download_support = true;
+
+	if (priv->bdf_download_support && priv->device_id == ADRASTEA_DEVICE_ID)
+		priv->ctrl_params.bdf_type = ICNSS_BDF_BIN;
+}
+
+static inline void icnss_runtime_pm_init(struct icnss_priv *priv)
+{
+	pm_runtime_get_sync(&priv->pdev->dev);
+	pm_runtime_forbid(&priv->pdev->dev);
+	pm_runtime_set_active(&priv->pdev->dev);
+	pm_runtime_enable(&priv->pdev->dev);
+}
+
+static inline void icnss_runtime_pm_deinit(struct icnss_priv *priv)
+{
+	pm_runtime_disable(&priv->pdev->dev);
+	pm_runtime_allow(&priv->pdev->dev);
+	pm_runtime_put_sync(&priv->pdev->dev);
+}
+
+static inline bool icnss_use_nv_mac(struct icnss_priv *priv)
+{
+	return of_property_read_bool(priv->pdev->dev.of_node,
+				     "use-nv-mac");
+}
+
+static void rproc_restart_level_notifier(void *data, struct rproc *rproc)
+{
+	struct icnss_subsys_restart_level_data *restart_level_data;
+
+	icnss_pr_info("rproc name: %s recovery disable: %d",
+		      rproc->name, rproc->recovery_disabled);
+
+	restart_level_data = kzalloc(sizeof(*restart_level_data), GFP_ATOMIC);
+	if (!restart_level_data)
+		return;
+
+	if (strnstr(rproc->name, "wpss", ICNSS_RPROC_LEN)) {
+		if (rproc->recovery_disabled)
+			restart_level_data->restart_level = ICNSS_DISABLE_M3_SSR;
+		else
+			restart_level_data->restart_level = ICNSS_ENABLE_M3_SSR;
+
+		icnss_driver_event_post(penv, ICNSS_DRIVER_EVENT_SUBSYS_RESTART_LEVEL,
+					0, restart_level_data);
+	}
+}
+
+static int icnss_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct device *dev = &pdev->dev;
+	struct icnss_priv *priv;
+	const struct of_device_id *of_id;
+	const struct platform_device_id *device_id;
+
+	if (dev_get_drvdata(dev)) {
+		icnss_pr_err("Driver is already initialized\n");
+		return -EEXIST;
+	}
+
+	of_id = of_match_device(icnss_dt_match, &pdev->dev);
+	if (!of_id || !of_id->data) {
+		icnss_pr_err("Failed to find of match device!\n");
+		ret = -ENODEV;
+		goto out_reset_drvdata;
+	}
+
+	device_id = of_id->data;
+
+	icnss_pr_dbg("Platform driver probe\n");
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->magic = ICNSS_MAGIC;
+	dev_set_drvdata(dev, priv);
+
+	priv->pdev = pdev;
+	priv->device_id = device_id->driver_data;
+	priv->is_chain1_supported = true;
+	INIT_LIST_HEAD(&priv->vreg_list);
+	INIT_LIST_HEAD(&priv->clk_list);
+	icnss_allow_recursive_recovery(dev);
+
+	icnss_init_control_params(priv);
+
+	ret = icnss_resource_parse(priv);
+	if (ret)
+		goto out_reset_drvdata;
+
+	ret = icnss_msa_dt_parse(priv);
+	if (ret)
+		goto out_free_resources;
+
+	ret = icnss_smmu_dt_parse(priv);
+	if (ret)
+		goto out_free_resources;
+
+	spin_lock_init(&priv->event_lock);
+	spin_lock_init(&priv->on_off_lock);
+	spin_lock_init(&priv->soc_wake_msg_lock);
+	mutex_init(&priv->dev_lock);
+	mutex_init(&priv->tcdev_lock);
+
+	priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
+	if (!priv->event_wq) {
+		icnss_pr_err("Workqueue creation failed\n");
+		ret = -EFAULT;
+		goto smmu_cleanup;
+	}
+
+	INIT_WORK(&priv->event_work, icnss_driver_event_work);
+	INIT_LIST_HEAD(&priv->event_list);
+
+	priv->soc_wake_wq = alloc_workqueue("icnss_soc_wake_event",
+					    WQ_UNBOUND|WQ_HIGHPRI, 1);
+	if (!priv->soc_wake_wq) {
+		icnss_pr_err("Soc wake Workqueue creation failed\n");
+		ret = -EFAULT;
+		goto out_destroy_wq;
+	}
+
+	INIT_WORK(&priv->soc_wake_msg_work, icnss_soc_wake_msg_work);
+	INIT_LIST_HEAD(&priv->soc_wake_msg_list);
+
+	ret = icnss_register_fw_service(priv);
+	if (ret < 0) {
+		icnss_pr_err("fw service registration failed: %d\n", ret);
+		goto out_destroy_soc_wq;
+	}
+
+	icnss_enable_recovery(priv);
+
+	icnss_debugfs_create(priv);
+
+	icnss_sysfs_create(priv);
+
+	ret = device_init_wakeup(&priv->pdev->dev, true);
+	if (ret)
+		icnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
+			     ret);
+
+	icnss_set_plat_priv(priv);
+
+	init_completion(&priv->unblock_shutdown);
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		ret = icnss_dms_init(priv);
+		if (ret)
+			icnss_pr_err("ICNSS DMS init failed %d\n", ret);
+		ret = icnss_genl_init();
+		if (ret < 0)
+			icnss_pr_err("ICNSS genl init failed %d\n", ret);
+
+		init_completion(&priv->smp2p_soc_wake_wait);
+		icnss_runtime_pm_init(priv);
+		icnss_aop_mbox_init(priv);
+		set_bit(ICNSS_COLD_BOOT_CAL, &priv->state);
+		priv->bdf_download_support = true;
+		priv->use_nv_mac = icnss_use_nv_mac(priv);
+		icnss_pr_dbg("NV MAC feature is %s\n",
+			     priv->use_nv_mac ? "Mandatory":"Not Mandatory");
+		INIT_WORK(&wpss_loader, icnss_wpss_load);
+		register_trace_android_vh_rproc_recovery_set(rproc_restart_level_notifier, NULL);
+	}
+
+	INIT_LIST_HEAD(&priv->icnss_tcdev_list);
+
+	icnss_pr_info("Platform driver probed successfully\n");
+
+	return 0;
+
+out_destroy_soc_wq:
+	destroy_workqueue(priv->soc_wake_wq);
+out_destroy_wq:
+	destroy_workqueue(priv->event_wq);
+smmu_cleanup:
+	priv->iommu_domain = NULL;
+out_free_resources:
+	icnss_put_resources(priv);
+out_reset_drvdata:
+	dev_set_drvdata(dev, NULL);
+	return ret;
+}
+
+void icnss_destroy_ramdump_device(struct icnss_ramdump_info *ramdump_info)
+{
+	device_unregister(ramdump_info->dev);
+
+	ida_simple_remove(&rd_minor_id, ramdump_info->minor);
+
+	kfree(ramdump_info);
+}
+
+static int icnss_remove(struct platform_device *pdev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(&pdev->dev);
+
+	icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state);
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		icnss_dms_deinit(priv);
+		icnss_genl_exit();
+		icnss_runtime_pm_deinit(priv);
+		if (!IS_ERR_OR_NULL(priv->mbox_chan))
+			mbox_free_channel(priv->mbox_chan);
+		unregister_trace_android_vh_rproc_recovery_set(rproc_restart_level_notifier, NULL);
+		complete_all(&priv->smp2p_soc_wake_wait);
+	}
+
+	device_init_wakeup(&priv->pdev->dev, false);
+
+	icnss_debugfs_destroy(priv);
+
+	icnss_sysfs_destroy(priv);
+
+	complete_all(&priv->unblock_shutdown);
+
+	icnss_destroy_ramdump_device(priv->msa0_dump_dev);
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		icnss_wpss_ssr_unregister_notifier(priv);
+		rproc_put(priv->rproc);
+		icnss_destroy_ramdump_device(priv->m3_dump_phyareg);
+		icnss_destroy_ramdump_device(priv->m3_dump_phydbg);
+		icnss_destroy_ramdump_device(priv->m3_dump_wmac0reg);
+		icnss_destroy_ramdump_device(priv->m3_dump_wcssdbg);
+		icnss_destroy_ramdump_device(priv->m3_dump_phyapdmem);
+	} else {
+		icnss_modem_ssr_unregister_notifier(priv);
+		icnss_pdr_unregister_notifier(priv);
+	}
+
+	class_destroy(priv->icnss_ramdump_class);
+	unregister_chrdev_region(priv->icnss_ramdump_dev, RAMDUMP_NUM_DEVICES);
+
+	icnss_unregister_fw_service(priv);
+	if (priv->event_wq)
+		destroy_workqueue(priv->event_wq);
+
+	if (priv->soc_wake_wq)
+		destroy_workqueue(priv->soc_wake_wq);
+
+	priv->iommu_domain = NULL;
+
+	icnss_hw_power_off(priv);
+
+	icnss_put_resources(priv);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icnss_pm_suspend(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for pm suspend: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
+
+	if (!priv->ops || !priv->ops->pm_suspend ||
+	    IS_ERR(priv->smp2p_info[ICNSS_SMP2P_OUT_POWER_SAVE].smem_state) ||
+	    !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		return 0;
+
+	ret = priv->ops->pm_suspend(dev);
+
+	if (ret == 0) {
+		if (priv->device_id == WCN6750_DEVICE_ID) {
+			if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+			    !test_bit(ICNSS_MODE_ON, &priv->state))
+				return 0;
+
+			ret = icnss_send_smp2p(priv, ICNSS_POWER_SAVE_ENTER,
+					       ICNSS_SMP2P_OUT_POWER_SAVE);
+		}
+		priv->stats.pm_suspend++;
+		set_bit(ICNSS_PM_SUSPEND, &priv->state);
+	} else {
+		priv->stats.pm_suspend_err++;
+	}
+	return ret;
+}
+
+static int icnss_pm_resume(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for pm resume: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
+
+	if (!priv->ops || !priv->ops->pm_resume ||
+	    IS_ERR(priv->smp2p_info[ICNSS_SMP2P_OUT_POWER_SAVE].smem_state) ||
+	    !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		goto out;
+
+	ret = priv->ops->pm_resume(dev);
+
+out:
+	if (ret == 0) {
+		priv->stats.pm_resume++;
+		clear_bit(ICNSS_PM_SUSPEND, &priv->state);
+	} else {
+		priv->stats.pm_resume_err++;
+	}
+	return ret;
+}
+
+static int icnss_pm_suspend_noirq(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
+
+	if (!priv->ops || !priv->ops->suspend_noirq ||
+	    !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		goto out;
+
+	ret = priv->ops->suspend_noirq(dev);
+
+out:
+	if (ret == 0) {
+		priv->stats.pm_suspend_noirq++;
+		set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+	} else {
+		priv->stats.pm_suspend_noirq_err++;
+	}
+	return ret;
+}
+
+static int icnss_pm_resume_noirq(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
+
+	if (!priv->ops || !priv->ops->resume_noirq ||
+	    !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+		goto out;
+
+	ret = priv->ops->resume_noirq(dev);
+
+out:
+	if (ret == 0) {
+		priv->stats.pm_resume_noirq++;
+		clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+	} else {
+		priv->stats.pm_resume_noirq_err++;
+	}
+	return ret;
+}
+
+static int icnss_pm_runtime_suspend(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->device_id != WCN6750_DEVICE_ID) {
+		icnss_pr_err("Ignore runtime suspend:\n");
+		goto out;
+	}
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for runtime suspend: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	if (!priv->ops || !priv->ops->runtime_suspend ||
+	    IS_ERR(priv->smp2p_info[ICNSS_SMP2P_OUT_POWER_SAVE].smem_state))
+		goto out;
+
+	icnss_pr_vdbg("Runtime suspend\n");
+	ret = priv->ops->runtime_suspend(dev);
+	if (!ret) {
+		if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+		    !test_bit(ICNSS_MODE_ON, &priv->state))
+			return 0;
+
+		ret = icnss_send_smp2p(priv, ICNSS_POWER_SAVE_ENTER,
+				       ICNSS_SMP2P_OUT_POWER_SAVE);
+	}
+out:
+	return ret;
+}
+
+static int icnss_pm_runtime_resume(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (priv->device_id != WCN6750_DEVICE_ID) {
+		icnss_pr_err("Ignore runtime resume:\n");
+		goto out;
+	}
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata for runtime resume: dev %pK, data %pK, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		return -EINVAL;
+	}
+
+	if (!priv->ops || !priv->ops->runtime_resume ||
+	    IS_ERR(priv->smp2p_info[ICNSS_SMP2P_OUT_POWER_SAVE].smem_state))
+		goto out;
+
+	icnss_pr_vdbg("Runtime resume, state: 0x%lx\n", priv->state);
+
+	ret = priv->ops->runtime_resume(dev);
+
+out:
+	return ret;
+}
+
+static int icnss_pm_runtime_idle(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->device_id != WCN6750_DEVICE_ID) {
+		icnss_pr_err("Ignore runtime idle:\n");
+		goto out;
+	}
+
+	icnss_pr_vdbg("Runtime idle\n");
+
+	pm_request_autosuspend(dev);
+
+out:
+	return -EBUSY;
+}
+#endif
+
+static const struct dev_pm_ops icnss_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
+				icnss_pm_resume)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
+				      icnss_pm_resume_noirq)
+	SET_RUNTIME_PM_OPS(icnss_pm_runtime_suspend, icnss_pm_runtime_resume,
+			   icnss_pm_runtime_idle)
+};
+
+static struct platform_driver icnss_driver = {
+	.probe  = icnss_probe,
+	.remove = icnss_remove,
+	.driver = {
+		.name = "icnss2",
+		.pm = &icnss_pm_ops,
+		.of_match_table = icnss_dt_match,
+	},
+};
+
+static int __init icnss_initialize(void)
+{
+	icnss_debug_init();
+	return platform_driver_register(&icnss_driver);
+}
+
+static void __exit icnss_exit(void)
+{
+	platform_driver_unregister(&icnss_driver);
+	icnss_debug_deinit();
+}
+
+
+module_init(icnss_initialize);
+module_exit(icnss_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("iWCN CORE platform driver");

+ 512 - 0
icnss2/main.h

@@ -0,0 +1,512 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2020, 2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include <linux/adc-tm-clients.h>
+#include <linux/iio/consumer.h>
+#include <linux/irqreturn.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/ipc_logging.h>
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include <soc/qcom/icnss2.h>
+#include "wlan_firmware_service_v01.h"
+#include <linux/mailbox_client.h>
+
+#define WCN6750_DEVICE_ID 0x6750
+#define ADRASTEA_DEVICE_ID 0xabcd
+#define QMI_WLFW_MAX_NUM_MEM_SEG 32
+#define THERMAL_NAME_LENGTH 20
+#define ICNSS_SMEM_VALUE_MASK 0xFFFFFFFF
+#define ICNSS_SMEM_SEQ_NO_POS 16
+#define QCA6750_PATH_PREFIX    "qca6750/"
+#define ICNSS_MAX_FILE_NAME      35
+#define ICNSS_PCI_EP_WAKE_OFFSET 4
+#define ICNSS_DISABLE_M3_SSR 0
+#define ICNSS_ENABLE_M3_SSR 1
+
+extern uint64_t dynamic_feature_mask;
+
+enum icnss_bdf_type {
+	ICNSS_BDF_BIN,
+	ICNSS_BDF_ELF,
+	ICNSS_BDF_REGDB = 4,
+	ICNSS_BDF_DUMMY = 255,
+};
+
+struct icnss_control_params {
+	unsigned long quirks;
+	unsigned int qmi_timeout;
+	unsigned int bdf_type;
+};
+
+enum icnss_driver_event_type {
+	ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
+	ICNSS_DRIVER_EVENT_SERVER_EXIT,
+	ICNSS_DRIVER_EVENT_FW_READY_IND,
+	ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
+	ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
+	ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+	ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
+	ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+	ICNSS_DRIVER_EVENT_IDLE_RESTART,
+	ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
+	ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
+	ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE,
+	ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
+	ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ,
+	ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA,
+	ICNSS_DRIVER_EVENT_SUBSYS_RESTART_LEVEL,
+	ICNSS_DRIVER_EVENT_MAX,
+};
+
+enum icnss_soc_wake_event_type {
+	ICNSS_SOC_WAKE_REQUEST_EVENT,
+	ICNSS_SOC_WAKE_RELEASE_EVENT,
+	ICNSS_SOC_WAKE_EVENT_MAX,
+};
+
+struct icnss_event_server_arrive_data {
+	unsigned int node;
+	unsigned int port;
+};
+
+struct icnss_event_pd_service_down_data {
+	bool crashed;
+	bool fw_rejuvenate;
+};
+
+struct icnss_driver_event {
+	struct list_head list;
+	enum icnss_driver_event_type type;
+	bool sync;
+	struct completion complete;
+	int ret;
+	void *data;
+};
+
+struct icnss_soc_wake_event {
+	struct list_head list;
+	enum icnss_soc_wake_event_type type;
+	bool sync;
+	struct completion complete;
+	int ret;
+	void *data;
+};
+
+enum icnss_driver_state {
+	ICNSS_WLFW_CONNECTED,
+	ICNSS_POWER_ON,
+	ICNSS_FW_READY,
+	ICNSS_DRIVER_PROBED,
+	ICNSS_FW_TEST_MODE,
+	ICNSS_PM_SUSPEND,
+	ICNSS_PM_SUSPEND_NOIRQ,
+	ICNSS_SSR_REGISTERED,
+	ICNSS_PDR_REGISTERED,
+	ICNSS_PD_RESTART,
+	ICNSS_WLFW_EXISTS,
+	ICNSS_SHUTDOWN_DONE,
+	ICNSS_HOST_TRIGGERED_PDR,
+	ICNSS_FW_DOWN,
+	ICNSS_DRIVER_UNLOADING,
+	ICNSS_REJUVENATE,
+	ICNSS_MODE_ON,
+	ICNSS_BLOCK_SHUTDOWN,
+	ICNSS_PDR,
+	ICNSS_DEL_SERVER,
+	ICNSS_COLD_BOOT_CAL,
+	ICNSS_QMI_DMS_CONNECTED,
+};
+
+struct ce_irq_list {
+	int irq;
+	irqreturn_t (*handler)(int irq, void *priv);
+};
+
+struct icnss_vreg_cfg {
+	const char *name;
+	u32 min_uv;
+	u32 max_uv;
+	u32 load_ua;
+	u32 delay_us;
+	u32 need_unvote;
+	bool required;
+	bool is_supported;
+};
+
+struct icnss_vreg_info {
+	struct list_head list;
+	struct regulator *reg;
+	struct icnss_vreg_cfg cfg;
+	u32 enabled;
+};
+
+struct icnss_cpr_info {
+	const char *vreg_ol_cpr;
+	u32 voltage;
+};
+
+enum icnss_vreg_type {
+	ICNSS_VREG_PRIM,
+};
+struct icnss_clk_cfg {
+	const char *name;
+	u32 freq;
+	u32 required;
+};
+
+struct icnss_clk_info {
+	struct list_head list;
+	struct clk *clk;
+	struct icnss_clk_cfg cfg;
+	u32 enabled;
+};
+
+struct icnss_fw_mem {
+	size_t size;
+	void *va;
+	phys_addr_t pa;
+	u8 valid;
+	u32 type;
+	unsigned long attrs;
+};
+
+enum icnss_smp2p_msg_id {
+	ICNSS_RESET_MSG,
+	ICNSS_POWER_SAVE_ENTER,
+	ICNSS_POWER_SAVE_EXIT,
+	ICNSS_TRIGGER_SSR,
+	ICNSS_SOC_WAKE_REQ,
+	ICNSS_SOC_WAKE_REL,
+	ICNSS_PCI_EP_POWER_SAVE_ENTER,
+	ICNSS_PCI_EP_POWER_SAVE_EXIT,
+};
+
+struct icnss_subsys_restart_level_data {
+	uint8_t restart_level;
+};
+
+struct icnss_stats {
+	struct {
+		uint32_t posted;
+		uint32_t processed;
+	} events[ICNSS_DRIVER_EVENT_MAX];
+
+	struct {
+		u32 posted;
+		u32 processed;
+	} soc_wake_events[ICNSS_SOC_WAKE_EVENT_MAX];
+
+	struct {
+		uint32_t request;
+		uint32_t free;
+		uint32_t enable;
+		uint32_t disable;
+	} ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+
+	struct {
+		uint32_t pdr_fw_crash;
+		uint32_t pdr_host_error;
+		uint32_t root_pd_crash;
+		uint32_t root_pd_shutdown;
+	} recovery;
+
+	uint32_t pm_suspend;
+	uint32_t pm_suspend_err;
+	uint32_t pm_resume;
+	uint32_t pm_resume_err;
+	uint32_t pm_suspend_noirq;
+	uint32_t pm_suspend_noirq_err;
+	uint32_t pm_resume_noirq;
+	uint32_t pm_resume_noirq_err;
+	uint32_t pm_stay_awake;
+	uint32_t pm_relax;
+
+	uint32_t ind_register_req;
+	uint32_t ind_register_resp;
+	uint32_t ind_register_err;
+	uint32_t msa_info_req;
+	uint32_t msa_info_resp;
+	uint32_t msa_info_err;
+	uint32_t msa_ready_req;
+	uint32_t msa_ready_resp;
+	uint32_t msa_ready_err;
+	uint32_t msa_ready_ind;
+	uint32_t cap_req;
+	uint32_t cap_resp;
+	uint32_t cap_err;
+	uint32_t pin_connect_result;
+	uint32_t cfg_req;
+	uint32_t cfg_resp;
+	uint32_t cfg_req_err;
+	uint32_t mode_req;
+	uint32_t mode_resp;
+	uint32_t mode_req_err;
+	uint32_t ini_req;
+	uint32_t ini_resp;
+	uint32_t ini_req_err;
+	u32 rejuvenate_ind;
+	uint32_t rejuvenate_ack_req;
+	uint32_t rejuvenate_ack_resp;
+	uint32_t rejuvenate_ack_err;
+	uint32_t vbatt_req;
+	uint32_t vbatt_resp;
+	uint32_t vbatt_req_err;
+	uint32_t device_info_req;
+	uint32_t device_info_resp;
+	uint32_t device_info_err;
+	u32 exit_power_save_req;
+	u32 exit_power_save_resp;
+	u32 exit_power_save_err;
+	u32 enter_power_save_req;
+	u32 enter_power_save_resp;
+	u32 enter_power_save_err;
+	u32 soc_wake_req;
+	u32 soc_wake_resp;
+	u32 soc_wake_err;
+	u32 restart_level_req;
+	u32 restart_level_resp;
+	u32 restart_level_err;
+};
+
+#define WLFW_MAX_TIMESTAMP_LEN 32
+#define WLFW_MAX_BUILD_ID_LEN 128
+#define WLFW_MAX_NUM_MEMORY_REGIONS 2
+#define WLFW_FUNCTION_NAME_LEN 129
+#define WLFW_MAX_DATA_SIZE 6144
+#define WLFW_MAX_STR_LEN 16
+#define WLFW_MAX_NUM_CE 12
+#define WLFW_MAX_NUM_SVC 24
+#define WLFW_MAX_NUM_SHADOW_REG 24
+#define WLFW_MAX_HANG_EVENT_DATA_SIZE 400
+
+struct wlfw_rf_chip_info {
+	uint32_t chip_id;
+	uint32_t chip_family;
+};
+
+struct wlfw_rf_board_info {
+	uint32_t board_id;
+};
+
+struct wlfw_fw_version_info {
+	uint32_t fw_version;
+	char fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN + 1];
+};
+
+struct icnss_mem_region_info {
+	uint64_t reg_addr;
+	uint32_t size;
+	uint8_t secure_flag;
+};
+
+struct icnss_msi_user {
+	char *name;
+	int num_vectors;
+	u32 base_vector;
+};
+
+struct icnss_msi_config {
+	int total_vectors;
+	int total_users;
+	struct icnss_msi_user *users;
+};
+
+struct icnss_thermal_cdev {
+	struct list_head tcdev_list;
+	int tcdev_id;
+	unsigned long curr_thermal_state;
+	unsigned long max_thermal_state;
+	struct device_node *dev_node;
+	struct thermal_cooling_device *tcdev;
+};
+
+enum smp2p_out_entry {
+	ICNSS_SMP2P_OUT_POWER_SAVE,
+	ICNSS_SMP2P_OUT_SOC_WAKE,
+	ICNSS_SMP2P_OUT_EP_POWER_SAVE,
+	ICNSS_SMP2P_OUT_MAX
+};
+
+static const char * const icnss_smp2p_str[] = {
+	[ICNSS_SMP2P_OUT_POWER_SAVE] = "wlan-smp2p-out",
+	[ICNSS_SMP2P_OUT_SOC_WAKE] = "wlan-soc-wake-smp2p-out",
+	[ICNSS_SMP2P_OUT_EP_POWER_SAVE] = "wlan-ep-powersave-smp2p-out",
+};
+
+struct smp2p_out_info {
+	unsigned short seq;
+	unsigned int smem_bit;
+	struct qcom_smem_state *smem_state;
+};
+
+struct icnss_dms_data {
+	u8 mac_valid;
+	u8 nv_mac_not_prov;
+	u8 mac[QMI_WLFW_MAC_ADDR_SIZE_V01];
+};
+
+struct icnss_ramdump_info {
+	int minor;
+	char name[32];
+	struct device *dev;
+};
+
+struct icnss_priv {
+	uint32_t magic;
+	struct platform_device *pdev;
+	struct icnss_driver_ops *ops;
+	struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
+	struct list_head vreg_list;
+	struct list_head clk_list;
+	struct icnss_cpr_info cpr_info;
+	unsigned long device_id;
+	struct icnss_msi_config *msi_config;
+	u32 msi_base_data;
+	struct icnss_control_params ctrl_params;
+	u8 cal_done;
+	u8 use_prefix_path;
+	u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+	u32 srng_irqs[IWCN_MAX_IRQ_REGISTRATIONS];
+	phys_addr_t mem_base_pa;
+	void __iomem *mem_base_va;
+	u32 mem_base_size;
+	phys_addr_t mhi_state_info_pa;
+	void __iomem *mhi_state_info_va;
+	u32 mhi_state_info_size;
+	struct iommu_domain *iommu_domain;
+	dma_addr_t smmu_iova_start;
+	size_t smmu_iova_len;
+	dma_addr_t smmu_iova_ipa_start;
+	dma_addr_t smmu_iova_ipa_current;
+	size_t smmu_iova_ipa_len;
+	struct qmi_handle qmi;
+	struct qmi_handle qmi_dms;
+	struct list_head event_list;
+	struct list_head soc_wake_msg_list;
+	spinlock_t event_lock;
+	spinlock_t soc_wake_msg_lock;
+	struct work_struct event_work;
+	struct work_struct fw_recv_msg_work;
+	struct work_struct soc_wake_msg_work;
+	struct workqueue_struct *event_wq;
+	struct workqueue_struct *soc_wake_wq;
+	phys_addr_t msa_pa;
+	phys_addr_t msi_addr_pa;
+	dma_addr_t msi_addr_iova;
+	uint32_t msa_mem_size;
+	void *msa_va;
+	unsigned long state;
+	struct wlfw_rf_chip_info chip_info;
+	uint32_t board_id;
+	uint32_t soc_id;
+	struct wlfw_fw_version_info fw_version_info;
+	char fw_build_id[WLFW_MAX_BUILD_ID_LEN + 1];
+	u32 pwr_pin_result;
+	u32 phy_io_pin_result;
+	u32 rf_pin_result;
+	uint32_t nr_mem_region;
+	struct icnss_mem_region_info
+		mem_region[WLFW_MAX_NUM_MEMORY_REGIONS];
+	struct dentry *root_dentry;
+	spinlock_t on_off_lock;
+	struct icnss_stats stats;
+	void *modem_notify_handler;
+	void *wpss_notify_handler;
+	struct notifier_block modem_ssr_nb;
+	struct notifier_block wpss_ssr_nb;
+	uint32_t diag_reg_read_addr;
+	uint32_t diag_reg_read_mem_type;
+	uint32_t diag_reg_read_len;
+	uint8_t *diag_reg_read_buf;
+	atomic_t pm_count;
+	struct icnss_ramdump_info *msa0_dump_dev;
+	struct icnss_ramdump_info *m3_dump_phyareg;
+	struct icnss_ramdump_info *m3_dump_phydbg;
+	struct icnss_ramdump_info *m3_dump_wmac0reg;
+	struct icnss_ramdump_info *m3_dump_wcssdbg;
+	struct icnss_ramdump_info *m3_dump_phyapdmem;
+	bool force_err_fatal;
+	bool allow_recursive_recovery;
+	bool early_crash_ind;
+	u8 cause_for_rejuvenation;
+	u8 requesting_sub_system;
+	u16 line_number;
+	struct mutex dev_lock;
+	uint32_t fw_error_fatal_irq;
+	uint32_t fw_early_crash_irq;
+	struct smp2p_out_info smp2p_info[ICNSS_SMP2P_OUT_MAX];
+	struct completion unblock_shutdown;
+	struct adc_tm_param vph_monitor_params;
+	struct adc_tm_chip *adc_tm_dev;
+	struct iio_channel *channel;
+	uint64_t vph_pwr;
+	bool vbatt_supported;
+	char function_name[WLFW_FUNCTION_NAME_LEN + 1];
+	bool is_ssr;
+	bool smmu_s1_enable;
+	struct kobject *icnss_kobject;
+	struct rproc *rproc;
+	atomic_t is_shutdown;
+	u32 qdss_mem_seg_len;
+	struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG];
+	void *get_info_cb_ctx;
+	int (*get_info_cb)(void *ctx, void *event, int event_len);
+	atomic_t soc_wake_ref_count;
+	phys_addr_t hang_event_data_pa;
+	void __iomem *hang_event_data_va;
+	uint16_t hang_event_data_len;
+	void *hang_event_data;
+	struct list_head icnss_tcdev_list;
+	struct mutex tcdev_lock;
+	bool is_chain1_supported;
+	bool chain_reg_info_updated;
+	u32 hw_trc_override;
+	struct icnss_dms_data dms;
+	u8 use_nv_mac;
+	struct pdr_handle *pdr_handle;
+	struct pdr_service *pdr_service;
+	bool root_pd_shutdown;
+	struct mbox_client mbox_client_data;
+	struct mbox_chan *mbox_chan;
+	u32 wlan_en_delay_ms;
+	struct class *icnss_ramdump_class;
+	dev_t icnss_ramdump_dev;
+	struct completion smp2p_soc_wake_wait;
+	uint32_t fw_soc_wake_ack_irq;
+	char foundry_name;
+	bool bdf_download_support;
+};
+
+struct icnss_reg_info {
+	uint32_t mem_type;
+	uint32_t reg_offset;
+	uint32_t data_len;
+};
+
+void icnss_free_qdss_mem(struct icnss_priv *priv);
+char *icnss_driver_event_to_str(enum icnss_driver_event_type type);
+int icnss_call_driver_uevent(struct icnss_priv *priv,
+				    enum icnss_uevent uevent, void *data);
+int icnss_driver_event_post(struct icnss_priv *priv,
+			    enum icnss_driver_event_type type,
+			    u32 flags, void *data);
+void icnss_allow_recursive_recovery(struct device *dev);
+void icnss_disallow_recursive_recovery(struct device *dev);
+char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type);
+int icnss_soc_wake_event_post(struct icnss_priv *priv,
+			      enum icnss_soc_wake_event_type type,
+			      u32 flags, void *data);
+int icnss_get_iova(struct icnss_priv *priv, u64 *addr, u64 *size);
+int icnss_get_iova_ipa(struct icnss_priv *priv, u64 *addr, u64 *size);
+int icnss_update_cpr_info(struct icnss_priv *priv);
+void icnss_add_fw_prefix_name(struct icnss_priv *priv, char *prefix_name,
+			      char *name);
+int icnss_aop_mbox_init(struct icnss_priv *priv);
+#endif
+

+ 942 - 0
icnss2/power.c

@@ -0,0 +1,942 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#if IS_ENABLED(CONFIG_MSM_QMP)
+#include <linux/mailbox/qmp.h>
+#endif
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <soc/qcom/cmd-db.h>
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+#include "power.h"
+
+static struct icnss_vreg_cfg icnss_wcn6750_vreg_list[] = {
+	{"vdd-cx-mx", 824000, 952000, 0, 0, 0, false, true},
+	{"vdd-1.8-xo", 1872000, 1872000, 0, 0, 0, false, true},
+	{"vdd-1.3-rfa", 1256000, 1352000, 0, 0, 0, false, true},
+};
+
+static struct icnss_vreg_cfg icnss_adrestea_vreg_list[] = {
+	{"vdd-cx-mx", 752000, 752000, 0, 0, 0, false, true},
+	{"vdd-1.8-xo", 1800000, 1800000, 0, 0, 0, false, true},
+	{"vdd-1.3-rfa", 1304000, 1304000, 0, 0, 0, false, true},
+	{"vdd-3.3-ch1", 3312000, 3312000, 0, 0, 0, false, true},
+	{"vdd-3.3-ch0", 3312000, 3312000, 0, 0, 0, false, true},
+};
+
+static struct icnss_clk_cfg icnss_clk_list[] = {
+	{"rf_clk", 0, 0},
+};
+
+static struct icnss_clk_cfg icnss_adrestea_clk_list[] = {
+	{"cxo_ref_clk_pin", 0, 0},
+};
+
+#define ICNSS_VREG_LIST_SIZE		ARRAY_SIZE(icnss_wcn6750_vreg_list)
+#define ICNSS_VREG_ADRESTEA_LIST_SIZE	ARRAY_SIZE(icnss_adrestea_vreg_list)
+#define ICNSS_CLK_LIST_SIZE		ARRAY_SIZE(icnss_clk_list)
+#define ICNSS_CLK_ADRESTEA_LIST_SIZE	ARRAY_SIZE(icnss_adrestea_clk_list)
+
+#define ICNSS_CHAIN1_REGULATOR                          "vdd-3.3-ch1"
+#define MAX_PROP_SIZE					32
+#define ICNSS_THRESHOLD_HIGH				3600000
+#define ICNSS_THRESHOLD_LOW				3450000
+#define ICNSS_THRESHOLD_GUARD				20000
+
+#define BT_CXMX_VOLTAGE_MV		950
+#define ICNSS_MBOX_MSG_MAX_LEN 64
+#define ICNSS_MBOX_TIMEOUT_MS 1000
+
+/**
+ * enum icnss_vreg_param: Voltage regulator TCS param
+ * @ICNSS_VREG_VOLTAGE: Provides voltage level to be configured in TCS
+ * @ICNSS_VREG_MODE: Regulator mode
+ * @ICNSS_VREG_ENABLE: Set Voltage regulator enable config in TCS
+ */
+enum icnss_vreg_param {
+	ICNSS_VREG_VOLTAGE,
+	ICNSS_VREG_MODE,
+	ICNSS_VREG_ENABLE,
+};
+
+/**
+ * enum icnss_tcs_seq: TCS sequence ID for trigger
+ * ICNSS_TCS_UP_SEQ: TCS Sequence based on up trigger / Wake TCS
+ * ICNSS_TCS_DOWN_SEQ: TCS Sequence based on down trigger / Sleep TCS
+ * ICNSS_TCS_ALL_SEQ: Update for both up and down triggers
+ */
+enum icnss_tcs_seq {
+	ICNSS_TCS_UP_SEQ,
+	ICNSS_TCS_DOWN_SEQ,
+	ICNSS_TCS_ALL_SEQ,
+};
+
+static int icnss_get_vreg_single(struct icnss_priv *priv,
+				 struct icnss_vreg_info *vreg)
+{
+	int ret = 0;
+	struct device *dev = NULL;
+	struct regulator *reg = NULL;
+	const __be32 *prop = NULL;
+	char prop_name[MAX_PROP_SIZE] = {0};
+	int len = 0;
+	int i;
+
+	dev = &priv->pdev->dev;
+
+	reg = devm_regulator_get_optional(dev, vreg->cfg.name);
+	if (IS_ERR(reg)) {
+		ret = PTR_ERR(reg);
+		if (ret == -ENODEV) {
+			return ret;
+		} else if (ret == -EPROBE_DEFER) {
+			icnss_pr_info("EPROBE_DEFER for regulator: %s\n",
+				      vreg->cfg.name);
+			goto out;
+		} else if (priv->device_id == ADRASTEA_DEVICE_ID) {
+			if (vreg->cfg.required) {
+				icnss_pr_err("Regulator %s doesn't exist: %d\n",
+					     vreg->cfg.name, ret);
+			goto out;
+			} else {
+				icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
+					     vreg->cfg.name, ret);
+				goto done;
+			}
+		} else {
+			icnss_pr_err("Failed to get regulator %s, err = %d\n",
+				     vreg->cfg.name, ret);
+			goto out;
+		}
+	}
+
+	vreg->reg = reg;
+
+	snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-config",
+		 vreg->cfg.name);
+
+	prop = of_get_property(dev->of_node, prop_name, &len);
+
+	icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
+		     prop_name, len);
+
+	if (!prop || len < (2 * sizeof(__be32))) {
+		icnss_pr_dbg("Property %s %s, use default\n", prop_name,
+			     prop ? "invalid format" : "doesn't exist");
+		goto done;
+	}
+
+	for (i = 0; (i * sizeof(__be32)) < len; i++) {
+		switch (i) {
+		case 0:
+			vreg->cfg.min_uv = be32_to_cpup(&prop[0]);
+			break;
+		case 1:
+			vreg->cfg.max_uv = be32_to_cpup(&prop[1]);
+			break;
+		case 2:
+			vreg->cfg.load_ua = be32_to_cpup(&prop[2]);
+			break;
+		case 3:
+			vreg->cfg.delay_us = be32_to_cpup(&prop[3]);
+			break;
+		case 4:
+			if (priv->device_id == WCN6750_DEVICE_ID)
+				vreg->cfg.need_unvote = be32_to_cpup(&prop[4]);
+			else
+				vreg->cfg.need_unvote = 0;
+			break;
+		default:
+			icnss_pr_dbg("Property %s, ignoring value at %d\n",
+				     prop_name, i);
+			break;
+		}
+	}
+
+done:
+	icnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u, need_unvote: %u\n",
+		     vreg->cfg.name, vreg->cfg.min_uv,
+		     vreg->cfg.max_uv, vreg->cfg.load_ua,
+		     vreg->cfg.delay_us, vreg->cfg.need_unvote);
+
+	return 0;
+
+out:
+	return ret;
+}
+
+static int icnss_vreg_on_single(struct icnss_vreg_info *vreg)
+{
+	int ret = 0;
+
+	if (vreg->enabled) {
+		icnss_pr_dbg("Regulator %s is already enabled\n",
+			     vreg->cfg.name);
+		return 0;
+	}
+
+	icnss_pr_dbg("Regulator %s is being enabled\n", vreg->cfg.name);
+
+	if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+		ret = regulator_set_voltage(vreg->reg,
+					    vreg->cfg.min_uv,
+					    vreg->cfg.max_uv);
+
+		if (ret) {
+			icnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n",
+				     vreg->cfg.name, vreg->cfg.min_uv,
+				     vreg->cfg.max_uv, ret);
+			goto out;
+		}
+	}
+
+	if (vreg->cfg.load_ua) {
+		ret = regulator_set_load(vreg->reg,
+					 vreg->cfg.load_ua);
+
+		if (ret < 0) {
+			icnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n",
+				     vreg->cfg.name, vreg->cfg.load_ua,
+				     ret);
+			goto out;
+		}
+	}
+
+	if (vreg->cfg.delay_us)
+		udelay(vreg->cfg.delay_us);
+
+	ret = regulator_enable(vreg->reg);
+	if (ret) {
+		icnss_pr_err("Failed to enable regulator %s, err = %d\n",
+			     vreg->cfg.name, ret);
+		goto out;
+	}
+
+	vreg->enabled = true;
+
+out:
+	return ret;
+}
+
+static int icnss_vreg_unvote_single(struct icnss_vreg_info *vreg)
+{
+	int ret = 0;
+
+	if (!vreg->enabled) {
+		icnss_pr_dbg("Regulator %s is already disabled\n",
+			     vreg->cfg.name);
+		return 0;
+	}
+
+	icnss_pr_dbg("Removing vote for Regulator %s\n", vreg->cfg.name);
+
+	if (vreg->cfg.load_ua) {
+		ret = regulator_set_load(vreg->reg, 0);
+		if (ret < 0)
+			icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
+				     vreg->cfg.name, ret);
+	}
+
+	if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+		ret = regulator_set_voltage(vreg->reg, 0,
+					    vreg->cfg.max_uv);
+		if (ret)
+			icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
+				     vreg->cfg.name, ret);
+	}
+
+	return ret;
+}
+
+static int icnss_vreg_off_single(struct icnss_vreg_info *vreg)
+{
+	int ret = 0;
+
+	if (!vreg->enabled) {
+		icnss_pr_dbg("Regulator %s is already disabled\n",
+			     vreg->cfg.name);
+		return 0;
+	}
+
+	icnss_pr_dbg("Regulator %s is being disabled\n",
+		     vreg->cfg.name);
+
+	ret = regulator_disable(vreg->reg);
+	if (ret)
+		icnss_pr_err("Failed to disable regulator %s, err = %d\n",
+			     vreg->cfg.name, ret);
+
+	if (vreg->cfg.load_ua) {
+		ret = regulator_set_load(vreg->reg, 0);
+		if (ret < 0)
+			icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
+				     vreg->cfg.name, ret);
+	}
+
+	if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
+		ret = regulator_set_voltage(vreg->reg, 0,
+					    vreg->cfg.max_uv);
+		if (ret)
+			icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
+				     vreg->cfg.name, ret);
+	}
+	vreg->enabled = false;
+
+	return ret;
+}
+
+static struct icnss_vreg_cfg *get_vreg_list(u32 *vreg_list_size,
+					    unsigned long device_id)
+{
+	switch (device_id) {
+	case WCN6750_DEVICE_ID:
+		*vreg_list_size = ICNSS_VREG_LIST_SIZE;
+		return icnss_wcn6750_vreg_list;
+
+	case ADRASTEA_DEVICE_ID:
+		*vreg_list_size = ICNSS_VREG_ADRESTEA_LIST_SIZE;
+		return icnss_adrestea_vreg_list;
+
+	default:
+		icnss_pr_err("Unsupported device_id 0x%x\n", device_id);
+		*vreg_list_size = 0;
+		return NULL;
+	}
+}
+
+int icnss_get_vreg(struct icnss_priv *priv)
+{
+	int ret = 0;
+	int i;
+	struct icnss_vreg_info *vreg;
+	struct icnss_vreg_cfg *vreg_cfg = NULL;
+	struct list_head *vreg_list = &priv->vreg_list;
+	struct device *dev = &priv->pdev->dev;
+	u32 vreg_list_size = 0;
+
+	vreg_cfg = get_vreg_list(&vreg_list_size, priv->device_id);
+	if (!vreg_cfg)
+		return -EINVAL;
+
+	for (i = 0; i < vreg_list_size; i++) {
+		vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+		if (!vreg)
+			return -ENOMEM;
+
+		memcpy(&vreg->cfg, &vreg_cfg[i], sizeof(vreg->cfg));
+		ret = icnss_get_vreg_single(priv, vreg);
+		if (ret != 0) {
+			if (ret == -ENODEV)
+				continue;
+			else
+				return ret;
+		}
+		list_add_tail(&vreg->list, vreg_list);
+	}
+
+	return 0;
+}
+
+void icnss_put_vreg(struct icnss_priv *priv)
+{
+	struct list_head *vreg_list = &priv->vreg_list;
+	struct icnss_vreg_info *vreg = NULL;
+
+	while (!list_empty(vreg_list)) {
+		vreg = list_first_entry(vreg_list,
+					struct icnss_vreg_info, list);
+		list_del(&vreg->list);
+	}
+}
+
+static int icnss_vreg_on(struct icnss_priv *priv)
+{
+	struct list_head *vreg_list = &priv->vreg_list;
+	struct icnss_vreg_info *vreg = NULL;
+	int ret = 0;
+
+	list_for_each_entry(vreg, vreg_list, list) {
+		if (IS_ERR_OR_NULL(vreg->reg) || !vreg->cfg.is_supported)
+			continue;
+		if (!priv->chain_reg_info_updated &&
+		    !strcmp(ICNSS_CHAIN1_REGULATOR, vreg->cfg.name)) {
+			priv->chain_reg_info_updated = true;
+			if (!priv->is_chain1_supported) {
+				vreg->cfg.is_supported = false;
+				continue;
+			}
+		}
+
+		ret = icnss_vreg_on_single(vreg);
+		if (ret)
+			break;
+	}
+
+	if (!ret)
+		return 0;
+
+	list_for_each_entry_continue_reverse(vreg, vreg_list, list) {
+		if (IS_ERR_OR_NULL(vreg->reg) || !vreg->enabled)
+			continue;
+
+		icnss_vreg_off_single(vreg);
+	}
+
+	return ret;
+}
+
+static int icnss_vreg_off(struct icnss_priv *priv)
+{
+	struct list_head *vreg_list = &priv->vreg_list;
+	struct icnss_vreg_info *vreg = NULL;
+
+	list_for_each_entry_reverse(vreg, vreg_list, list) {
+		if (IS_ERR_OR_NULL(vreg->reg))
+			continue;
+
+		icnss_vreg_off_single(vreg);
+	}
+
+	return 0;
+}
+
+int icnss_vreg_unvote(struct icnss_priv *priv)
+{
+	struct list_head *vreg_list = &priv->vreg_list;
+	struct icnss_vreg_info *vreg = NULL;
+
+	list_for_each_entry_reverse(vreg, vreg_list, list) {
+		if (IS_ERR_OR_NULL(vreg->reg))
+			continue;
+
+		if (vreg->cfg.need_unvote)
+			icnss_vreg_unvote_single(vreg);
+	}
+
+	return 0;
+}
+
+int icnss_get_clk_single(struct icnss_priv *priv,
+			 struct icnss_clk_info *clk_info)
+{
+	struct device *dev = &priv->pdev->dev;
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(dev, clk_info->cfg.name);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		if (clk_info->cfg.required)
+			icnss_pr_err("Failed to get clock %s, err = %d\n",
+				     clk_info->cfg.name, ret);
+		else
+			icnss_pr_dbg("Failed to get optional clock %s, err = %d\n",
+				     clk_info->cfg.name, ret);
+		return ret;
+	}
+
+	clk_info->clk = clk;
+	icnss_pr_dbg("Got clock: %s, freq: %u\n",
+		     clk_info->cfg.name, clk_info->cfg.freq);
+
+	return 0;
+}
+
+static int icnss_clk_on_single(struct icnss_clk_info *clk_info)
+{
+	int ret;
+
+	if (clk_info->enabled) {
+		icnss_pr_dbg("Clock %s is already enabled\n",
+			     clk_info->cfg.name);
+		return 0;
+	}
+
+	icnss_pr_dbg("Clock %s is being enabled\n", clk_info->cfg.name);
+
+	if (clk_info->cfg.freq) {
+		ret = clk_set_rate(clk_info->clk, clk_info->cfg.freq);
+		if (ret) {
+			icnss_pr_err("Failed to set frequency %u for clock %s, err = %d\n",
+				     clk_info->cfg.freq, clk_info->cfg.name,
+				     ret);
+			return ret;
+		}
+	}
+
+	ret = clk_prepare_enable(clk_info->clk);
+	if (ret) {
+		icnss_pr_err("Failed to enable clock %s, err = %d\n",
+			     clk_info->cfg.name, ret);
+		return ret;
+	}
+
+	clk_info->enabled = true;
+
+	return 0;
+}
+
+static int icnss_clk_off_single(struct icnss_clk_info *clk_info)
+{
+	if (!clk_info->enabled) {
+		icnss_pr_dbg("Clock %s is already disabled\n",
+			     clk_info->cfg.name);
+		return 0;
+	}
+
+	icnss_pr_dbg("Clock %s is being disabled\n", clk_info->cfg.name);
+
+	clk_disable_unprepare(clk_info->clk);
+	clk_info->enabled = false;
+
+	return 0;
+}
+
+int icnss_get_clk(struct icnss_priv *priv)
+{
+	struct device *dev;
+	struct list_head *clk_list;
+	struct icnss_clk_info *clk_info;
+	struct icnss_clk_cfg *clk_cfg;
+	int ret, i;
+	u32 clk_list_size = 0;
+
+	if (!priv)
+		return -ENODEV;
+
+	dev = &priv->pdev->dev;
+	clk_list = &priv->clk_list;
+
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		clk_cfg = icnss_adrestea_clk_list;
+		clk_list_size = ICNSS_CLK_ADRESTEA_LIST_SIZE;
+	} else if (priv->device_id == WCN6750_DEVICE_ID) {
+		clk_cfg = icnss_clk_list;
+		clk_list_size = ICNSS_CLK_LIST_SIZE;
+	}
+
+	if (!list_empty(clk_list)) {
+		icnss_pr_dbg("Clocks have already been updated\n");
+		return 0;
+	}
+
+	for (i = 0; i < clk_list_size; i++) {
+		clk_info = devm_kzalloc(dev, sizeof(*clk_info), GFP_KERNEL);
+		if (!clk_info) {
+			ret = -ENOMEM;
+			goto cleanup;
+		}
+
+		memcpy(&clk_info->cfg, &clk_cfg[i],
+		       sizeof(clk_info->cfg));
+		ret = icnss_get_clk_single(priv, clk_info);
+		if (ret != 0) {
+			if (clk_info->cfg.required)
+				goto cleanup;
+			else
+				continue;
+		}
+		list_add_tail(&clk_info->list, clk_list);
+	}
+
+	return 0;
+
+cleanup:
+	while (!list_empty(clk_list)) {
+		clk_info = list_first_entry(clk_list, struct icnss_clk_info,
+					    list);
+		list_del(&clk_info->list);
+	}
+
+	return ret;
+}
+
+void icnss_put_clk(struct icnss_priv *priv)
+{
+	struct device *dev;
+	struct list_head *clk_list;
+	struct icnss_clk_info *clk_info;
+
+	if (!priv)
+		return;
+
+	dev = &priv->pdev->dev;
+	clk_list = &priv->clk_list;
+
+	while (!list_empty(clk_list)) {
+		clk_info = list_first_entry(clk_list, struct icnss_clk_info,
+					    list);
+		list_del(&clk_info->list);
+	}
+}
+
+static int icnss_clk_on(struct list_head *clk_list)
+{
+	struct icnss_clk_info *clk_info;
+	int ret = 0;
+
+	list_for_each_entry(clk_info, clk_list, list) {
+		if (IS_ERR_OR_NULL(clk_info->clk))
+			continue;
+		ret = icnss_clk_on_single(clk_info);
+		if (ret)
+			break;
+	}
+
+	if (!ret)
+		return 0;
+
+	list_for_each_entry_continue_reverse(clk_info, clk_list, list) {
+		if (IS_ERR_OR_NULL(clk_info->clk))
+			continue;
+
+		icnss_clk_off_single(clk_info);
+	}
+
+	return ret;
+}
+
+static int icnss_clk_off(struct list_head *clk_list)
+{
+	struct icnss_clk_info *clk_info;
+
+	list_for_each_entry_reverse(clk_info, clk_list, list) {
+		if (IS_ERR_OR_NULL(clk_info->clk))
+			continue;
+
+		icnss_clk_off_single(clk_info);
+	}
+
+	return 0;
+}
+
+int icnss_hw_power_on(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
+
+	spin_lock(&priv->on_off_lock);
+	if (test_bit(ICNSS_POWER_ON, &priv->state)) {
+		spin_unlock(&priv->on_off_lock);
+		return ret;
+	}
+	set_bit(ICNSS_POWER_ON, &priv->state);
+	spin_unlock(&priv->on_off_lock);
+
+	ret = icnss_vreg_on(priv);
+	if (ret) {
+		icnss_pr_err("Failed to turn on vreg, err = %d\n", ret);
+		goto out;
+	}
+
+	ret = icnss_clk_on(&priv->clk_list);
+	if (ret)
+		goto vreg_off;
+
+	return ret;
+
+vreg_off:
+	icnss_vreg_off(priv);
+out:
+	clear_bit(ICNSS_POWER_ON, &priv->state);
+	return ret;
+}
+
+int icnss_hw_power_off(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	if (test_bit(HW_ALWAYS_ON, &priv->ctrl_params.quirks))
+		return 0;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return 0;
+
+	icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
+
+	spin_lock(&priv->on_off_lock);
+	if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
+		spin_unlock(&priv->on_off_lock);
+		return ret;
+	}
+	clear_bit(ICNSS_POWER_ON, &priv->state);
+	spin_unlock(&priv->on_off_lock);
+
+	icnss_clk_off(&priv->clk_list);
+
+	ret = icnss_vreg_off(priv);
+
+	return ret;
+}
+
+int icnss_power_on(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+			     dev, priv);
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
+
+	return icnss_hw_power_on(priv);
+}
+EXPORT_SYMBOL(icnss_power_on);
+
+int icnss_power_off(struct device *dev)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv) {
+		icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
+			     dev, priv);
+		return -EINVAL;
+	}
+
+	icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
+
+	return icnss_hw_power_off(priv);
+}
+EXPORT_SYMBOL(icnss_power_off);
+
+void icnss_put_resources(struct icnss_priv *priv)
+{
+	icnss_put_clk(priv);
+	icnss_put_vreg(priv);
+}
+
+static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
+{
+	int ret = 0;
+	int result;
+
+	if (!priv->channel) {
+		icnss_pr_err("Channel doesn't exists\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = iio_read_channel_processed(priv->channel, &result);
+	if (ret < 0) {
+		icnss_pr_err("Error reading channel, ret = %d\n", ret);
+		goto out;
+	}
+
+	*result_uv = (uint64_t)result;
+out:
+	return ret;
+}
+
+static void icnss_vph_notify(enum adc_tm_state state, void *ctx)
+{
+	struct icnss_priv *priv = ctx;
+	u64 vph_pwr = 0;
+	u64 vph_pwr_prev;
+	int ret = 0;
+	bool update = true;
+
+	if (!priv) {
+		icnss_pr_err("Priv pointer is NULL\n");
+		return;
+	}
+
+	vph_pwr_prev = priv->vph_pwr;
+
+	ret = icnss_get_phone_power(priv, &vph_pwr);
+	if (ret < 0)
+		return;
+
+	if (vph_pwr < ICNSS_THRESHOLD_LOW) {
+		if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
+			update = false;
+		priv->vph_monitor_params.state_request =
+			ADC_TM_HIGH_THR_ENABLE;
+		priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
+			ICNSS_THRESHOLD_GUARD;
+		priv->vph_monitor_params.low_thr = 0;
+	} else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
+		if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
+			update = false;
+		priv->vph_monitor_params.state_request =
+			ADC_TM_LOW_THR_ENABLE;
+		priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
+			ICNSS_THRESHOLD_GUARD;
+		priv->vph_monitor_params.high_thr = 0;
+	} else {
+		if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
+		    vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
+			update = false;
+		priv->vph_monitor_params.state_request =
+			ADC_TM_HIGH_LOW_THR_ENABLE;
+		priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+		priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+	}
+
+	priv->vph_pwr = vph_pwr;
+
+	if (update) {
+		icnss_send_vbatt_update(priv, vph_pwr);
+		icnss_pr_dbg("set low threshold to %d, high threshold to %d Phone power=%llu\n",
+			     priv->vph_monitor_params.low_thr,
+			     priv->vph_monitor_params.high_thr, vph_pwr);
+	}
+
+	ret = adc_tm_channel_measure(priv->adc_tm_dev,
+				      &priv->vph_monitor_params);
+	if (ret)
+		icnss_pr_err("TM channel setup failed %d\n", ret);
+}
+
+static int icnss_setup_vph_monitor(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	if (!priv->adc_tm_dev) {
+		icnss_pr_err("ADC TM handler is NULL\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+	priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+	priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+	priv->vph_monitor_params.channel = ADC5_VBAT_SNS;
+	priv->vph_monitor_params.btm_ctx = priv;
+	priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
+	icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
+		     priv->vph_monitor_params.low_thr,
+		     priv->vph_monitor_params.high_thr);
+
+	ret = adc_tm_channel_measure(priv->adc_tm_dev,
+				      &priv->vph_monitor_params);
+	if (ret)
+		icnss_pr_err("TM channel setup failed %d\n", ret);
+out:
+	return ret;
+}
+
+int icnss_init_vph_monitor(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	ret = icnss_get_phone_power(priv, &priv->vph_pwr);
+	if (ret < 0)
+		goto out;
+
+	icnss_pr_dbg("Phone power=%llu\n", priv->vph_pwr);
+
+	icnss_send_vbatt_update(priv, priv->vph_pwr);
+
+	ret = icnss_setup_vph_monitor(priv);
+	if (ret)
+		goto out;
+out:
+	return ret;
+}
+
+int icnss_aop_mbox_init(struct icnss_priv *priv)
+{
+	struct mbox_client *mbox = &priv->mbox_client_data;
+	struct mbox_chan *chan;
+	int ret = 0;
+
+	ret = of_property_read_string(priv->pdev->dev.of_node,
+				      "qcom,vreg_ol_cpr",
+				      &priv->cpr_info.vreg_ol_cpr);
+	if (ret) {
+		icnss_pr_dbg("Vreg for OL CPR not configured\n");
+		return -EINVAL;
+	}
+
+	mbox->dev = &priv->pdev->dev;
+	mbox->tx_block = true;
+	mbox->tx_tout = ICNSS_MBOX_TIMEOUT_MS;
+	mbox->knows_txdone = false;
+
+	priv->mbox_chan = NULL;
+	chan = mbox_request_channel(mbox, 0);
+	if (IS_ERR(chan)) {
+		ret = PTR_ERR(chan);
+		icnss_pr_err("Failed to get mbox channel with err %d\n", ret);
+		return ret;
+	}
+	priv->mbox_chan = chan;
+
+	icnss_pr_dbg("Mbox channel initialized\n");
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_MSM_QMP)
+static int icnss_aop_set_vreg_param(struct icnss_priv *priv,
+				    const char *vreg_name,
+				    enum icnss_vreg_param param,
+				    enum icnss_tcs_seq seq, int val)
+{
+	struct qmp_pkt pkt;
+	char mbox_msg[ICNSS_MBOX_MSG_MAX_LEN];
+	static const char * const vreg_param_str[] = {"v", "m", "e"};
+	static const char *const tcs_seq_str[] = {"upval", "dwnval", "enable"};
+	int ret = 0;
+
+	if (param > ICNSS_VREG_ENABLE || seq > ICNSS_TCS_ALL_SEQ || !vreg_name)
+		return -EINVAL;
+
+	snprintf(mbox_msg, ICNSS_MBOX_MSG_MAX_LEN,
+		 "{class: wlan_pdc, res: %s.%s, %s: %d}", vreg_name,
+		 vreg_param_str[param], tcs_seq_str[seq], val);
+
+	icnss_pr_dbg("Sending AOP Mbox msg: %s\n", mbox_msg);
+	pkt.size = ICNSS_MBOX_MSG_MAX_LEN;
+	pkt.data = mbox_msg;
+
+	ret = mbox_send_message(priv->mbox_chan, &pkt);
+	if (ret < 0)
+		icnss_pr_err("Failed to send AOP mbox msg: %s,ret: %d\n",
+			     mbox_msg, ret);
+	else
+		ret = 0;
+
+	return ret;
+}
+#else
+static int icnss_aop_set_vreg_param(struct icnss_priv *priv,
+				    const char *vreg_name,
+				    enum icnss_vreg_param param,
+				    enum icnss_tcs_seq seq, int val)
+{
+	return 0;
+}
+#endif
+
+int icnss_update_cpr_info(struct icnss_priv *priv)
+{
+	struct icnss_cpr_info *cpr_info = &priv->cpr_info;
+
+	if (!cpr_info->vreg_ol_cpr || !priv->mbox_chan) {
+		icnss_pr_dbg("Mbox channel / OL CPR Vreg not configured\n");
+		return 0;
+	}
+
+	if (cpr_info->voltage == 0) {
+		icnss_pr_err("Voltage %dmV is not valid\n", cpr_info->voltage);
+		return -EINVAL;
+	}
+
+	cpr_info->voltage = cpr_info->voltage > BT_CXMX_VOLTAGE_MV ?
+		cpr_info->voltage : BT_CXMX_VOLTAGE_MV;
+
+	return icnss_aop_set_vreg_param(priv,
+				       cpr_info->vreg_ol_cpr,
+				       ICNSS_VREG_VOLTAGE,
+				       ICNSS_TCS_UP_SEQ,
+				       cpr_info->voltage);
+}

+ 19 - 0
icnss2/power.h

@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __ICNSS_POWER_H__
+#define __ICNSS_POWER_H__
+
+int icnss_hw_power_on(struct icnss_priv *priv);
+int icnss_hw_power_off(struct icnss_priv *priv);
+int icnss_get_clk(struct icnss_priv *priv);
+int icnss_get_vreg(struct icnss_priv *priv);
+int icnss_init_vph_monitor(struct icnss_priv *priv);
+void icnss_put_resources(struct icnss_priv *priv);
+void icnss_put_vreg(struct icnss_priv *priv);
+void icnss_put_clk(struct icnss_priv *priv);
+int icnss_vreg_unvote(struct icnss_priv *priv);
+
+#endif

+ 3469 - 0
icnss2/qmi.c

@@ -0,0 +1,3469 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "icnss2_qmi: " fmt
+
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ipc_logging.h>
+#include <linux/thread_info.h>
+#include <linux/firmware.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/icnss2.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/of_common.h>
+#include "wlan_firmware_service_v01.h"
+#include "main.h"
+#include "qmi.h"
+#include "debug.h"
+#include "genl.h"
+
+#define WLFW_SERVICE_WCN_INS_ID_V01	3
+#define WLFW_SERVICE_INS_ID_V01		0
+#define WLFW_CLIENT_ID			0x4b4e454c
+#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED	0x77
+
+#define BDF_FILE_NAME_PREFIX		"bdwlan"
+#define ELF_BDF_FILE_NAME		"bdwlan.elf"
+#define ELF_BDF_FILE_NAME_PREFIX	"bdwlan.e"
+#define BIN_BDF_FILE_NAME		"bdwlan.bin"
+#define BIN_BDF_FILE_NAME_PREFIX	"bdwlan."
+#define REGDB_FILE_NAME			"regdb.bin"
+#define DUMMY_BDF_FILE_NAME		"bdwlan.dmy"
+
+#define QDSS_TRACE_CONFIG_FILE "qdss_trace_config.cfg"
+
+#define WLAN_BOARD_ID_INDEX		0x100
+#define DEVICE_BAR_SIZE			0x200000
+#define M3_SEGMENT_ADDR_MASK		0xFFFFFFFF
+#define DMS_QMI_MAX_MSG_LEN		SZ_256
+#define DMS_MAC_NOT_PROVISIONED		16
+#define BDWLAN_SIZE			6
+#define UMC_CHIP_ID                    0x4320
+
+#ifdef CONFIG_ICNSS2_DEBUG
+bool ignore_fw_timeout;
+#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_fw_timeout)
+#else
+#define ICNSS_QMI_ASSERT() do { } while (0)
+#endif
+
+#ifdef CONFIG_ICNSS2_DEBUG
+void icnss_ignore_fw_timeout(bool ignore)
+{
+	ignore_fw_timeout = ignore;
+}
+#else
+void icnss_ignore_fw_timeout(bool ignore) { }
+#endif
+
+#define icnss_qmi_fatal_err(_fmt, ...) do {		\
+	icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__);	\
+	ICNSS_QMI_ASSERT();				\
+	} while (0)
+
+int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	int i;
+	struct wlfw_msa_info_req_msg_v01 *req;
+	struct wlfw_msa_info_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	uint64_t max_mapped_addr;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->msa_addr = priv->msa_pa;
+	req->size = priv->msa_mem_size;
+
+	priv->stats.msa_info_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_msa_info_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"Fail to init txn for MSA Mem info resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_MSA_INFO_REQ_V01,
+			       WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_msa_info_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send MSA Mem info req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("MSA Mem info resp wait failed ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+		"QMI MSA Mem info request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	icnss_pr_dbg("Receive mem_region_info_len: %d\n",
+		     resp->mem_region_info_len);
+
+	if (resp->mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
+		icnss_qmi_fatal_err(
+			"Invalid memory region length received: %d\n",
+			 resp->mem_region_info_len);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	max_mapped_addr = priv->msa_pa + priv->msa_mem_size;
+	priv->stats.msa_info_resp++;
+	priv->nr_mem_region = resp->mem_region_info_len;
+	for (i = 0; i < resp->mem_region_info_len; i++) {
+
+		if (resp->mem_region_info[i].size > priv->msa_mem_size ||
+		    resp->mem_region_info[i].region_addr >= max_mapped_addr ||
+		    resp->mem_region_info[i].region_addr < priv->msa_pa ||
+		    resp->mem_region_info[i].size +
+		    resp->mem_region_info[i].region_addr > max_mapped_addr) {
+			icnss_pr_dbg("Received out of range Addr: 0x%llx Size: 0x%x\n",
+					resp->mem_region_info[i].region_addr,
+					resp->mem_region_info[i].size);
+			ret = -EINVAL;
+			goto fail_unwind;
+		}
+
+		priv->mem_region[i].reg_addr =
+			resp->mem_region_info[i].region_addr;
+		priv->mem_region[i].size =
+			resp->mem_region_info[i].size;
+		priv->mem_region[i].secure_flag =
+			resp->mem_region_info[i].secure_flag;
+		icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
+			     i, priv->mem_region[i].reg_addr,
+			     priv->mem_region[i].size,
+			     priv->mem_region[i].secure_flag);
+	}
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+fail_unwind:
+	memset(&priv->mem_region[0], 0, sizeof(priv->mem_region[0]) * i);
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.msa_info_err++;
+	return ret;
+}
+
+int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_msa_ready_req_msg_v01 *req;
+	struct wlfw_msa_ready_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	priv->stats.msa_ready_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_msa_ready_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"Fail to init txn for MSA Mem Ready resp %d\n",
+				ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_MSA_READY_REQ_V01,
+			       WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_msa_ready_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send MSA Mem Ready req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"MSA Mem Ready resp wait failed with ret %d\n",
+				ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+		   "QMI MSA Mem Ready request rejected, result:%d error:%d\n",
+		   resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.msa_ready_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.msa_ready_err++;
+	return ret;
+}
+
+int wlfw_device_info_send_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_device_info_req_msg_v01 *req;
+	struct wlfw_device_info_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending Device Info request message, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	priv->stats.device_info_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_device_info_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"Fail to init txn for Device Info resp %d\n",
+				ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_DEVICE_INFO_REQ_V01,
+			       WLFW_DEVICE_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_device_info_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send device info req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"Device Info resp wait failed with ret %d\n",
+				ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+		   "QMI Device info request rejected, result:%d error:%d\n",
+		   resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.device_info_resp++;
+
+	if (resp->bar_addr_valid)
+		priv->mem_base_pa = resp->bar_addr;
+
+	if (resp->bar_size_valid)
+		priv->mem_base_size = resp->bar_size;
+
+	if (!priv->mem_base_pa) {
+		ret = -EINVAL;
+		icnss_qmi_fatal_err("Fail to get bar address\n");
+		goto out;
+	}
+
+	if (priv->mem_base_size <  DEVICE_BAR_SIZE) {
+		ret = -EINVAL;
+		icnss_qmi_fatal_err("Bar size is not proper 0x%x\n",
+				    priv->mem_base_size);
+		goto out;
+	}
+
+	if (resp->mhi_state_info_addr_valid)
+		priv->mhi_state_info_pa = resp->mhi_state_info_addr;
+
+	if (resp->mhi_state_info_size_valid)
+		priv->mhi_state_info_size = resp->mhi_state_info_size;
+
+	if (!priv->mhi_state_info_pa)
+		icnss_pr_err("Fail to get MHI info address\n");
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.device_info_err++;
+	return ret;
+}
+
+int wlfw_power_save_send_msg(struct icnss_priv *priv,
+			     enum wlfw_power_save_mode_v01 mode)
+{
+	int ret;
+	struct wlfw_power_save_req_msg_v01 *req;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
+	    !test_bit(ICNSS_MODE_ON, &priv->state))
+		return 0;
+
+	icnss_pr_dbg("Sending power save mode: %d, state: 0x%lx\n",
+		     mode, priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->power_save_mode_valid = 1;
+	req->power_save_mode = mode;
+
+	if (mode == WLFW_POWER_SAVE_EXIT_V01)
+		priv->stats.exit_power_save_req++;
+	else
+		priv->stats.enter_power_save_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   NULL, NULL);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for exit power save%d\n",
+				    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_POWER_SAVE_REQ_V01,
+			       WLFW_POWER_SAVE_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_power_save_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send exit power save req %d\n",
+				    ret);
+		goto out;
+	}
+
+	qmi_txn_cancel(&txn);
+
+	if (mode == WLFW_POWER_SAVE_EXIT_V01)
+		priv->stats.exit_power_save_resp++;
+	else
+		priv->stats.enter_power_save_resp++;
+
+	kfree(req);
+	return 0;
+
+out:
+	kfree(req);
+
+	if (mode == WLFW_POWER_SAVE_EXIT_V01)
+		priv->stats.exit_power_save_err++;
+	else
+		priv->stats.enter_power_save_err++;
+	return ret;
+}
+
+int wlfw_send_soc_wake_msg(struct icnss_priv *priv,
+			   enum wlfw_soc_wake_enum_v01 type)
+{
+	int ret;
+	struct wlfw_soc_wake_req_msg_v01 *req;
+	struct wlfw_soc_wake_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_soc_wake("Sending soc wake msg, type: 0x%x\n",
+		     type);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+	req->wake_valid = 1;
+	req->wake = type;
+
+	priv->stats.soc_wake_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_soc_wake_resp_msg_v01_ei, resp);
+
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for wake msg resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_SOC_WAKE_REQ_V01,
+			       WLFW_SOC_WAKE_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_soc_wake_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send soc wake msg %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("SOC wake timed out with ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+			"SOC wake request rejected,result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.soc_wake_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(req);
+	kfree(resp);
+	priv->stats.soc_wake_err++;
+	return ret;
+}
+
+int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_ind_register_req_msg_v01 *req;
+	struct wlfw_ind_register_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->client_id_valid = 1;
+	req->client_id = WLFW_CLIENT_ID;
+	req->fw_ready_enable_valid = 1;
+	req->fw_ready_enable = 1;
+	req->pin_connect_result_enable_valid = 1;
+	req->pin_connect_result_enable = 1;
+
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		req->msa_ready_enable_valid = 1;
+		req->msa_ready_enable = 1;
+		if (test_bit(FW_REJUVENATE_ENABLE,
+			 &priv->ctrl_params.quirks)) {
+			req->rejuvenate_enable_valid = 1;
+			req->rejuvenate_enable = 1;
+		}
+	} else if (priv->device_id == WCN6750_DEVICE_ID) {
+		req->fw_init_done_enable_valid = 1;
+		req->fw_init_done_enable = 1;
+		req->cal_done_enable_valid = 1;
+		req->cal_done_enable = 1;
+		req->qdss_trace_req_mem_enable_valid = 1;
+		req->qdss_trace_req_mem_enable = 1;
+		req->qdss_trace_save_enable_valid = 1;
+		req->qdss_trace_save_enable = 1;
+		req->qdss_trace_free_enable_valid = 1;
+		req->qdss_trace_free_enable = 1;
+		req->respond_get_info_enable_valid = 1;
+		req->respond_get_info_enable = 1;
+		req->m3_dump_upload_segments_req_enable_valid = 1;
+		req->m3_dump_upload_segments_req_enable = 1;
+	}
+
+	priv->stats.ind_register_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_ind_register_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+				"Fail to init txn for Ind Register resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_IND_REGISTER_REQ_V01,
+			       WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_ind_register_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send Ind Register req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+			"Ind Register resp wait failed with ret %d\n",
+			 ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+		    "QMI Ind Register request rejected, result:%d error:%d\n",
+		     resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.ind_register_resp++;
+
+	if (resp->fw_status_valid &&
+	   (resp->fw_status & QMI_WLFW_ALREADY_REGISTERED_V01)) {
+		ret = -EALREADY;
+		icnss_pr_dbg("WLFW already registered\n");
+		goto qmi_registered;
+	}
+
+	kfree(resp);
+	kfree(req);
+
+	return 0;
+
+out:
+	priv->stats.ind_register_err++;
+qmi_registered:
+	kfree(resp);
+	kfree(req);
+	return ret;
+}
+
+int wlfw_cal_report_req(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_cal_report_req_msg_v01 *req;
+	struct wlfw_cal_report_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_info("Sending cal report request, state: 0x%lx\n",
+		      priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+	req->meta_data_len = 0;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_cal_report_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for cal report req %d\n",
+				    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_CAL_REPORT_REQ_V01,
+			       WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_cal_report_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send cal report req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn,
+			   priv->ctrl_params.qmi_timeout);
+
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Cal report wait failed with ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err("QMI cal report request rejected, result:%d error:%d\n",
+				    resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	kfree(resp);
+	kfree(req);
+
+	return 0;
+
+out:
+	return ret;
+}
+
+int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_cap_req_msg_v01 *req;
+	struct wlfw_cap_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending target capability message, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	priv->stats.cap_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn, wlfw_cap_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for Capability resp %d\n",
+				    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_CAP_REQ_V01,
+			       WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_cap_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send Capability req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn,
+			   priv->ctrl_params.qmi_timeout +
+			   msecs_to_jiffies(priv->wlan_en_delay_ms));
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Capability resp wait failed with ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		ret = -resp->resp.result;
+		if (resp->resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED) {
+			icnss_pr_err("RF card not present\n");
+			goto out;
+		}
+		icnss_qmi_fatal_err(
+			"QMI Capability request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		goto out;
+	}
+
+	priv->stats.cap_resp++;
+
+	if (resp->chip_info_valid) {
+		priv->chip_info.chip_id = resp->chip_info.chip_id;
+		priv->chip_info.chip_family = resp->chip_info.chip_family;
+	}
+	if (resp->board_info_valid)
+		priv->board_id = resp->board_info.board_id;
+	else
+		priv->board_id = 0xFF;
+	if (resp->soc_info_valid)
+		priv->soc_id = resp->soc_info.soc_id;
+	if (resp->fw_version_info_valid) {
+		priv->fw_version_info.fw_version =
+			resp->fw_version_info.fw_version;
+		strlcpy(priv->fw_version_info.fw_build_timestamp,
+				resp->fw_version_info.fw_build_timestamp,
+				WLFW_MAX_TIMESTAMP_LEN + 1);
+	}
+
+	if (resp->voltage_mv_valid) {
+		priv->cpr_info.voltage = resp->voltage_mv;
+		icnss_pr_dbg("Voltage for CPR: %dmV\n",
+			    priv->cpr_info.voltage);
+		icnss_update_cpr_info(priv);
+	}
+
+	if (resp->fw_build_id_valid)
+		strlcpy(priv->fw_build_id, resp->fw_build_id,
+			QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1);
+
+	if (resp->rd_card_chain_cap_valid &&
+	    resp->rd_card_chain_cap == WLFW_RD_CARD_CHAIN_CAP_1x1_V01)
+		priv->is_chain1_supported = false;
+
+	if (resp->foundry_name_valid)
+		priv->foundry_name = resp->foundry_name[0];
+	else if (resp->chip_info_valid && priv->chip_info.chip_id == UMC_CHIP_ID)
+		priv->foundry_name = 'u';
+
+	icnss_pr_dbg("Capability, chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x",
+		     priv->chip_info.chip_id, priv->chip_info.chip_family,
+		     priv->board_id, priv->soc_id);
+
+	icnss_pr_dbg("fw_version: 0x%x, fw_build_timestamp: %s, fw_build_id: %s",
+		     priv->fw_version_info.fw_version,
+		     priv->fw_version_info.fw_build_timestamp,
+		     priv->fw_build_id);
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.cap_err++;
+	return ret;
+}
+
+int icnss_qmi_get_dms_mac(struct icnss_priv *priv)
+{
+	struct dms_get_mac_address_req_msg_v01 req;
+	struct dms_get_mac_address_resp_msg_v01 resp;
+	struct qmi_txn txn;
+	int ret = 0;
+
+	if  (!test_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state)) {
+		icnss_pr_err("DMS QMI connection not established\n");
+		return -EAGAIN;
+	}
+	icnss_pr_dbg("Requesting DMS MAC address");
+
+	memset(&resp, 0, sizeof(resp));
+	ret = qmi_txn_init(&priv->qmi_dms, &txn,
+			   dms_get_mac_address_resp_msg_v01_ei, &resp);
+	if (ret < 0) {
+		icnss_pr_err("Failed to initialize txn for dms, err: %d\n",
+			     ret);
+		goto out;
+	}
+	req.device = DMS_DEVICE_MAC_WLAN_V01;
+	ret = qmi_send_request(&priv->qmi_dms, NULL, &txn,
+			       QMI_DMS_GET_MAC_ADDRESS_REQ_V01,
+			       DMS_GET_MAC_ADDRESS_REQ_MSG_V01_MAX_MSG_LEN,
+			       dms_get_mac_address_req_msg_v01_ei, &req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Failed to send QMI_DMS_GET_MAC_ADDRESS_REQ_V01, err: %d\n",
+			     ret);
+		goto out;
+	}
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Failed to wait for QMI_DMS_GET_MAC_ADDRESS_RESP_V01, err: %d\n",
+			     ret);
+		goto out;
+	}
+
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		if (resp.resp.error == DMS_MAC_NOT_PROVISIONED) {
+			icnss_pr_err("NV MAC address is not provisioned");
+			priv->dms.nv_mac_not_prov = 1;
+		} else {
+			icnss_pr_err("QMI_DMS_GET_MAC_ADDRESS_REQ_V01 failed, result: %d, err: %d\n",
+				     resp.resp.result, resp.resp.error);
+		}
+		ret = -resp.resp.result;
+		goto out;
+	}
+	if (!resp.mac_address_valid ||
+	    resp.mac_address_len != QMI_WLFW_MAC_ADDR_SIZE_V01) {
+		icnss_pr_err("Invalid MAC address received from DMS\n");
+		priv->dms.mac_valid = false;
+		goto out;
+	}
+	priv->dms.mac_valid = true;
+	memcpy(priv->dms.mac, resp.mac_address, QMI_WLFW_MAC_ADDR_SIZE_V01);
+	icnss_pr_info("Received DMS MAC: [%pM]\n", priv->dms.mac);
+out:
+	return ret;
+}
+
+int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv,
+				      u8 *mac, u32 mac_len)
+{
+	struct wlfw_mac_addr_req_msg_v01 req;
+	struct wlfw_mac_addr_resp_msg_v01 resp = {0};
+	struct qmi_txn txn;
+	int ret;
+
+	if (!priv || !mac || mac_len != QMI_WLFW_MAC_ADDR_SIZE_V01)
+		return -EINVAL;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_mac_addr_resp_msg_v01_ei, &resp);
+	if (ret < 0) {
+		icnss_pr_err("Failed to initialize txn for mac req, err: %d\n",
+			     ret);
+		ret = -EIO;
+		goto out;
+	}
+
+	icnss_pr_dbg("Sending WLAN mac req [%pM], state: 0x%lx\n",
+			     mac, priv->state);
+	memcpy(req.mac_addr, mac, mac_len);
+	req.mac_addr_valid = 1;
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_MAC_ADDR_REQ_V01,
+			       WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_mac_addr_req_msg_v01_ei, &req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Failed to send mac req, err: %d\n", ret);
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Failed to wait for resp of mac req, err: %d\n",
+			     ret);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("WLAN mac req failed, result: %d, err: %d\n",
+			     resp.resp.result);
+		ret = -resp.resp.result;
+	}
+out:
+	return ret;
+}
+
+static int icnss_dms_connect_to_server(struct icnss_priv *priv,
+				      unsigned int node, unsigned int port)
+{
+	struct qmi_handle *qmi_dms = &priv->qmi_dms;
+	struct sockaddr_qrtr sq = {0};
+	int ret = 0;
+
+	sq.sq_family = AF_QIPCRTR;
+	sq.sq_node = node;
+	sq.sq_port = port;
+
+	ret = kernel_connect(qmi_dms->sock, (struct sockaddr *)&sq,
+			     sizeof(sq), 0);
+	if (ret < 0) {
+		icnss_pr_err("Failed to connect to QMI DMS remote service Node: %d Port: %d\n",
+			     node, port);
+		goto out;
+	}
+
+	set_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state);
+	icnss_pr_info("QMI DMS service connected, state: 0x%lx\n",
+		      priv->state);
+out:
+	return ret;
+}
+
+static int dms_new_server(struct qmi_handle *qmi_dms,
+			  struct qmi_service *service)
+{
+	struct icnss_priv *priv =
+		container_of(qmi_dms, struct icnss_priv, qmi_dms);
+
+	if (!service)
+		return -EINVAL;
+
+	return icnss_dms_connect_to_server(priv, service->node,
+					   service->port);
+}
+
+static void dms_del_server(struct qmi_handle *qmi_dms,
+			   struct qmi_service *service)
+{
+	struct icnss_priv *priv =
+		container_of(qmi_dms, struct icnss_priv, qmi_dms);
+
+	clear_bit(ICNSS_QMI_DMS_CONNECTED, &priv->state);
+	icnss_pr_info("QMI DMS service disconnected, state: 0x%lx\n",
+		      priv->state);
+}
+
+static struct qmi_ops qmi_dms_ops = {
+	.new_server = dms_new_server,
+	.del_server = dms_del_server,
+};
+
+int icnss_dms_init(struct icnss_priv *priv)
+{
+	int ret = 0;
+
+	ret = qmi_handle_init(&priv->qmi_dms, DMS_QMI_MAX_MSG_LEN,
+			      &qmi_dms_ops, NULL);
+	if (ret < 0) {
+		icnss_pr_err("Failed to initialize DMS handle, err: %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_add_lookup(&priv->qmi_dms, DMS_SERVICE_ID_V01,
+			     DMS_SERVICE_VERS_V01, 0);
+	if (ret < 0)
+		icnss_pr_err("Failed to add DMS lookup, err: %d\n", ret);
+out:
+	return ret;
+}
+
+void icnss_dms_deinit(struct icnss_priv *priv)
+{
+	qmi_handle_release(&priv->qmi_dms);
+}
+
+static int icnss_get_bdf_file_name(struct icnss_priv *priv,
+				   u32 bdf_type, char *filename,
+				   u32 filename_len)
+{
+	char filename_tmp[ICNSS_MAX_FILE_NAME];
+	char foundry_specific_filename[ICNSS_MAX_FILE_NAME];
+	int ret = 0;
+
+	switch (bdf_type) {
+	case ICNSS_BDF_ELF:
+		if (priv->board_id == 0xFF)
+			snprintf(filename_tmp, filename_len, ELF_BDF_FILE_NAME);
+		else if (priv->board_id < 0xFF)
+			snprintf(filename_tmp, filename_len,
+				 ELF_BDF_FILE_NAME_PREFIX "%02x",
+				 priv->board_id);
+		else
+			snprintf(filename_tmp, filename_len,
+				 BDF_FILE_NAME_PREFIX "%02x.e%02x",
+				 priv->board_id >> 8 & 0xFF,
+				 priv->board_id & 0xFF);
+		break;
+	case ICNSS_BDF_BIN:
+		if (priv->board_id == 0xFF)
+			snprintf(filename_tmp, filename_len, BIN_BDF_FILE_NAME);
+		else if (priv->board_id >= WLAN_BOARD_ID_INDEX)
+			snprintf(filename_tmp, filename_len,
+				 BIN_BDF_FILE_NAME_PREFIX "%03x",
+				 priv->board_id);
+		else
+			snprintf(filename_tmp, filename_len,
+				 BIN_BDF_FILE_NAME_PREFIX "b%02x",
+				 priv->board_id);
+		if (priv->foundry_name) {
+			strlcpy(foundry_specific_filename, filename_tmp, ICNSS_MAX_FILE_NAME);
+			memmove(foundry_specific_filename + BDWLAN_SIZE + 1,
+				foundry_specific_filename + BDWLAN_SIZE,
+				BDWLAN_SIZE - 1);
+			foundry_specific_filename[BDWLAN_SIZE] = priv->foundry_name;
+			foundry_specific_filename[ICNSS_MAX_FILE_NAME - 1] = '\0';
+			strlcpy(filename_tmp, foundry_specific_filename, ICNSS_MAX_FILE_NAME);
+		}
+		break;
+	case ICNSS_BDF_REGDB:
+		snprintf(filename_tmp, filename_len, REGDB_FILE_NAME);
+		break;
+	case ICNSS_BDF_DUMMY:
+		icnss_pr_dbg("CNSS_BDF_DUMMY is set, sending dummy BDF\n");
+		snprintf(filename_tmp, filename_len, DUMMY_BDF_FILE_NAME);
+		ret = ICNSS_MAX_FILE_NAME;
+		break;
+	default:
+		icnss_pr_err("Invalid BDF type: %d\n",
+			     priv->ctrl_params.bdf_type);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret >= 0)
+		icnss_add_fw_prefix_name(priv, filename, filename_tmp);
+
+	return ret;
+}
+
+static char *icnss_bdf_type_to_str(enum icnss_bdf_type bdf_type)
+{
+	switch (bdf_type) {
+	case ICNSS_BDF_BIN:
+		return "BDF";
+	case ICNSS_BDF_ELF:
+		return "BDF";
+	case ICNSS_BDF_REGDB:
+		return "REGDB";
+	case ICNSS_BDF_DUMMY:
+		return "BDF";
+	default:
+		return "UNKNOWN";
+	}
+};
+
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type)
+{
+	struct wlfw_bdf_download_req_msg_v01 *req;
+	struct wlfw_bdf_download_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	char filename[ICNSS_MAX_FILE_NAME];
+	const struct firmware *fw_entry = NULL;
+	const u8 *temp;
+	unsigned int remaining;
+	int ret = 0;
+
+	icnss_pr_dbg("Sending %s download message, state: 0x%lx, type: %d\n",
+		     icnss_bdf_type_to_str(bdf_type), priv->state, bdf_type);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	ret = icnss_get_bdf_file_name(priv, bdf_type,
+				      filename, sizeof(filename));
+	if (ret > 0) {
+		temp = DUMMY_BDF_FILE_NAME;
+		remaining = ICNSS_MAX_FILE_NAME;
+		goto bypass_bdf;
+	} else if (ret < 0) {
+		goto err_req_fw;
+	}
+
+	ret = request_firmware(&fw_entry, filename, &priv->pdev->dev);
+	if (ret) {
+		icnss_pr_err("Failed to load %s: %s ret:%d\n",
+			     icnss_bdf_type_to_str(bdf_type), filename, ret);
+		goto err_req_fw;
+	}
+
+	temp = fw_entry->data;
+	remaining = fw_entry->size;
+
+bypass_bdf:
+	icnss_pr_dbg("Downloading %s: %s, size: %u\n",
+		     icnss_bdf_type_to_str(bdf_type), filename, remaining);
+
+	while (remaining) {
+		req->valid = 1;
+		req->file_id_valid = 1;
+		req->file_id = priv->board_id;
+		req->total_size_valid = 1;
+		req->total_size = fw_entry->size;
+		req->seg_id_valid = 1;
+		req->data_valid = 1;
+		req->end_valid = 1;
+		req->bdf_type_valid = 1;
+		req->bdf_type = bdf_type;
+
+		if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
+			req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
+		} else {
+			req->data_len = remaining;
+			req->end = 1;
+		}
+
+		memcpy(req->data, temp, req->data_len);
+
+		ret = qmi_txn_init(&priv->qmi, &txn,
+				   wlfw_bdf_download_resp_msg_v01_ei, resp);
+		if (ret < 0) {
+			icnss_pr_err("Failed to initialize txn for %s download request, err: %d\n",
+				     icnss_bdf_type_to_str(bdf_type), ret);
+			goto err_send;
+		}
+
+		ret = qmi_send_request
+			(&priv->qmi, NULL, &txn,
+			 QMI_WLFW_BDF_DOWNLOAD_REQ_V01,
+			 WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
+			 wlfw_bdf_download_req_msg_v01_ei, req);
+		if (ret < 0) {
+			qmi_txn_cancel(&txn);
+			icnss_pr_err("Failed to send respond %s download request, err: %d\n",
+				     icnss_bdf_type_to_str(bdf_type), ret);
+			goto err_send;
+		}
+
+		ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+		if (ret < 0) {
+			icnss_pr_err("Failed to wait for response of %s download request, err: %d\n",
+				     icnss_bdf_type_to_str(bdf_type), ret);
+			goto err_send;
+		}
+
+		if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+			icnss_pr_err("%s download request failed, result: %d, err: %d\n",
+				     icnss_bdf_type_to_str(bdf_type), resp->resp.result,
+				     resp->resp.error);
+			ret = -resp->resp.result;
+			goto err_send;
+		}
+
+		remaining -= req->data_len;
+		temp += req->data_len;
+		req->seg_id++;
+	}
+
+	if (bdf_type != ICNSS_BDF_DUMMY)
+		release_firmware(fw_entry);
+
+	kfree(req);
+	kfree(resp);
+	return 0;
+
+err_send:
+	if (bdf_type != ICNSS_BDF_DUMMY)
+		release_firmware(fw_entry);
+err_req_fw:
+	if (bdf_type != ICNSS_BDF_REGDB)
+		ICNSS_QMI_ASSERT();
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name,
+				   u32 total_size)
+{
+	int ret = 0;
+	struct wlfw_qdss_trace_data_req_msg_v01 *req;
+	struct wlfw_qdss_trace_data_resp_msg_v01 *resp;
+	unsigned char *p_qdss_trace_data_temp, *p_qdss_trace_data = NULL;
+	unsigned int remaining;
+	struct qmi_txn txn;
+
+	icnss_pr_dbg("%s", __func__);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	p_qdss_trace_data = kzalloc(total_size, GFP_KERNEL);
+	if (!p_qdss_trace_data) {
+		ret = ENOMEM;
+		goto end;
+	}
+
+	remaining = total_size;
+	p_qdss_trace_data_temp = p_qdss_trace_data;
+	while (remaining && resp->end == 0) {
+		ret = qmi_txn_init(&priv->qmi, &txn,
+				   wlfw_qdss_trace_data_resp_msg_v01_ei, resp);
+
+		if (ret < 0) {
+			icnss_pr_err("Fail to init txn for QDSS trace resp %d\n",
+				     ret);
+			goto fail;
+		}
+
+		ret = qmi_send_request
+			(&priv->qmi, NULL, &txn,
+			 QMI_WLFW_QDSS_TRACE_DATA_REQ_V01,
+			 WLFW_QDSS_TRACE_DATA_REQ_MSG_V01_MAX_MSG_LEN,
+			 wlfw_qdss_trace_data_req_msg_v01_ei, req);
+
+		if (ret < 0) {
+			qmi_txn_cancel(&txn);
+			icnss_pr_err("Fail to send QDSS trace data req %d\n",
+				     ret);
+			goto fail;
+		}
+
+		ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+
+		if (ret < 0) {
+			icnss_pr_err("QDSS trace resp wait failed with rc %d\n",
+				     ret);
+			goto fail;
+		} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+			icnss_pr_err("QMI QDSS trace request rejected, result:%d error:%d\n",
+				     resp->resp.result, resp->resp.error);
+				     ret = -resp->resp.result;
+			goto fail;
+		} else {
+			ret = 0;
+		}
+
+		icnss_pr_dbg("%s: response total size  %d data len %d",
+			     __func__, resp->total_size, resp->data_len);
+
+		if ((resp->total_size_valid == 1 &&
+		     resp->total_size == total_size) &&
+		    (resp->seg_id_valid == 1 && resp->seg_id == req->seg_id) &&
+		    (resp->data_valid == 1 &&
+		     resp->data_len <= QMI_WLFW_MAX_DATA_SIZE_V01)) {
+			memcpy(p_qdss_trace_data_temp,
+			       resp->data, resp->data_len);
+		} else {
+			icnss_pr_err("%s: Unmatched qdss trace data, Expect total_size %u, seg_id %u, Recv total_size_valid %u, total_size %u, seg_id_valid %u, seg_id %u, data_len_valid %u, data_len %u",
+				     __func__,
+				     total_size, req->seg_id,
+				     resp->total_size_valid,
+				     resp->total_size,
+				     resp->seg_id_valid,
+				     resp->seg_id,
+				     resp->data_valid,
+				     resp->data_len);
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		remaining -= resp->data_len;
+		p_qdss_trace_data_temp += resp->data_len;
+		req->seg_id++;
+	}
+
+	if (remaining == 0 && (resp->end_valid && resp->end)) {
+		ret = icnss_genl_send_msg(p_qdss_trace_data,
+					  ICNSS_GENL_MSG_TYPE_QDSS, file_name,
+					  total_size);
+		if (ret < 0) {
+			icnss_pr_err("Fail to save QDSS trace data: %d\n",
+				     ret);
+		ret = -EINVAL;
+		}
+	} else {
+		icnss_pr_err("%s: QDSS trace file corrupted: remaining %u, end_valid %u, end %u",
+			     __func__,
+			     remaining, resp->end_valid, resp->end);
+		ret = -EINVAL;
+	}
+
+fail:
+	kfree(p_qdss_trace_data);
+
+end:
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv)
+{
+	struct wlfw_qdss_trace_config_download_req_msg_v01 *req;
+	struct wlfw_qdss_trace_config_download_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	char filename[ICNSS_MAX_FILE_NAME];
+	const struct firmware *fw_entry = NULL;
+	const u8 *temp;
+	unsigned int remaining;
+	int ret = 0;
+
+	icnss_pr_dbg("Sending QDSS config download message, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	icnss_add_fw_prefix_name(priv, filename, QDSS_TRACE_CONFIG_FILE);
+	ret = request_firmware(&fw_entry, filename,
+			       &priv->pdev->dev);
+	if (ret) {
+		icnss_pr_err("Failed to load QDSS: %s ret:%d\n",
+			     filename, ret);
+		goto err_req_fw;
+	}
+
+	temp = fw_entry->data;
+	remaining = fw_entry->size;
+
+	icnss_pr_dbg("Downloading QDSS: %s, size: %u\n",
+		     filename, remaining);
+
+	while (remaining) {
+		req->total_size_valid = 1;
+		req->total_size = remaining;
+		req->seg_id_valid = 1;
+		req->data_valid = 1;
+		req->end_valid = 1;
+
+		if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) {
+			req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01;
+		} else {
+			req->data_len = remaining;
+			req->end = 1;
+		}
+
+		memcpy(req->data, temp, req->data_len);
+
+		ret = qmi_txn_init
+			(&priv->qmi, &txn,
+			 wlfw_qdss_trace_config_download_resp_msg_v01_ei,
+			 resp);
+		if (ret < 0) {
+			icnss_pr_err("Failed to initialize txn for QDSS download request, err: %d\n",
+				     ret);
+			goto err_send;
+		}
+
+		ret = qmi_send_request
+		      (&priv->qmi, NULL, &txn,
+		       QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01,
+		       WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
+		       wlfw_qdss_trace_config_download_req_msg_v01_ei, req);
+		if (ret < 0) {
+			qmi_txn_cancel(&txn);
+			icnss_pr_err("Failed to send respond QDSS download request, err: %d\n",
+				     ret);
+			goto err_send;
+		}
+
+		ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+		if (ret < 0) {
+			icnss_pr_err("Failed to wait for response of QDSS download request, err: %d\n",
+				     ret);
+			goto err_send;
+		}
+
+		if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+			icnss_pr_err("QDSS download request failed, result: %d, err: %d\n",
+				     resp->resp.result, resp->resp.error);
+			ret = -resp->resp.result;
+			goto err_send;
+		}
+
+		remaining -= req->data_len;
+		temp += req->data_len;
+		req->seg_id++;
+	}
+
+	release_firmware(fw_entry);
+	kfree(req);
+	kfree(resp);
+	return 0;
+
+err_send:
+	release_firmware(fw_entry);
+err_req_fw:
+
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+		enum wlfw_driver_mode_enum_v01 mode)
+{
+	int ret;
+	struct wlfw_wlan_mode_req_msg_v01 *req;
+	struct wlfw_wlan_mode_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	/* During recovery do not send mode request for WLAN OFF as
+	 * FW not able to process it.
+	 */
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) &&
+	    mode == QMI_WLFW_OFF_V01)
+		return 0;
+
+	if (!test_bit(ICNSS_MODE_ON, &priv->state) &&
+	    mode == QMI_WLFW_OFF_V01)
+		return 0;
+
+	icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
+		     priv->state, mode);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->mode = mode;
+	req->hw_debug_valid = 1;
+	req->hw_debug = !!test_bit(HW_DEBUG_ENABLE, &priv->ctrl_params.quirks);
+
+	priv->stats.mode_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_wlan_mode_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for Mode resp %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_WLAN_MODE_REQ_V01,
+			       WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_wlan_mode_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send Mode req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn,
+			   priv->ctrl_params.qmi_timeout +
+			   msecs_to_jiffies(priv->wlan_en_delay_ms));
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Mode resp wait failed with ret %d\n", ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+			"QMI Mode request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.mode_resp++;
+
+	if (mode == QMI_WLFW_OFF_V01) {
+		icnss_pr_dbg("Clear mode on 0x%lx, mode: %d\n",
+			     priv->state, mode);
+		clear_bit(ICNSS_MODE_ON, &priv->state);
+	} else {
+		icnss_pr_dbg("Set mode on 0x%lx, mode: %d\n",
+			     priv->state, mode);
+		set_bit(ICNSS_MODE_ON, &priv->state);
+	}
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.mode_req_err++;
+	return ret;
+}
+
+static int wlfw_send_qdss_trace_mode_req
+		(struct icnss_priv *priv,
+		 enum wlfw_qdss_trace_mode_enum_v01 mode,
+		 unsigned long long option)
+{
+	int rc = 0;
+	int tmp = 0;
+	struct wlfw_qdss_trace_mode_req_msg_v01 *req;
+	struct wlfw_qdss_trace_mode_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->mode_valid = 1;
+	req->mode = mode;
+	req->option_valid = 1;
+	req->option = option;
+
+	tmp = priv->hw_trc_override;
+
+	req->hw_trc_disable_override_valid = 1;
+	req->hw_trc_disable_override =
+	(tmp > QMI_PARAM_DISABLE_V01 ? QMI_PARAM_DISABLE_V01 :
+		 (tmp < 0 ? QMI_PARAM_INVALID_V01 : tmp));
+
+	icnss_pr_dbg("%s: mode %u, option %llu, hw_trc_disable_override: %u",
+		     __func__, mode, option, req->hw_trc_disable_override);
+
+	rc = qmi_txn_init(&priv->qmi, &txn,
+			  wlfw_qdss_trace_mode_resp_msg_v01_ei, resp);
+	if (rc < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for QDSS Mode resp %d\n",
+				    rc);
+		goto out;
+	}
+
+	rc = qmi_send_request(&priv->qmi, NULL, &txn,
+			      QMI_WLFW_QDSS_TRACE_MODE_REQ_V01,
+			      WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN,
+			      wlfw_qdss_trace_mode_req_msg_v01_ei, req);
+	if (rc < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send QDSS Mode req %d\n", rc);
+		goto out;
+	}
+
+	rc = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (rc < 0) {
+		icnss_qmi_fatal_err("QDSS Mode resp wait failed with rc %d\n",
+				    rc);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+			"QMI QDSS Mode request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		rc = -resp->resp.result;
+		goto out;
+	}
+
+out:
+	kfree(resp);
+	kfree(req);
+	return rc;
+}
+
+int wlfw_qdss_trace_start(struct icnss_priv *priv)
+{
+	return wlfw_send_qdss_trace_mode_req(priv,
+					     QMI_WLFW_QDSS_TRACE_ON_V01, 0);
+}
+
+int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option)
+{
+	return wlfw_send_qdss_trace_mode_req(priv, QMI_WLFW_QDSS_TRACE_OFF_V01,
+					     option);
+}
+
+int wlfw_wlan_cfg_send_sync_msg(struct icnss_priv *priv,
+				struct wlfw_wlan_cfg_req_msg_v01 *data)
+{
+	int ret;
+	struct wlfw_wlan_cfg_req_msg_v01 *req;
+	struct wlfw_wlan_cfg_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending config request, state: 0x%lx\n", priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	memcpy(req, data, sizeof(*req));
+
+	priv->stats.cfg_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_wlan_cfg_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for Config resp %d\n",
+				    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_WLAN_CFG_REQ_V01,
+			       WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_wlan_cfg_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send Config req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Config resp wait failed with ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+			"QMI Config request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.cfg_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.cfg_req_err++;
+	return ret;
+}
+
+int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_shutdown_req_msg_v01 *req;
+	struct wlfw_shutdown_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_dbg("Sending modem shutdown request, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->shutdown_valid = 1;
+	req->shutdown = 1;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_shutdown_resp_msg_v01_ei, resp);
+
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for shutdown resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_SHUTDOWN_REQ_V01,
+			       WLFW_SHUTDOWN_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_shutdown_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send Shutdown req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Shutdown resp wait failed with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI modem shutdown request rejected result:%d error:%d\n",
+			     resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+out:
+	kfree(resp);
+	kfree(req);
+	return ret;
+}
+
+int wlfw_ini_send_sync_msg(struct icnss_priv *priv, uint8_t fw_log_mode)
+{
+	int ret;
+	struct wlfw_ini_req_msg_v01 *req;
+	struct wlfw_ini_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
+		     priv->state, fw_log_mode);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->enablefwlog_valid = 1;
+	req->enablefwlog = fw_log_mode;
+
+	priv->stats.ini_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn, wlfw_ini_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("Fail to init txn for INI resp %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_INI_REQ_V01,
+			       WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_ini_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send INI req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err("INI resp wait failed with ret %d\n", ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+			"QMI INI request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.ini_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.ini_req_err++;
+	return ret;
+}
+
+int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+					   uint32_t offset, uint32_t mem_type,
+					   uint32_t data_len, uint8_t *data)
+{
+	int ret;
+	struct wlfw_athdiag_read_req_msg_v01 *req;
+	struct wlfw_athdiag_read_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
+		     priv->state, offset, mem_type, data_len);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return  -ENOMEM;
+	}
+
+	req->offset = offset;
+	req->mem_type = mem_type;
+	req->data_len = data_len;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_athdiag_read_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for Athdiag Read resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_ATHDIAG_READ_REQ_V01,
+			       WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_athdiag_read_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send Athdiag Read req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Athdaig Read resp wait failed with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI Athdiag Read request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	} else {
+		ret = 0;
+	}
+
+	if (!resp->data_valid || resp->data_len < data_len) {
+		icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
+			     resp->data_valid, resp->data_len);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	memcpy(data, resp->data, resp->data_len);
+
+out:
+	kfree(resp);
+	kfree(req);
+	return ret;
+}
+
+int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+					    uint32_t offset, uint32_t mem_type,
+					    uint32_t data_len, uint8_t *data)
+{
+	int ret;
+	struct wlfw_athdiag_write_req_msg_v01 *req;
+	struct wlfw_athdiag_write_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %pK\n",
+		     priv->state, offset, mem_type, data_len, data);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->offset = offset;
+	req->mem_type = mem_type;
+	req->data_len = data_len;
+	memcpy(req->data, data, data_len);
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_athdiag_write_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for Athdiag Write resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_ATHDIAG_WRITE_REQ_V01,
+			       WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_athdiag_write_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send Athdiag Write req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Athdiag Write resp wait failed with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI Athdiag Write request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	} else {
+		ret = 0;
+	}
+
+out:
+	kfree(resp);
+	kfree(req);
+	return ret;
+}
+
+int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_rejuvenate_ack_req_msg_v01 *req;
+	struct wlfw_rejuvenate_ack_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	priv->stats.rejuvenate_ack_req++;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_rejuvenate_ack_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+			"Fail to init txn for Rejuvenate Ack resp %d\n",
+			 ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_REJUVENATE_ACK_REQ_V01,
+			       WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_rejuvenate_ack_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_qmi_fatal_err("Fail to send Rejuvenate Ack req %d\n",
+				    ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_qmi_fatal_err(
+			     "Rejuvenate Ack resp wait failed with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_qmi_fatal_err(
+		   "QMI Rejuvenate Ack request rejected, result:%d error:%d\n",
+		    resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.rejuvenate_ack_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.rejuvenate_ack_err++;
+	return ret;
+}
+
+int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
+					   uint64_t dynamic_feature_mask)
+{
+	int ret;
+	struct wlfw_dynamic_feature_mask_req_msg_v01 *req;
+	struct wlfw_dynamic_feature_mask_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (!test_bit(ICNSS_WLFW_CONNECTED, &priv->state)) {
+		icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
+			     priv->state);
+		return -EINVAL;
+	}
+
+	if (!test_bit(FW_REJUVENATE_ENABLE, &priv->ctrl_params.quirks)) {
+		icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
+		return 0;
+	}
+
+	icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
+		     dynamic_feature_mask, priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->mask_valid = 1;
+	req->mask = dynamic_feature_mask;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_dynamic_feature_mask_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for Dynamic Feature Mask resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+		       QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01,
+		       WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN,
+		       wlfw_dynamic_feature_mask_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send Dynamic Feature Mask req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Dynamic Feature Mask resp wait failed with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI Dynamic Feature Mask request rejected, result:%d error:%d\n",
+			resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
+		     resp->prev_mask_valid, resp->prev_mask,
+		     resp->curr_mask_valid, resp->curr_mask);
+
+out:
+	kfree(resp);
+	kfree(req);
+	return ret;
+}
+
+void icnss_handle_rejuvenate(struct icnss_priv *priv)
+{
+	struct icnss_event_pd_service_down_data *event_data;
+	struct icnss_uevent_fw_down_data fw_down_data = {0};
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+	if (event_data == NULL)
+		return;
+
+	event_data->crashed = true;
+	event_data->fw_rejuvenate = true;
+	fw_down_data.crashed = true;
+	set_bit(ICNSS_REJUVENATE, &priv->state);
+
+	icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
+				 &fw_down_data);
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+				0, event_data);
+}
+
+int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv)
+{
+	struct wlfw_qdss_trace_mem_info_req_msg_v01 *req;
+	struct wlfw_qdss_trace_mem_info_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	struct icnss_fw_mem *qdss_mem = priv->qdss_mem;
+	int ret = 0;
+	int i;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_dbg("Sending QDSS trace mem info, state: 0x%lx\n",
+		     priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->mem_seg_len = priv->qdss_mem_seg_len;
+
+	if (priv->qdss_mem_seg_len > QMI_WLFW_MAX_NUM_MEM_SEG) {
+		icnss_pr_err("Invalid seg len %u\n",
+			     priv->qdss_mem_seg_len);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; i < req->mem_seg_len; i++) {
+		icnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n",
+			     qdss_mem[i].va, &qdss_mem[i].pa,
+			     qdss_mem[i].size, qdss_mem[i].type);
+
+		req->mem_seg[i].addr = qdss_mem[i].pa;
+		req->mem_seg[i].size = qdss_mem[i].size;
+		req->mem_seg[i].type = qdss_mem[i].type;
+	}
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_qdss_trace_mem_info_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to initialize txn for QDSS trace mem request: err %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_QDSS_TRACE_MEM_INFO_REQ_V01,
+			       WLFW_QDSS_TRACE_MEM_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_qdss_trace_mem_info_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send QDSS trace mem info request: err %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Fail to wait for response of QDSS trace mem info request, err %d\n",
+			     ret);
+		goto out;
+	}
+
+	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QDSS trace mem info request failed, result: %d, err: %d\n",
+			     resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	kfree(req);
+	kfree(resp);
+	return 0;
+
+out:
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv,
+					     u32 pdev_id, int status)
+{
+	struct wlfw_m3_dump_upload_done_req_msg_v01 *req;
+	struct wlfw_m3_dump_upload_done_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	int ret = 0;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	icnss_pr_dbg("Sending M3 Upload done req, pdev %d, status %d\n",
+		     pdev_id, status);
+
+	req->pdev_id = pdev_id;
+	req->status = status;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_m3_dump_upload_done_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to initialize txn for M3 dump upload done req: err %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_M3_DUMP_UPLOAD_DONE_REQ_V01,
+			       WLFW_M3_DUMP_UPLOAD_DONE_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_m3_dump_upload_done_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send M3 dump upload done request: err %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Fail to wait for response of M3 dump upload done request, err %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("M3 Dump Upload Done Req failed, result: %d, err: 0x%X\n",
+			     resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+out:
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+static void fw_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+			    struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv =
+		container_of(qmi, struct icnss_priv, qmi);
+
+	icnss_pr_dbg("Received FW Ready Indication\n");
+
+	if (!txn) {
+		pr_err("spurious indication\n");
+		return;
+	}
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_READY_IND,
+				0, NULL);
+}
+
+static void msa_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+			     struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	struct device *dev = &priv->pdev->dev;
+	const struct wlfw_msa_ready_ind_msg_v01 *ind_msg = data;
+	uint64_t msa_base_addr = priv->msa_pa;
+	phys_addr_t hang_data_phy_addr;
+
+	icnss_pr_dbg("Received MSA Ready Indication\n");
+
+	if (!txn) {
+		pr_err("spurious indication\n");
+		return;
+	}
+
+	priv->stats.msa_ready_ind++;
+
+	/* Check if the length is valid &
+	 * the length should not be 0 and
+	 * should be <=  WLFW_MAX_HANG_EVENT_DATA_SIZE(400)
+	 */
+
+	if (ind_msg->hang_data_length_valid &&
+	    ind_msg->hang_data_length &&
+	    ind_msg->hang_data_length <= WLFW_MAX_HANG_EVENT_DATA_SIZE)
+		priv->hang_event_data_len = ind_msg->hang_data_length;
+	else
+		goto out;
+
+	/* Check if the offset is valid &
+	 * the offset should be in range of 0 to msa_mem_size-hang_data_length
+	 */
+
+	if (ind_msg->hang_data_addr_offset_valid &&
+	    (ind_msg->hang_data_addr_offset <= (priv->msa_mem_size -
+						 ind_msg->hang_data_length)))
+		hang_data_phy_addr = msa_base_addr +
+						ind_msg->hang_data_addr_offset;
+	else
+		goto out;
+
+	if (priv->hang_event_data_pa == hang_data_phy_addr)
+		goto exit;
+
+	priv->hang_event_data_pa = hang_data_phy_addr;
+	priv->hang_event_data_va = devm_ioremap(dev, priv->hang_event_data_pa,
+						ind_msg->hang_data_length);
+
+	if (!priv->hang_event_data_va) {
+		icnss_pr_err("Hang Data ioremap failed: phy addr: %pa\n",
+			     &priv->hang_event_data_pa);
+		goto fail;
+	}
+exit:
+	icnss_pr_dbg("Hang Event Data details,Offset:0x%x, Length:0x%x,va_addr: 0x%pK\n",
+		     ind_msg->hang_data_addr_offset,
+		     ind_msg->hang_data_length,
+		     priv->hang_event_data_va);
+
+	return;
+
+out:
+	icnss_pr_err("Invalid Hang Data details, Offset:0x%x, Length:0x%x",
+		     ind_msg->hang_data_addr_offset,
+		     ind_msg->hang_data_length);
+fail:
+	priv->hang_event_data_va = NULL;
+	priv->hang_event_data_pa = 0;
+	priv->hang_event_data_len = 0;
+}
+
+static void pin_connect_result_ind_cb(struct qmi_handle *qmi,
+				      struct sockaddr_qrtr *sq,
+				      struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_pin_connect_result_ind_msg_v01 *ind_msg = data;
+
+	icnss_pr_dbg("Received Pin Connect Result Indication\n");
+
+	if (!txn) {
+		pr_err("spurious indication\n");
+		return;
+	}
+
+	if (ind_msg->pwr_pin_result_valid)
+		priv->pwr_pin_result = ind_msg->pwr_pin_result;
+	if (ind_msg->phy_io_pin_result_valid)
+		priv->phy_io_pin_result = ind_msg->phy_io_pin_result;
+	if (ind_msg->rf_pin_result_valid)
+		priv->rf_pin_result = ind_msg->rf_pin_result;
+
+	icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n",
+		     ind_msg->pwr_pin_result, ind_msg->phy_io_pin_result,
+		     ind_msg->rf_pin_result);
+	priv->stats.pin_connect_result++;
+}
+
+static void rejuvenate_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+			      struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_rejuvenate_ind_msg_v01 *ind_msg = data;
+
+	icnss_pr_dbg("Received Rejuvenate Indication\n");
+
+	if (!txn) {
+		pr_err("spurious indication\n");
+		return;
+	}
+
+	icnss_ignore_fw_timeout(true);
+
+	if (ind_msg->cause_for_rejuvenation_valid)
+		priv->cause_for_rejuvenation = ind_msg->cause_for_rejuvenation;
+	else
+		priv->cause_for_rejuvenation = 0;
+	if (ind_msg->requesting_sub_system_valid)
+		priv->requesting_sub_system = ind_msg->requesting_sub_system;
+	else
+		priv->requesting_sub_system = 0;
+	if (ind_msg->line_number_valid)
+		priv->line_number = ind_msg->line_number;
+	else
+		priv->line_number = 0;
+	if (ind_msg->function_name_valid)
+		memcpy(priv->function_name, ind_msg->function_name,
+		       QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
+	else
+		memset(priv->function_name, 0,
+		       QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1);
+
+	icnss_pr_info("Cause for rejuvenation: 0x%x, requesting sub-system: 0x%x, line number: %u, function name: %s\n",
+		      priv->cause_for_rejuvenation,
+		      priv->requesting_sub_system,
+		      priv->line_number,
+		      priv->function_name);
+
+	priv->stats.rejuvenate_ind++;
+
+	icnss_handle_rejuvenate(priv);
+}
+
+static void cal_done_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
+			    struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+
+	icnss_pr_dbg("Received QMI WLFW calibration done indication\n");
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	priv->cal_done = true;
+	clear_bit(ICNSS_COLD_BOOT_CAL, &priv->state);
+}
+
+static void fw_init_done_ind_cb(struct qmi_handle *qmi,
+				struct sockaddr_qrtr *sq,
+				struct qmi_txn *txn, const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	struct device *dev = &priv->pdev->dev;
+	const struct wlfw_fw_init_done_ind_msg_v01 *ind_msg = data;
+	uint64_t msa_base_addr = priv->msa_pa;
+	phys_addr_t hang_data_phy_addr;
+
+	icnss_pr_dbg("Received QMI WLFW FW initialization done indication\n");
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	/* Check if the length is valid &
+	 * the length should not be 0 and
+	 * should be <=  WLFW_MAX_HANG_EVENT_DATA_SIZE(400)
+	 */
+
+	if (ind_msg->hang_data_length_valid &&
+	ind_msg->hang_data_length &&
+	ind_msg->hang_data_length <= WLFW_MAX_HANG_EVENT_DATA_SIZE)
+		priv->hang_event_data_len = ind_msg->hang_data_length;
+	else
+		goto out;
+
+	/* Check if the offset is valid &
+	 * the offset should be in range of 0 to msa_mem_size-hang_data_length
+	 */
+
+	if (ind_msg->hang_data_addr_offset_valid &&
+	    (ind_msg->hang_data_addr_offset <= (priv->msa_mem_size -
+					ind_msg->hang_data_length)))
+		hang_data_phy_addr = msa_base_addr +
+					ind_msg->hang_data_addr_offset;
+	else
+		goto out;
+
+	if (priv->hang_event_data_pa == hang_data_phy_addr)
+		goto exit;
+
+	priv->hang_event_data_pa = hang_data_phy_addr;
+	priv->hang_event_data_va = devm_ioremap(dev, priv->hang_event_data_pa,
+					ind_msg->hang_data_length);
+
+	if (!priv->hang_event_data_va) {
+		icnss_pr_err("Hang Data ioremap failed: phy addr: %pa\n",
+		&priv->hang_event_data_pa);
+		goto fail;
+	}
+
+exit:
+	icnss_pr_dbg("Hang Event Data details,Offset:0x%x, Length:0x%x,va_addr: 0x%pK\n",
+		     ind_msg->hang_data_addr_offset,
+		     ind_msg->hang_data_length,
+		     priv->hang_event_data_va);
+
+	goto post;
+
+out:
+	icnss_pr_err("Invalid Hang Data details, Offset:0x%x, Length:0x%x",
+		     ind_msg->hang_data_addr_offset,
+		     ind_msg->hang_data_length);
+fail:
+	priv->hang_event_data_va = NULL;
+	priv->hang_event_data_pa = 0;
+	priv->hang_event_data_len = 0;
+post:
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
+				0, NULL);
+
+}
+
+static void wlfw_qdss_trace_req_mem_ind_cb(struct qmi_handle *qmi,
+					   struct sockaddr_qrtr *sq,
+					   struct qmi_txn *txn,
+					   const void *data)
+{
+	struct icnss_priv *priv =
+		container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_qdss_trace_req_mem_ind_msg_v01 *ind_msg = data;
+	int i;
+
+	icnss_pr_dbg("Received QMI WLFW QDSS trace request mem indication\n");
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	if (priv->qdss_mem_seg_len) {
+		icnss_pr_err("Ignore double allocation for QDSS trace, current len %u\n",
+			     priv->qdss_mem_seg_len);
+		return;
+	}
+
+	priv->qdss_mem_seg_len = ind_msg->mem_seg_len;
+
+	if (priv->qdss_mem_seg_len > QMI_WLFW_MAX_NUM_MEM_SEG) {
+		icnss_pr_err("Invalid seg len %u\n",
+			     priv->qdss_mem_seg_len);
+		return;
+	}
+
+	for (i = 0; i < priv->qdss_mem_seg_len; i++) {
+		icnss_pr_dbg("QDSS requests for memory, size: 0x%x, type: %u\n",
+			     ind_msg->mem_seg[i].size,
+			     ind_msg->mem_seg[i].type);
+		priv->qdss_mem[i].type = ind_msg->mem_seg[i].type;
+		priv->qdss_mem[i].size = ind_msg->mem_seg[i].size;
+	}
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
+				0, NULL);
+}
+
+static void wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi,
+					struct sockaddr_qrtr *sq,
+					struct qmi_txn *txn,
+					const void *data)
+{
+	struct icnss_priv *priv =
+		container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_qdss_trace_save_ind_msg_v01 *ind_msg = data;
+	struct icnss_qmi_event_qdss_trace_save_data *event_data;
+	int i = 0;
+
+	icnss_pr_dbg("Received QMI WLFW QDSS trace save indication\n");
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	icnss_pr_dbg("QDSS_trace_save info: source %u, total_size %u, file_name_valid %u, file_name %s\n",
+		     ind_msg->source, ind_msg->total_size,
+		     ind_msg->file_name_valid, ind_msg->file_name);
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+	if (!event_data)
+		return;
+
+	if (ind_msg->mem_seg_valid) {
+		if (ind_msg->mem_seg_len > QDSS_TRACE_SEG_LEN_MAX) {
+			icnss_pr_err("Invalid seg len %u\n",
+				     ind_msg->mem_seg_len);
+			goto free_event_data;
+		}
+		icnss_pr_dbg("QDSS_trace_save seg len %u\n",
+			     ind_msg->mem_seg_len);
+		event_data->mem_seg_len = ind_msg->mem_seg_len;
+		for (i = 0; i < ind_msg->mem_seg_len; i++) {
+			event_data->mem_seg[i].addr = ind_msg->mem_seg[i].addr;
+			event_data->mem_seg[i].size = ind_msg->mem_seg[i].size;
+			icnss_pr_dbg("seg-%d: addr 0x%llx size 0x%x\n",
+				     i, ind_msg->mem_seg[i].addr,
+				     ind_msg->mem_seg[i].size);
+		}
+	}
+
+	event_data->total_size = ind_msg->total_size;
+
+	if (ind_msg->file_name_valid)
+		strlcpy(event_data->file_name, ind_msg->file_name,
+			QDSS_TRACE_FILE_NAME_MAX + 1);
+
+	if (ind_msg->source == 1) {
+		if (!ind_msg->file_name_valid)
+			strlcpy(event_data->file_name, "qdss_trace_wcss_etb",
+				QDSS_TRACE_FILE_NAME_MAX + 1);
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA,
+				0, event_data);
+	} else {
+		if (!ind_msg->file_name_valid)
+			strlcpy(event_data->file_name, "qdss_trace_ddr",
+				QDSS_TRACE_FILE_NAME_MAX + 1);
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE,
+				0, event_data);
+	}
+
+	return;
+
+free_event_data:
+	kfree(event_data);
+}
+
+static void wlfw_qdss_trace_free_ind_cb(struct qmi_handle *qmi,
+					struct sockaddr_qrtr *sq,
+					struct qmi_txn *txn,
+					const void *data)
+{
+	struct icnss_priv *priv =
+		container_of(qmi, struct icnss_priv, qmi);
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
+				0, NULL);
+}
+
+static void icnss_wlfw_respond_get_info_ind_cb(struct qmi_handle *qmi,
+					      struct sockaddr_qrtr *sq,
+					      struct qmi_txn *txn,
+					      const void *data)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_respond_get_info_ind_msg_v01 *ind_msg = data;
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	icnss_pr_vdbg("Extract message with event length: %d, type: %d, is last: %d, seq no: %d\n",
+		     ind_msg->data_len, ind_msg->type,
+		     ind_msg->is_last, ind_msg->seq_no);
+
+	if (priv->get_info_cb_ctx && priv->get_info_cb)
+		priv->get_info_cb(priv->get_info_cb_ctx,
+				       (void *)ind_msg->data,
+				       ind_msg->data_len);
+}
+
+static void icnss_wlfw_m3_dump_upload_segs_req_ind_cb(struct qmi_handle *qmi,
+						      struct sockaddr_qrtr *sq,
+						      struct qmi_txn *txn,
+						      const void *d)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+	const struct wlfw_m3_dump_upload_segments_req_ind_msg_v01 *ind_msg = d;
+	struct icnss_m3_upload_segments_req_data *event_data = NULL;
+	u64 max_mapped_addr = 0;
+	u64 segment_addr = 0;
+	int i = 0;
+
+	icnss_pr_dbg("Received QMI WLFW M3 dump upload sigments indication\n");
+
+	if (!txn) {
+		icnss_pr_err("Spurious indication\n");
+		return;
+	}
+
+	icnss_pr_dbg("M3 Dump upload info: pdev_id: %d no_of_segments: %d\n",
+		     ind_msg->pdev_id, ind_msg->no_of_valid_segments);
+
+	if (ind_msg->no_of_valid_segments > QMI_WLFW_MAX_M3_SEGMENTS_SIZE_V01)
+		return;
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+	if (!event_data)
+		return;
+
+	event_data->pdev_id = ind_msg->pdev_id;
+	event_data->no_of_valid_segments = ind_msg->no_of_valid_segments;
+	max_mapped_addr = priv->msa_pa + priv->msa_mem_size;
+
+	for (i = 0; i < ind_msg->no_of_valid_segments; i++) {
+		segment_addr = ind_msg->m3_segment[i].addr &
+				M3_SEGMENT_ADDR_MASK;
+
+		if (ind_msg->m3_segment[i].size > priv->msa_mem_size ||
+		    segment_addr >= max_mapped_addr ||
+		    segment_addr < priv->msa_pa ||
+		    ind_msg->m3_segment[i].size +
+		    segment_addr > max_mapped_addr) {
+			icnss_pr_dbg("Received out of range Segment %d Addr: 0x%llx Size: 0x%x, Name: %s, type: %d\n",
+				     (i + 1), segment_addr,
+				     ind_msg->m3_segment[i].size,
+				     ind_msg->m3_segment[i].name,
+				     ind_msg->m3_segment[i].type);
+			goto out;
+		}
+
+		event_data->m3_segment[i].addr = segment_addr;
+		event_data->m3_segment[i].size = ind_msg->m3_segment[i].size;
+		event_data->m3_segment[i].type = ind_msg->m3_segment[i].type;
+		strlcpy(event_data->m3_segment[i].name,
+			ind_msg->m3_segment[i].name,
+			WLFW_MAX_STR_LEN + 1);
+
+		icnss_pr_dbg("Received Segment %d Addr: 0x%llx Size: 0x%x, Name: %s, type: %d\n",
+			     (i + 1), segment_addr,
+			     ind_msg->m3_segment[i].size,
+			     ind_msg->m3_segment[i].name,
+			     ind_msg->m3_segment[i].type);
+	}
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ,
+				0, event_data);
+
+	return;
+out:
+	kfree(event_data);
+}
+
+static struct qmi_msg_handler wlfw_msg_handlers[] = {
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_FW_READY_IND_V01,
+		.ei = wlfw_fw_ready_ind_msg_v01_ei,
+		.decoded_size = sizeof(struct wlfw_fw_ready_ind_msg_v01),
+		.fn = fw_ready_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_MSA_READY_IND_V01,
+		.ei = wlfw_msa_ready_ind_msg_v01_ei,
+		.decoded_size = sizeof(struct wlfw_msa_ready_ind_msg_v01),
+		.fn = msa_ready_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01,
+		.ei = wlfw_pin_connect_result_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_pin_connect_result_ind_msg_v01),
+		.fn = pin_connect_result_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_REJUVENATE_IND_V01,
+		.ei = wlfw_rejuvenate_ind_msg_v01_ei,
+		.decoded_size = sizeof(struct wlfw_rejuvenate_ind_msg_v01),
+		.fn = rejuvenate_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_CAL_DONE_IND_V01,
+		.ei = wlfw_cal_done_ind_msg_v01_ei,
+		.decoded_size = sizeof(struct wlfw_cal_done_ind_msg_v01),
+		.fn = cal_done_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_FW_INIT_DONE_IND_V01,
+		.ei = wlfw_fw_init_done_ind_msg_v01_ei,
+		.decoded_size = sizeof(struct wlfw_fw_init_done_ind_msg_v01),
+		.fn = fw_init_done_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_QDSS_TRACE_REQ_MEM_IND_V01,
+		.ei = wlfw_qdss_trace_req_mem_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_qdss_trace_req_mem_ind_msg_v01),
+		.fn = wlfw_qdss_trace_req_mem_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_QDSS_TRACE_SAVE_IND_V01,
+		.ei = wlfw_qdss_trace_save_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_qdss_trace_save_ind_msg_v01),
+		.fn = wlfw_qdss_trace_save_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_QDSS_TRACE_FREE_IND_V01,
+		.ei = wlfw_qdss_trace_free_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_qdss_trace_free_ind_msg_v01),
+		.fn = wlfw_qdss_trace_free_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_RESPOND_GET_INFO_IND_V01,
+		.ei = wlfw_respond_get_info_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_respond_get_info_ind_msg_v01),
+		.fn = icnss_wlfw_respond_get_info_ind_cb
+	},
+	{
+		.type = QMI_INDICATION,
+		.msg_id = QMI_WLFW_M3_DUMP_UPLOAD_SEGMENTS_REQ_IND_V01,
+		.ei = wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei,
+		.decoded_size =
+		sizeof(struct wlfw_m3_dump_upload_segments_req_ind_msg_v01),
+		.fn = icnss_wlfw_m3_dump_upload_segs_req_ind_cb
+	},
+	{}
+};
+
+int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data)
+{
+	struct icnss_event_server_arrive_data *event_data = data;
+	struct qmi_handle *qmi = &priv->qmi;
+	struct sockaddr_qrtr sq = { 0 };
+	int ret = 0;
+
+	if (!priv) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	sq.sq_family = AF_QIPCRTR;
+	sq.sq_node = event_data->node;
+	sq.sq_port = event_data->port;
+	ret = kernel_connect(qmi->sock, (struct sockaddr *)&sq, sizeof(sq), 0);
+	if (ret < 0) {
+		icnss_pr_err("Fail to connect to remote service port\n");
+		goto out;
+	}
+
+	icnss_pr_info("QMI Server Connected: state: 0x%lx\n", priv->state);
+
+	kfree(data);
+	return 0;
+
+out:
+	kfree(data);
+	ICNSS_ASSERT(0);
+	return ret;
+}
+
+int icnss_clear_server(struct icnss_priv *priv)
+{
+	int ret;
+
+	if (!priv)
+		return -ENODEV;
+
+	icnss_pr_info("QMI Service Disconnected: 0x%lx\n", priv->state);
+	clear_bit(ICNSS_WLFW_CONNECTED, &priv->state);
+
+	icnss_unregister_fw_service(priv);
+
+	clear_bit(ICNSS_DEL_SERVER, &priv->state);
+
+	ret =  icnss_register_fw_service(priv);
+	if (ret < 0) {
+		icnss_pr_err("WLFW server registration failed\n");
+		ICNSS_ASSERT(0);
+	}
+
+	return 0;
+}
+
+static int wlfw_new_server(struct qmi_handle *qmi,
+			   struct qmi_service *service)
+{
+	struct icnss_priv *priv =
+		container_of(qmi, struct icnss_priv, qmi);
+	struct icnss_event_server_arrive_data *event_data;
+
+	if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) {
+		icnss_pr_info("WLFW server delete in progress, Ignore server arrive: 0x%lx\n",
+			      priv->state);
+		return 0;
+	}
+
+	icnss_pr_dbg("WLFW server arrive: node %u port %u\n",
+		     service->node, service->port);
+
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+	if (event_data == NULL)
+		return -ENOMEM;
+
+	event_data->node = service->node;
+	event_data->port = service->port;
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
+				0, event_data);
+
+	return 0;
+}
+
+static void wlfw_del_server(struct qmi_handle *qmi,
+			    struct qmi_service *service)
+{
+	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
+
+	if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) {
+		icnss_pr_info("WLFW server delete in progress, Ignore server delete:  0x%lx\n",
+			      priv->state);
+		return;
+	}
+
+	icnss_pr_dbg("WLFW server delete\n");
+
+	if (priv) {
+		set_bit(ICNSS_DEL_SERVER, &priv->state);
+		set_bit(ICNSS_FW_DOWN, &priv->state);
+		icnss_ignore_fw_timeout(true);
+	}
+
+	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_SERVER_EXIT,
+				0, NULL);
+}
+
+static struct qmi_ops wlfw_qmi_ops = {
+	.new_server = wlfw_new_server,
+	.del_server = wlfw_del_server,
+};
+
+int icnss_register_fw_service(struct icnss_priv *priv)
+{
+	int ret;
+
+	ret = qmi_handle_init(&priv->qmi,
+			      WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
+			      &wlfw_qmi_ops, wlfw_msg_handlers);
+	if (ret < 0)
+		return ret;
+
+	if (priv->device_id == WCN6750_DEVICE_ID)
+		ret = qmi_add_lookup(&priv->qmi, WLFW_SERVICE_ID_V01,
+				     WLFW_SERVICE_VERS_V01,
+				     WLFW_SERVICE_WCN_INS_ID_V01);
+	else
+		ret = qmi_add_lookup(&priv->qmi, WLFW_SERVICE_ID_V01,
+				     WLFW_SERVICE_VERS_V01,
+				     WLFW_SERVICE_INS_ID_V01);
+	return ret;
+}
+
+void icnss_unregister_fw_service(struct icnss_priv *priv)
+{
+	qmi_handle_release(&priv->qmi);
+}
+
+int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+			struct icnss_wlan_enable_cfg *config,
+			enum icnss_driver_mode mode,
+			const char *host_version)
+{
+	struct wlfw_wlan_cfg_req_msg_v01 req;
+	u32 i;
+	int ret;
+
+	icnss_pr_dbg("Mode: %d, config: %pK, host_version: %s\n",
+		     mode, config, host_version);
+
+	memset(&req, 0, sizeof(req));
+
+	if (mode == ICNSS_WALTEST || mode == ICNSS_CCPM)
+		goto skip;
+
+	if (!config || !host_version) {
+		icnss_pr_err("Invalid cfg pointer, config: %pK, host_version: %pK\n",
+			     config, host_version);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	req.host_version_valid = 1;
+	strlcpy(req.host_version, host_version,
+		WLFW_MAX_STR_LEN + 1);
+
+	req.tgt_cfg_valid = 1;
+	if (config->num_ce_tgt_cfg > WLFW_MAX_NUM_CE)
+		req.tgt_cfg_len = WLFW_MAX_NUM_CE;
+	else
+		req.tgt_cfg_len = config->num_ce_tgt_cfg;
+	for (i = 0; i < req.tgt_cfg_len; i++) {
+		req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
+		req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
+		req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
+		req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
+		req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
+	}
+
+	req.svc_cfg_valid = 1;
+	if (config->num_ce_svc_pipe_cfg > WLFW_MAX_NUM_SVC)
+		req.svc_cfg_len = WLFW_MAX_NUM_SVC;
+	else
+		req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
+	for (i = 0; i < req.svc_cfg_len; i++) {
+		req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
+		req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
+		req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID) {
+		req.shadow_reg_v2_valid = 1;
+		if (config->num_shadow_reg_v2_cfg >
+			QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01)
+			req.shadow_reg_v2_len =
+				QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01;
+		else
+			req.shadow_reg_v2_len = config->num_shadow_reg_v2_cfg;
+
+		memcpy(req.shadow_reg_v2, config->shadow_reg_v2_cfg,
+			 sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) *
+			 req.shadow_reg_v2_len);
+	} else if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		req.shadow_reg_valid = 1;
+		if (config->num_shadow_reg_cfg >
+			QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
+			req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
+		else
+			req.shadow_reg_len = config->num_shadow_reg_cfg;
+
+		memcpy(req.shadow_reg, config->shadow_reg_cfg,
+		       sizeof(struct wlfw_msi_cfg_s_v01) * req.shadow_reg_len);
+	}
+
+	ret = wlfw_wlan_cfg_send_sync_msg(priv, &req);
+	if (ret)
+		goto out;
+skip:
+	ret = wlfw_wlan_mode_send_sync_msg(priv,
+			   (enum wlfw_driver_mode_enum_v01)mode);
+out:
+	if (test_bit(SKIP_QMI, &priv->ctrl_params.quirks))
+		ret = 0;
+
+	return ret;
+}
+
+int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
+{
+	enum wlfw_driver_mode_enum_v01 mode = QMI_WLFW_OFF_V01;
+
+	return wlfw_wlan_mode_send_sync_msg(priv, mode);
+}
+
+int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv)
+{
+	int ret;
+	struct wlfw_vbatt_req_msg_v01 *req;
+	struct wlfw_vbatt_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!priv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &priv->state))
+		return -EINVAL;
+
+	icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n", priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	priv->stats.vbatt_req++;
+
+	req->voltage_uv = voltage_uv;
+
+	ret = qmi_txn_init(&priv->qmi, &txn, wlfw_vbatt_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for Vbatt message resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_VBATT_REQ_V01,
+			       WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_vbatt_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send Vbatt message req %d\n", ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("VBATT message resp wait failed with ret %d\n",
+				    ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI Vbatt message request rejected, result:%d error:%d\n",
+				    resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	priv->stats.vbatt_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(resp);
+	kfree(req);
+	priv->stats.vbatt_req_err++;
+	return ret;
+}
+
+#ifdef CONFIG_ICNSS2_DEBUG
+static inline u32 icnss_get_host_build_type(void)
+{
+	return QMI_HOST_BUILD_TYPE_PRIMARY_V01;
+}
+#else
+static inline u32 icnss_get_host_build_type(void)
+{
+	return QMI_HOST_BUILD_TYPE_SECONDARY_V01;
+}
+#endif
+
+int wlfw_host_cap_send_sync(struct icnss_priv *priv)
+{
+	struct wlfw_host_cap_req_msg_v01 *req;
+	struct wlfw_host_cap_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	int ddr_type;
+	u32 gpio;
+	int ret = 0;
+	u64 iova_start = 0, iova_size = 0,
+	    iova_ipa_start = 0, iova_ipa_size = 0;
+
+	icnss_pr_dbg("Sending host capability message, state: 0x%lx\n",
+		    priv->state);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->num_clients_valid = 1;
+	req->num_clients = 1;
+
+	req->bdf_support_valid = 1;
+	req->bdf_support = 1;
+
+	req->cal_done_valid = 1;
+	req->cal_done = priv->cal_done;
+	icnss_pr_dbg("Calibration done is %d\n", priv->cal_done);
+
+	if (priv->smmu_s1_enable &&
+	    !icnss_get_iova(priv, &iova_start, &iova_size) &&
+	    !icnss_get_iova_ipa(priv, &iova_ipa_start,
+				&iova_ipa_size)) {
+		req->ddr_range_valid = 1;
+		req->ddr_range[0].start = iova_start;
+		req->ddr_range[0].size = iova_size + iova_ipa_size;
+		req->ddr_range[1].start = priv->msa_pa;
+		req->ddr_range[1].size = priv->msa_mem_size;
+		icnss_pr_dbg("Sending iova starting 0x%llx with size 0x%llx\n",
+			    req->ddr_range[0].start, req->ddr_range[0].size);
+		icnss_pr_dbg("Sending msa starting 0x%llx with size 0x%llx\n",
+			    req->ddr_range[1].start, req->ddr_range[1].size);
+	}
+
+	req->host_build_type_valid = 1;
+	req->host_build_type = icnss_get_host_build_type();
+
+	if (priv->wlan_en_delay_ms >= 100) {
+		icnss_pr_dbg("Setting WLAN_EN delay: %d ms\n",
+			     priv->wlan_en_delay_ms);
+		req->wlan_enable_delay_valid = 1;
+		req->wlan_enable_delay = priv->wlan_en_delay_ms;
+	}
+
+	/* ddr_type = 7(LPDDR4) and 8(LPDDR5) */
+	ddr_type = of_fdt_get_ddrtype();
+	if (ddr_type > 0) {
+		icnss_pr_dbg("DDR Type: %d\n", ddr_type);
+		req->ddr_type_valid = 1;
+		req->ddr_type = ddr_type;
+	}
+
+	ret = of_property_read_u32(priv->pdev->dev.of_node, "wlan-en-gpio",
+				   &gpio);
+	if (!ret) {
+		icnss_pr_dbg("WLAN_EN_GPIO modified through DT: %d\n", gpio);
+		req->gpio_info_valid = 1;
+		req->gpio_info[WLAN_EN_GPIO_V01] = gpio;
+	} else {
+		req->gpio_info[WLAN_EN_GPIO_V01] = 0xFFFF;
+	}
+
+	ret = of_property_read_u32(priv->pdev->dev.of_node, "bt-en-gpio",
+				   &gpio);
+	if (!ret) {
+		icnss_pr_dbg("BT_EN_GPIO modified through DT: %d\n", gpio);
+		req->gpio_info_valid = 1;
+		req->gpio_info[BT_EN_GPIO_V01] = gpio;
+	} else {
+		req->gpio_info[BT_EN_GPIO_V01] = 0xFFFF;
+	}
+
+	ret = of_property_read_u32(priv->pdev->dev.of_node, "host-sol-gpio",
+				   &gpio);
+	if (!ret) {
+		icnss_pr_dbg("HOST_SOL_GPIO modified through DT: %d\n", gpio);
+		req->gpio_info_valid = 1;
+		req->gpio_info[HOST_SOL_GPIO_V01] = gpio;
+	} else {
+		req->gpio_info[HOST_SOL_GPIO_V01] = 0xFFFF;
+	}
+
+	ret = of_property_read_u32(priv->pdev->dev.of_node, "target-sol-gpio",
+				   &gpio);
+	if (!ret) {
+		icnss_pr_dbg("TARGET_SOL_GPIO modified through DT: %d\n", gpio);
+		req->gpio_info_valid = 1;
+		req->gpio_info[TARGET_SOL_GPIO_V01] = gpio;
+	} else {
+		req->gpio_info[TARGET_SOL_GPIO_V01] = 0xFFFF;
+	}
+
+	req->gpio_info_len = GPIO_TYPE_MAX_V01;
+
+	ret = qmi_txn_init(&priv->qmi, &txn,
+			   wlfw_host_cap_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Failed to initialize txn for host capability request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&priv->qmi, NULL, &txn,
+			       QMI_WLFW_HOST_CAP_REQ_V01,
+			       WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_host_cap_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Failed to send host capability request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Failed to wait for response of host capability request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("Host capability request failed, result: %d, err: %d\n",
+			    resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	kfree(req);
+	kfree(resp);
+	return 0;
+
+out:
+	ICNSS_QMI_ASSERT();
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int icnss_wlfw_get_info_send_sync(struct icnss_priv *plat_priv, int type,
+				 void *cmd, int cmd_len)
+{
+	struct wlfw_get_info_req_msg_v01 *req;
+	struct wlfw_get_info_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+	int ret = 0;
+
+	if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01)
+		return -EINVAL;
+
+	if (test_bit(ICNSS_FW_DOWN, &plat_priv->state))
+		return -EINVAL;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->type = type;
+	req->data_len = cmd_len;
+	memcpy(req->data, cmd, req->data_len);
+
+	ret = qmi_txn_init(&plat_priv->qmi, &txn,
+			   wlfw_get_info_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Failed to initialize txn for get info request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&plat_priv->qmi, NULL, &txn,
+			       QMI_WLFW_GET_INFO_REQ_V01,
+			       WLFW_GET_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_get_info_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Failed to send get info request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, plat_priv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Failed to wait for response of get info request, err: %d\n",
+			    ret);
+		goto out;
+	}
+
+	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("Get info request failed, result: %d, err: %d\n",
+			    resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	kfree(req);
+	kfree(resp);
+	return 0;
+
+out:
+	kfree(req);
+	kfree(resp);
+	return ret;
+}
+
+int wlfw_subsys_restart_level_msg(struct icnss_priv *penv, uint8_t restart_level)
+{
+	int ret;
+	struct wlfw_subsys_restart_level_req_msg_v01 *req;
+	struct wlfw_subsys_restart_level_resp_msg_v01 *resp;
+	struct qmi_txn txn;
+
+	if (!penv)
+		return -ENODEV;
+
+	if (test_bit(ICNSS_FW_DOWN, &penv->state))
+		return -EINVAL;
+
+	icnss_pr_dbg("Sending subsystem restart level: 0x%x\n", restart_level);
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		kfree(req);
+		return -ENOMEM;
+	}
+
+	req->restart_level_type_valid = 1;
+	req->restart_level_type = restart_level;
+
+	penv->stats.restart_level_req++;
+
+	ret = qmi_txn_init(&penv->qmi, &txn,
+			   wlfw_subsys_restart_level_resp_msg_v01_ei, resp);
+	if (ret < 0) {
+		icnss_pr_err("Fail to init txn for subsystem restart level, resp %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_send_request(&penv->qmi, NULL, &txn,
+			       QMI_WLFW_SUBSYS_RESTART_LEVEL_REQ_V01,
+			       WLFW_SUBSYS_RESTART_LEVEL_REQ_MSG_V01_MAX_MSG_LEN,
+			       wlfw_subsys_restart_level_req_msg_v01_ei, req);
+	if (ret < 0) {
+		qmi_txn_cancel(&txn);
+		icnss_pr_err("Fail to send subsystem restart level %d\n",
+			     ret);
+		goto out;
+	}
+
+	ret = qmi_txn_wait(&txn, penv->ctrl_params.qmi_timeout);
+	if (ret < 0) {
+		icnss_pr_err("Subsystem restart level timed out with ret %d\n",
+			     ret);
+		goto out;
+	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("Subsystem restart level request rejected,result:%d error:%d\n",
+			     resp->resp.result, resp->resp.error);
+		ret = -resp->resp.result;
+		goto out;
+	}
+
+	penv->stats.restart_level_resp++;
+
+	kfree(resp);
+	kfree(req);
+	return 0;
+
+out:
+	kfree(req);
+	kfree(resp);
+	penv->stats.restart_level_err++;
+	return ret;
+}

+ 264 - 0
icnss2/qmi.h

@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __ICNSS_QMI_H__
+#define __ICNSS_QMI_H__
+
+#include "device_management_service_v01.h"
+
+#define QDSS_TRACE_SEG_LEN_MAX 32
+#define QDSS_TRACE_FILE_NAME_MAX 16
+#define M3_SEGMENTS_SIZE_MAX 10
+#define M3_SEGMENT_NAME_LEN_MAX 16
+
+struct icnss_mem_seg {
+	u64 addr;
+	u32 size;
+};
+
+struct icnss_qmi_event_qdss_trace_save_data {
+	u32 total_size;
+	u32 mem_seg_len;
+	struct icnss_mem_seg mem_seg[QDSS_TRACE_SEG_LEN_MAX];
+	char file_name[QDSS_TRACE_FILE_NAME_MAX + 1];
+};
+
+struct icnss_m3_segment {
+	u32 type;
+	u64 addr;
+	u64 size;
+	char name[M3_SEGMENT_NAME_LEN_MAX + 1];
+};
+
+struct icnss_m3_upload_segments_req_data {
+	u32 pdev_id;
+	u32 no_of_valid_segments;
+	struct icnss_m3_segment m3_segment[M3_SEGMENTS_SIZE_MAX];
+};
+
+struct icnss_qmi_event_qdss_trace_req_data {
+	u32 total_size;
+	char file_name[QDSS_TRACE_FILE_NAME_MAX + 1];
+};
+
+#ifndef CONFIG_ICNSS2_QMI
+
+static inline int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int icnss_connect_to_fw_server(struct icnss_priv *priv,
+					     void *data)
+{
+	return 0;
+}
+static inline int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int wlfw_dynamic_feature_mask_send_sync_msg(
+		struct icnss_priv *priv, uint64_t dynamic_feature_mask)
+{
+	return 0;
+}
+static inline int icnss_clear_server(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline void icnss_ignore_fw_timeout(bool ignore) {}
+static int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int wlfw_ini_send_sync_msg(struct icnss_priv *priv,
+		uint8_t fw_log_mode)
+{
+	return 0;
+}
+static inline int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+					   uint32_t offset, uint32_t mem_type,
+					   uint32_t data_len, uint8_t *data)
+{
+	return 0;
+}
+static inline int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+					    uint32_t offset, uint32_t mem_type,
+					    uint32_t data_len, uint8_t *data)
+{
+	return 0;
+}
+static inline int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+		enum icnss_driver_mode mode)
+{
+	return 0;
+}
+static int wlfw_host_cap_send_sync(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+		struct icnss_wlan_enable_cfg *config,
+		enum icnss_driver_mode mode,
+		const char *host_version)
+{
+	return 0;
+}
+static inline int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline int icnss_register_fw_service(struct icnss_priv *priv)
+{
+	return 0;
+}
+static inline void icnss_unregister_fw_service(struct icnss_priv *priv) {}
+static inline int icnss_send_vbatt_update(struct icnss_priv *priv,
+					  uint64_t voltage_uv)
+{
+	return 0;
+}
+
+static inline int wlfw_device_info_send_msg(struct icnss_priv *priv)
+{
+	return 0;
+}
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+				 enum wlfw_driver_mode_enum_v01 mode)
+{
+	return 0;
+}
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type)
+{
+	return 0;
+}
+
+int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv)
+{
+	return 0;
+}
+
+int wlfw_power_save_send_msg(struct icnss_priv *priv,
+			     enum wlfw_power_save_mode_v01 mode)
+{
+	return 0;
+}
+
+int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type,
+				  void *cmd, int cmd_len)
+{
+	return 0;
+}
+
+int wlfw_send_soc_wake_msg(struct icnss_priv *priv,
+			   enum wlfw_soc_wake_enum_v01 type)
+{
+	return 0;
+}
+
+int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv,
+					     u32 pdev_id, int status)
+{
+	return 0;
+}
+
+int icnss_qmi_get_dms_mac(struct icnss_priv *priv)
+{
+	return 0;
+}
+
+int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv,
+				      u8 *mac, u32 mac_len)
+{
+	return 0;
+}
+
+int icnss_dms_init(struct icns_priv *priv)
+{
+	return 0;
+}
+
+void icnss_dms_deinit(struct icnss_priv *priv)
+{
+}
+
+int wlfw_subsys_restart_level_msg(struct icnss_priv *penv, uint8_t restart_level)
+{
+	return 0;
+}
+
+static inline int wlfw_cal_report_req(struct icnss_priv *priv)
+{
+	return 0;
+}
+#else
+int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv);
+int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data);
+int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv);
+int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv);
+int wlfw_cap_send_sync_msg(struct icnss_priv *priv);
+int icnss_qmi_pin_connect_result_ind(struct icnss_priv *priv,
+					void *msg, unsigned int msg_len);
+int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
+					   uint64_t dynamic_feature_mask);
+int icnss_clear_server(struct icnss_priv *priv);
+int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv);
+void icnss_ignore_fw_timeout(bool ignore);
+int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv);
+int wlfw_ini_send_sync_msg(struct icnss_priv *priv, uint8_t fw_log_mode);
+int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+					   uint32_t offset, uint32_t mem_type,
+					   uint32_t data_len, uint8_t *data);
+int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+					    uint32_t offset, uint32_t mem_type,
+					    uint32_t data_len, uint8_t *data);
+int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
+		struct icnss_wlan_enable_cfg *config,
+		enum icnss_driver_mode mode,
+		const char *host_version);
+int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv);
+int icnss_register_fw_service(struct icnss_priv *priv);
+void icnss_unregister_fw_service(struct icnss_priv *priv);
+int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv);
+int wlfw_host_cap_send_sync(struct icnss_priv *priv);
+int wlfw_device_info_send_msg(struct icnss_priv *priv);
+int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
+				 enum wlfw_driver_mode_enum_v01 mode);
+int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type);
+int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv);
+int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name,
+				   u32 total_size);
+int wlfw_qdss_trace_start(struct icnss_priv *priv);
+int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option);
+int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv);
+int wlfw_power_save_send_msg(struct icnss_priv *priv,
+			     enum wlfw_power_save_mode_v01 mode);
+int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type,
+				  void *cmd, int cmd_len);
+int wlfw_send_soc_wake_msg(struct icnss_priv *priv,
+			   enum wlfw_soc_wake_enum_v01 type);
+int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv,
+					     u32 pdev_id, int status);
+int icnss_qmi_get_dms_mac(struct icnss_priv *priv);
+int icnss_wlfw_wlan_mac_req_send_sync(struct icnss_priv *priv,
+				      u8 *mac, u32 mac_len);
+int icnss_dms_init(struct icnss_priv *priv);
+void icnss_dms_deinit(struct icnss_priv *priv);
+int wlfw_subsys_restart_level_msg(struct icnss_priv *penv, uint8_t restart_level);
+int wlfw_cal_report_req(struct icnss_priv *priv);
+#endif
+
+#endif /* __ICNSS_QMI_H__*/

+ 207 - 0
inc/icnss2.h

@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ */
+#ifndef _ICNSS_WLAN_H_
+#define _ICNSS_WLAN_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#define ICNSS_MAX_IRQ_REGISTRATIONS    12
+#define IWCN_MAX_IRQ_REGISTRATIONS    32
+#define ICNSS_MAX_TIMESTAMP_LEN        32
+
+#ifndef ICNSS_API_WITH_DEV
+#define ICNSS_API_WITH_DEV
+#endif
+
+#define DEVICE_NAME_MAX		10
+enum icnss_uevent {
+	ICNSS_UEVENT_FW_CRASHED,
+	ICNSS_UEVENT_FW_DOWN,
+	ICNSS_UEVENT_HANG_DATA,
+	ICNSS_UEVENT_SMMU_FAULT,
+};
+
+struct icnss_uevent_hang_data {
+	void *hang_event_data;
+	uint16_t hang_event_data_len;
+};
+
+struct icnss_uevent_fw_down_data {
+	bool crashed;
+};
+
+struct icnss_uevent_data {
+	enum icnss_uevent uevent;
+	void *data;
+};
+
+/* Device information like supported device ids, etc*/
+struct device_info {
+	char name[DEVICE_NAME_MAX];
+	uint16_t device_id;
+};
+
+struct icnss_driver_ops {
+	char *name;
+	struct device_info *dev_info;
+	unsigned long drv_state;
+	struct device_driver driver;
+	int (*probe)(struct device *dev);
+	void (*remove)(struct device *dev);
+	void (*shutdown)(struct device *dev);
+	int (*reinit)(struct device *dev);
+	void (*crash_shutdown)(void *pdev);
+	int (*pm_suspend)(struct device *dev);
+	int (*pm_resume)(struct device *dev);
+	int (*suspend_noirq)(struct device *dev);
+	int (*resume_noirq)(struct device *dev);
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent);
+	int (*idle_shutdown)(struct device *dev);
+	int (*idle_restart)(struct device *dev);
+	int (*set_therm_cdev_state)(struct device *dev,
+				    unsigned long thermal_state,
+				    int tcdev_id);
+};
+
+
+struct ce_tgt_pipe_cfg {
+	u32 pipe_num;
+	u32 pipe_dir;
+	u32 nentries;
+	u32 nbytes_max;
+	u32 flags;
+	u32 reserved;
+};
+
+struct ce_svc_pipe_cfg {
+	u32 service_id;
+	u32 pipe_dir;
+	u32 pipe_num;
+};
+
+struct icnss_shadow_reg_cfg {
+	u16 ce_id;
+	u16 reg_offset;
+};
+
+struct icnss_shadow_reg_v2_cfg {
+	u32 addr;
+};
+
+struct icnss_rri_over_ddr_cfg {
+	u32 base_addr_low;
+	u32 base_addr_high;
+};
+/* CE configuration to target */
+struct icnss_wlan_enable_cfg {
+	u32 num_ce_tgt_cfg;
+	struct ce_tgt_pipe_cfg *ce_tgt_cfg;
+	u32 num_ce_svc_pipe_cfg;
+	struct ce_svc_pipe_cfg *ce_svc_cfg;
+	u32 num_shadow_reg_cfg;
+	struct icnss_shadow_reg_cfg *shadow_reg_cfg;
+	u32 num_shadow_reg_v2_cfg;
+	struct icnss_shadow_reg_v2_cfg *shadow_reg_v2_cfg;
+	bool rri_over_ddr_cfg_valid;
+	struct icnss_rri_over_ddr_cfg rri_over_ddr_cfg;
+};
+
+/* driver modes */
+enum icnss_driver_mode {
+	ICNSS_MISSION,
+	ICNSS_FTM,
+	ICNSS_EPPING,
+	ICNSS_WALTEST,
+	ICNSS_OFF,
+	ICNSS_CCPM,
+	ICNSS_QVIT,
+	ICNSS_CALIBRATION,
+};
+
+struct icnss_soc_info {
+	void __iomem *v_addr;
+	phys_addr_t p_addr;
+	uint32_t chip_id;
+	uint32_t chip_family;
+	uint32_t board_id;
+	uint32_t soc_id;
+	uint32_t fw_version;
+	char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1];
+};
+
+#define icnss_register_driver(ops)		\
+	__icnss_register_driver(ops, THIS_MODULE, KBUILD_MODNAME)
+extern int __icnss_register_driver(struct icnss_driver_ops *ops,
+				   struct module *owner, const char *mod_name);
+
+extern int icnss_unregister_driver(struct icnss_driver_ops *ops);
+
+extern int icnss_wlan_enable(struct device *dev,
+			     struct icnss_wlan_enable_cfg *config,
+			     enum icnss_driver_mode mode,
+			     const char *host_version);
+extern int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode);
+extern void icnss_enable_irq(struct device *dev, unsigned int ce_id);
+extern void icnss_disable_irq(struct device *dev, unsigned int ce_id);
+extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info);
+extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx);
+extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
+	irqreturn_t (*handler)(int, void *),
+	unsigned long flags, const char *name, void *ctx);
+extern int icnss_get_ce_id(struct device *dev, int irq);
+extern int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode);
+extern int icnss_athdiag_read(struct device *dev, uint32_t offset,
+			      uint32_t mem_type, uint32_t data_len,
+			      uint8_t *output);
+extern int icnss_athdiag_write(struct device *dev, uint32_t offset,
+			       uint32_t mem_type, uint32_t data_len,
+			       uint8_t *input);
+extern int icnss_get_irq(struct device *dev, int ce_id);
+extern int icnss_power_on(struct device *dev);
+extern int icnss_power_off(struct device *dev);
+extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev);
+extern struct iommu_domain *icnss_smmu_get_domain(struct device *dev);
+extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
+			  uint32_t *iova_addr, size_t size);
+extern int icnss_smmu_unmap(struct device *dev,
+			    uint32_t iova_addr, size_t size);
+extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
+extern bool icnss_is_qmi_disable(struct device *dev);
+extern bool icnss_is_fw_ready(void);
+extern bool icnss_is_fw_down(void);
+extern bool icnss_is_rejuvenate(void);
+extern int icnss_trigger_recovery(struct device *dev);
+extern void icnss_block_shutdown(bool status);
+extern bool icnss_is_pdr(void);
+extern int icnss_idle_restart(struct device *dev);
+extern int icnss_idle_shutdown(struct device *dev);
+extern int icnss_get_user_msi_assignment(struct device *dev, char *user_name,
+				 int *num_vectors, u32 *user_base_data,
+				 u32 *base_vector);
+extern int icnss_get_msi_irq(struct device *dev, unsigned int vector);
+extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low,
+			   u32 *msi_addr_high);
+extern int icnss_qmi_send(struct device *dev, int type, void *cmd,
+			  int cmd_len, void *cb_ctx,
+			  int (*cb)(void *ctx, void *event, int event_len));
+extern int icnss_force_wake_request(struct device *dev);
+extern int icnss_force_wake_release(struct device *dev);
+extern int icnss_is_device_awake(struct device *dev);
+extern int icnss_thermal_cdev_register(struct device *dev,
+					unsigned long max_state,
+					int tcdev_id);
+extern void icnss_thermal_cdev_unregister(struct device *dev, int tcdev_id);
+extern int icnss_get_curr_therm_cdev_state(struct device *dev,
+					    unsigned long *thermal_state,
+					    int tcdev_id);
+extern int icnss_exit_power_save(struct device *dev);
+extern int icnss_prevent_l1(struct device *dev);
+extern void icnss_allow_l1(struct device *dev);
+extern int icnss_get_mhi_state(struct device *dev);
+extern int icnss_is_pci_ep_awake(struct device *dev);
+#endif /* _ICNSS_WLAN_H_ */