Просмотр исходного кода

Merge 4f03c58310f5e08f5a550ceb911491997576976b on remote branch

Change-Id: I92a9213e9c300f9811e744169af6ad4d2d0fb1a0
Linux Build Service Account 2 лет назад
Родитель
Сommit
7cc38a20ce
20 измененных файлов с 1512 добавлено и 181 удалено
  1. 8 0
      Kbuild
  2. 6 7
      Makefile
  3. 8 0
      cnss2/Kconfig
  4. 41 0
      cnss2/bus.c
  5. 9 0
      cnss2/bus.h
  6. 11 2
      cnss2/debug.c
  7. 433 36
      cnss2/main.c
  8. 34 0
      cnss2/main.h
  9. 487 24
      cnss2/pci.c
  10. 14 0
      cnss2/pci.h
  11. 55 23
      cnss2/power.c
  12. 119 26
      cnss2/qmi.c
  13. 3 0
      cnss2/reg.h
  14. 4 1
      icnss2/debug.c
  15. 217 48
      icnss2/main.c
  16. 10 0
      icnss2/main.h
  17. 15 1
      icnss2/power.c
  18. 19 3
      icnss2/qmi.c
  19. 11 6
      inc/cnss2.h
  20. 8 4
      inc/icnss2.h

+ 8 - 0
Kbuild

@@ -36,6 +36,14 @@ ifeq ($(CONFIG_CNSS2_CONDITIONAL_POWEROFF),y)
 KBUILD_CPPFLAGS += -DCONFIG_CNSS2_CONDITIONAL_POWEROFF
 endif
 
+ifeq ($(CONFIG_CNSS_REQ_FW_DIRECT),y)
+KBUILD_CPPFLAGS += -DCONFIG_CNSS_REQ_FW_DIRECT
+endif
+
+ifeq ($(CONFIG_CNSS_SUPPORT_DUAL_DEV),y)
+KBUILD_CPPFLAGS += -DCONFIG_CNSS_SUPPORT_DUAL_DEV
+endif
+
 ifeq ($(CONFIG_AUTO_PROJECT),y)
 KBUILD_CPPFLAGS += -DCONFIG_PULLDOWN_WLANEN
 endif

+ 6 - 7
Makefile

@@ -11,14 +11,11 @@ WLAN_PLATFORM_ROOT = $(shell pwd)
 KBUILD_OPTIONS := WLAN_PLATFORM_ROOT=$(WLAN_PLATFORM_ROOT)
 ifeq ($(USE_EXTERNAL_CONFIGS),)
 KBUILD_OPTIONS += CONFIG_CNSS_OUT_OF_TREE=y
-
-ifneq (,$(filter $(CONFIG_PCI_MSM),y m))
 KBUILD_OPTIONS += CONFIG_CNSS2=m
-KBUILD_OPTIONS += CONFIG_CNSS2_QMI=y
-KBUILD_OPTIONS += CONFIG_CNSS2_DEBUG=y
-endif
 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
@@ -29,8 +26,10 @@ KBUILD_OPTIONS += CONFIG_CNSS2_SSR_DRIVER_DUMP=y
 endif
 endif
 
-all:
-	$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+all: modules
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
 
 modules_install:
 	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install

+ 8 - 0
cnss2/Kconfig

@@ -97,6 +97,14 @@ config CNSS_REQ_FW_DIRECT
 	  configuration file to avoid 60s timeout while search file under user
 	  space failure.
 
+config CNSS_SUPPORT_DUAL_DEV
+	bool "Enable cnss2 support dual wlan card"
+	depends on CNSS2 && !CNSS_ASYNC
+	help
+	  This enables the changes from cnss2 platform driver to support dual
+	  wlan card attach. Now just supports QCA6390 chip and does not support
+	  asynchronous probe.
+
 config CNSS2_CONDITIONAL_POWEROFF
 	bool "Enable/Disable conditional bus suspend and device power off"
 	depends on CNSS2

+ 41 - 0
cnss2/bus.c

@@ -48,6 +48,7 @@ enum cnss_dev_bus_type cnss_get_bus_type(struct cnss_plat_data *plat_priv)
 	case QCA6174_DEVICE_ID:
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -635,6 +636,46 @@ int cnss_bus_update_time_sync_period(struct cnss_plat_data *plat_priv,
 	}
 }
 
+int cnss_bus_set_therm_cdev_state(struct cnss_plat_data *plat_priv,
+				  unsigned long thermal_state,
+				  int tcdev_id)
+{
+	if (!plat_priv)
+		return -ENODEV;
+
+	switch (plat_priv->bus_type) {
+	case CNSS_BUS_PCI:
+		return cnss_pci_set_therm_cdev_state(plat_priv->bus_priv,
+						     thermal_state,
+						     tcdev_id);
+	default:
+		cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type);
+		return -EINVAL;
+	}
+}
+
+int cnss_bus_get_msi_assignment(struct cnss_plat_data *plat_priv,
+				char *msi_name,
+				int *num_vectors,
+				u32 *user_base_data,
+				u32 *base_vector)
+{
+	if (!plat_priv)
+		return -ENODEV;
+
+	switch (plat_priv->bus_type) {
+	case CNSS_BUS_PCI:
+		return cnss_pci_get_user_msi_assignment(plat_priv->bus_priv,
+							msi_name,
+							num_vectors,
+							user_base_data,
+							base_vector);
+	default:
+		cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type);
+		return -EINVAL;
+	}
+}
+
 #if IS_ENABLED(CONFIG_MHI_BUS_MISC)
 void cnss_bus_disable_mhi_satellite_cfg(struct cnss_plat_data *plat_priv)
 {

+ 9 - 0
cnss2/bus.h

@@ -20,6 +20,7 @@
 #define QCA6390_DEVICE_ID		0x1101
 #define QCA6490_VENDOR_ID		0x17CB
 #define QCA6490_DEVICE_ID		0x1103
+#define QCN7605_VENDOR_ID               0x17CB
 #define QCN7605_DEVICE_ID               0x1102
 #define KIWI_VENDOR_ID			0x17CB
 #define KIWI_DEVICE_ID			0x1107
@@ -77,4 +78,12 @@ bool cnss_bus_is_smmu_s1_enabled(struct cnss_plat_data *plat_priv);
 int cnss_bus_update_time_sync_period(struct cnss_plat_data *plat_priv,
 				     unsigned int time_sync_period);
 void cnss_bus_disable_mhi_satellite_cfg(struct cnss_plat_data *plat_priv);
+int cnss_bus_set_therm_cdev_state(struct cnss_plat_data *plat_priv,
+				  unsigned long thermal_state,
+				  int tcdev_id);
+int cnss_bus_get_msi_assignment(struct cnss_plat_data *plat_priv,
+				char *msi_name,
+				int *num_vectors,
+				u32 *user_base_data,
+				u32 *base_vector);
 #endif /* _CNSS_BUS_H */

+ 11 - 2
cnss2/debug.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
 
 
 #include <linux/err.h>
@@ -576,6 +576,8 @@ static ssize_t cnss_runtime_pm_debug_write(struct file *fp,
 	buf[len] = '\0';
 	cmd = buf;
 
+	cnss_pr_dbg("Received runtime_pm debug command: %s\n", cmd);
+
 	if (sysfs_streq(cmd, "usage_count")) {
 		cnss_pci_pm_runtime_show_usage_count(pci_priv);
 	} else if (sysfs_streq(cmd, "request_resume")) {
@@ -944,8 +946,15 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
 {
 	int ret = 0;
 	struct dentry *root_dentry;
+	char name[CNSS_FS_NAME_SIZE];
+
+	if (cnss_is_dual_wlan_enabled())
+		snprintf(name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME "_%d",
+			 plat_priv->plat_idx);
+	else
+		snprintf(name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
 
-	root_dentry = debugfs_create_dir("cnss", 0);
+	root_dentry = debugfs_create_dir(name, 0);
 	if (IS_ERR(root_dentry)) {
 		ret = PTR_ERR(root_dentry);
 		cnss_pr_err("Unable to create debugfs %d\n", ret);

+ 433 - 36
cnss2/main.c

@@ -17,6 +17,7 @@
 #include <linux/rwsem.h>
 #include <linux/suspend.h>
 #include <linux/timer.h>
+#include <linux/thermal.h>
 #include <linux/version.h>
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0))
 #include <linux/panic_notifier.h>
@@ -88,7 +89,13 @@ enum cnss_recovery_type {
 	CNSS_PCSS_RECOVERY = 0x2,
 };
 
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define CNSS_MAX_DEV_NUM		2
+static struct cnss_plat_data *plat_env[CNSS_MAX_DEV_NUM];
+static int plat_env_count;
+#else
 static struct cnss_plat_data *plat_env;
+#endif
 
 static bool cnss_allow_driver_loading;
 
@@ -111,15 +118,122 @@ struct cnss_driver_event {
 	void *data;
 };
 
+bool cnss_check_driver_loading_allowed(void)
+{
+	return cnss_allow_driver_loading;
+}
+
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
 static void cnss_set_plat_priv(struct platform_device *plat_dev,
 			       struct cnss_plat_data *plat_priv)
 {
-	plat_env = plat_priv;
+	cnss_pr_dbg("Set plat_priv at %d", plat_env_count);
+	if (plat_priv) {
+		plat_priv->plat_idx = plat_env_count;
+		plat_env[plat_priv->plat_idx] = plat_priv;
+		plat_env_count++;
+	}
 }
 
-bool cnss_check_driver_loading_allowed(void)
+struct cnss_plat_data *cnss_get_plat_priv(struct platform_device
+						 *plat_dev)
 {
-	return cnss_allow_driver_loading;
+	int i;
+
+	if (!plat_dev)
+		return NULL;
+
+	for (i = 0; i < plat_env_count; i++) {
+		if (plat_env[i]->plat_dev == plat_dev)
+			return plat_env[i];
+	}
+	return NULL;
+}
+
+static void cnss_clear_plat_priv(struct cnss_plat_data *plat_priv)
+{
+	cnss_pr_dbg("Clear plat_priv at %d", plat_priv->plat_idx);
+	plat_env[plat_priv->plat_idx] = NULL;
+	plat_env_count--;
+}
+
+static int cnss_set_device_name(struct cnss_plat_data *plat_priv)
+{
+	snprintf(plat_priv->device_name, sizeof(plat_priv->device_name),
+		 "wlan_%d", plat_priv->plat_idx);
+
+	return 0;
+}
+
+static int cnss_plat_env_available(void)
+{
+	int ret = 0;
+
+	if (plat_env_count >= CNSS_MAX_DEV_NUM) {
+		cnss_pr_err("ERROR: No space to store plat_priv\n");
+		ret = -ENOMEM;
+	}
+	return ret;
+}
+
+int cnss_get_plat_env_count(void)
+{
+	return plat_env_count;
+}
+
+struct cnss_plat_data *cnss_get_plat_env(int index)
+{
+	return plat_env[index];
+}
+
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num)
+{
+	int i;
+
+	for (i = 0; i < plat_env_count; i++) {
+		if (plat_env[i]->rc_num == rc_num)
+			return plat_env[i];
+	}
+	return NULL;
+}
+
+static inline int
+cnss_get_qrtr_node_id(struct cnss_plat_data *plat_priv)
+{
+	return of_property_read_u32(plat_priv->dev_node,
+		"qcom,qrtr_node_id", &plat_priv->qrtr_node_id);
+}
+
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv)
+{
+	int ret = 0;
+
+	ret = cnss_get_qrtr_node_id(plat_priv);
+	if (ret) {
+		cnss_pr_warn("Failed to find qrtr_node_id err=%d\n", ret);
+		plat_priv->qrtr_node_id = 0;
+		plat_priv->wlfw_service_instance_id = 0;
+	} else {
+		plat_priv->wlfw_service_instance_id = plat_priv->qrtr_node_id +
+						      QRTR_NODE_FW_ID_BASE;
+		cnss_pr_dbg("service_instance_id=0x%x\n",
+			    plat_priv->wlfw_service_instance_id);
+	}
+}
+
+static inline int
+cnss_get_pld_bus_ops_name(struct cnss_plat_data *plat_priv)
+{
+	return of_property_read_string(plat_priv->plat_dev->dev.of_node,
+				       "qcom,pld_bus_ops_name",
+				       &plat_priv->pld_bus_ops_name);
+}
+
+#else
+static void cnss_set_plat_priv(struct platform_device *plat_dev,
+			       struct cnss_plat_data *plat_priv)
+{
+	plat_env = plat_priv;
 }
 
 struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev)
@@ -127,6 +241,51 @@ struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev)
 	return plat_env;
 }
 
+static void cnss_clear_plat_priv(struct cnss_plat_data *plat_priv)
+{
+	plat_env = NULL;
+}
+
+static int cnss_set_device_name(struct cnss_plat_data *plat_priv)
+{
+	snprintf(plat_priv->device_name, sizeof(plat_priv->device_name),
+		 "wlan");
+	return 0;
+}
+
+static int cnss_plat_env_available(void)
+{
+	return 0;
+}
+
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num)
+{
+	return cnss_bus_dev_to_plat_priv(NULL);
+}
+
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv)
+{
+}
+
+static int
+cnss_get_pld_bus_ops_name(struct cnss_plat_data *plat_priv)
+{
+	return 0;
+}
+#endif
+
+static inline int
+cnss_get_rc_num(struct cnss_plat_data *plat_priv)
+{
+	return of_property_read_u32(plat_priv->plat_dev->dev.of_node,
+		"qcom,wlan-rc-num", &plat_priv->rc_num);
+}
+
+bool cnss_is_dual_wlan_enabled(void)
+{
+	return IS_ENABLED(CONFIG_CNSS_SUPPORT_DUAL_DEV);
+}
+
 /**
  * cnss_get_mem_seg_count - Get segment count of memory
  * @type: memory type
@@ -436,6 +595,9 @@ int cnss_wlan_enable(struct device *dev,
 	if (mode == CNSS_WALTEST || mode == CNSS_CCPM)
 		goto skip_cfg;
 
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		config->send_msi_ce = true;
+
 	ret = cnss_wlfw_wlan_cfg_send_sync(plat_priv, config, host_version);
 	if (ret)
 		goto out;
@@ -639,6 +801,9 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv)
 
 	cnss_wlfw_bdf_dnld_send_sync(plat_priv, CNSS_BDF_REGDB);
 
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		plat_priv->ctrl_params.bdf_type = CNSS_BDF_BIN;
+
 	cnss_wlfw_ini_file_send_sync(plat_priv, WLFW_CONN_ROAM_INI_V01);
 
 	ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv,
@@ -646,6 +811,9 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv)
 	if (ret)
 		goto out;
 
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		return 0;
+
 	ret = cnss_bus_load_m3(plat_priv);
 	if (ret)
 		goto out;
@@ -1212,8 +1380,6 @@ EXPORT_SYMBOL(cnss_idle_restart);
 int cnss_idle_shutdown(struct device *dev)
 {
 	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
-	unsigned int timeout;
-	int ret;
 
 	if (!plat_priv) {
 		cnss_pr_err("plat_priv is NULL\n");
@@ -1227,21 +1393,12 @@ int cnss_idle_shutdown(struct device *dev)
 
 	cnss_pr_dbg("Doing idle shutdown\n");
 
-	if (!test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) &&
-	    !test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state))
-		goto skip_wait;
-
-	reinit_completion(&plat_priv->recovery_complete);
-	timeout = cnss_get_timeout(plat_priv, CNSS_TIMEOUT_RECOVERY);
-	ret = wait_for_completion_timeout(&plat_priv->recovery_complete,
-					  msecs_to_jiffies(timeout));
-	if (!ret) {
-		cnss_pr_err("Timeout (%ums) waiting for recovery to complete\n",
-			    timeout);
-		CNSS_ASSERT(0);
+	if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) ||
+	    test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) {
+		cnss_pr_dbg("Recovery in progress. Ignore IDLE Shutdown\n");
+		return -EBUSY;
 	}
 
-skip_wait:
 	return cnss_driver_event_post(plat_priv,
 				      CNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
 				      CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
@@ -2187,8 +2344,14 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv,
 	cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_OFF);
 	cnss_bus_free_qdss_mem(plat_priv);
 	cnss_release_antenna_sharing(plat_priv);
+
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		goto skip_shutdown;
+
 	cnss_bus_dev_shutdown(plat_priv);
 	msleep(POWER_RESET_MIN_DELAY_MS);
+
+skip_shutdown:
 	complete(&plat_priv->cal_complete);
 	clear_bit(CNSS_IN_COLD_BOOT_CAL, &plat_priv->driver_state);
 	set_bit(CNSS_COLD_BOOT_CAL_DONE, &plat_priv->driver_state);
@@ -2482,7 +2645,7 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv)
 
 	subsys_info = &plat_priv->subsys_info;
 
-	subsys_info->subsys_desc.name = "wlan";
+	subsys_info->subsys_desc.name = plat_priv->device_name;
 	subsys_info->subsys_desc.owner = THIS_MODULE;
 	subsys_info->subsys_desc.powerup = cnss_subsys_powerup;
 	subsys_info->subsys_desc.shutdown = cnss_subsys_shutdown;
@@ -3225,6 +3388,7 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -3247,6 +3411,7 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -3861,8 +4026,21 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
 {
 	struct device *dev = &plat_priv->plat_dev->dev;
 	int ret;
+	char cnss_name[CNSS_FS_NAME_SIZE];
+	char shutdown_name[32];
+
+	if (cnss_is_dual_wlan_enabled()) {
+		snprintf(cnss_name, CNSS_FS_NAME_SIZE,
+			 CNSS_FS_NAME "_%d", plat_priv->plat_idx);
+		snprintf(shutdown_name, sizeof(shutdown_name),
+			 "shutdown_wlan_%d", plat_priv->plat_idx);
+	} else {
+		snprintf(cnss_name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
+		snprintf(shutdown_name, sizeof(shutdown_name),
+			 "shutdown_wlan");
+	}
 
-	ret = sysfs_create_link(kernel_kobj, &dev->kobj, "cnss");
+	ret = sysfs_create_link(kernel_kobj, &dev->kobj, cnss_name);
 	if (ret) {
 		cnss_pr_err("Failed to create cnss link, err = %d\n",
 			    ret);
@@ -3870,7 +4048,7 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
 	}
 
 	/* This is only for backward compatibility. */
-	ret = sysfs_create_link(kernel_kobj, &dev->kobj, "shutdown_wlan");
+	ret = sysfs_create_link(kernel_kobj, &dev->kobj, shutdown_name);
 	if (ret) {
 		cnss_pr_err("Failed to create shutdown_wlan link, err = %d\n",
 			    ret);
@@ -3880,15 +4058,29 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
 	return 0;
 
 rm_cnss_link:
-	sysfs_remove_link(kernel_kobj, "cnss");
+	sysfs_remove_link(kernel_kobj, cnss_name);
 out:
 	return ret;
 }
 
 static void cnss_remove_sysfs_link(struct cnss_plat_data *plat_priv)
 {
-	sysfs_remove_link(kernel_kobj, "shutdown_wlan");
-	sysfs_remove_link(kernel_kobj, "cnss");
+	char cnss_name[CNSS_FS_NAME_SIZE];
+	char shutdown_name[32];
+
+	if (cnss_is_dual_wlan_enabled()) {
+		snprintf(cnss_name, CNSS_FS_NAME_SIZE,
+			 CNSS_FS_NAME "_%d", plat_priv->plat_idx);
+		snprintf(shutdown_name, sizeof(shutdown_name),
+			 "shutdown_wlan_%d", plat_priv->plat_idx);
+	} else {
+		snprintf(cnss_name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
+		snprintf(shutdown_name, sizeof(shutdown_name),
+			 "shutdown_wlan");
+	}
+
+	sysfs_remove_link(kernel_kobj, shutdown_name);
+	sysfs_remove_link(kernel_kobj, cnss_name);
 }
 
 static int cnss_create_sysfs(struct cnss_plat_data *plat_priv)
@@ -4095,6 +4287,7 @@ static void cnss_misc_deinit(struct cnss_plat_data *plat_priv)
 	wakeup_source_unregister(plat_priv->recovery_ws);
 	cnss_deinit_sol_gpio(plat_priv);
 	kfree(plat_priv->sram_dump);
+	kfree(plat_priv->on_chip_pmic_board_ids);
 }
 
 static void cnss_init_control_params(struct cnss_plat_data *plat_priv)
@@ -4358,6 +4551,195 @@ int cnss_set_wfc_mode(struct device *dev, struct cnss_wfc_cfg cfg)
 }
 EXPORT_SYMBOL(cnss_set_wfc_mode);
 
+static int cnss_tcdev_get_max_state(struct thermal_cooling_device *tcdev,
+				    unsigned long *thermal_state)
+{
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+
+	if (!tcdev || !tcdev->devdata) {
+		cnss_pr_err("tcdev or tcdev->devdata is null!\n");
+		return -EINVAL;
+	}
+
+	cnss_tcdev = tcdev->devdata;
+	*thermal_state = cnss_tcdev->max_thermal_state;
+
+	return 0;
+}
+
+static int cnss_tcdev_get_cur_state(struct thermal_cooling_device *tcdev,
+				    unsigned long *thermal_state)
+{
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+
+	if (!tcdev || !tcdev->devdata) {
+		cnss_pr_err("tcdev or tcdev->devdata is null!\n");
+		return -EINVAL;
+	}
+
+	cnss_tcdev = tcdev->devdata;
+	*thermal_state = cnss_tcdev->curr_thermal_state;
+
+	return 0;
+}
+
+static int cnss_tcdev_set_cur_state(struct thermal_cooling_device *tcdev,
+				    unsigned long thermal_state)
+{
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+	struct cnss_plat_data *plat_priv =  cnss_get_plat_priv(NULL);
+	int ret = 0;
+
+	if (!tcdev || !tcdev->devdata) {
+		cnss_pr_err("tcdev or tcdev->devdata is null!\n");
+		return -EINVAL;
+	}
+
+	cnss_tcdev = tcdev->devdata;
+
+	if (thermal_state > cnss_tcdev->max_thermal_state)
+		return -EINVAL;
+
+	cnss_pr_vdbg("Cooling device set current state: %ld,for cdev id %d",
+		     thermal_state, cnss_tcdev->tcdev_id);
+
+	mutex_lock(&plat_priv->tcdev_lock);
+	ret = cnss_bus_set_therm_cdev_state(plat_priv,
+					    thermal_state,
+					    cnss_tcdev->tcdev_id);
+	if (!ret)
+		cnss_tcdev->curr_thermal_state = thermal_state;
+	mutex_unlock(&plat_priv->tcdev_lock);
+	if (ret) {
+		cnss_pr_err("Setting Current Thermal State Failed: %d,for cdev id %d",
+			    ret, cnss_tcdev->tcdev_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct thermal_cooling_device_ops cnss_cooling_ops = {
+	.get_max_state = cnss_tcdev_get_max_state,
+	.get_cur_state = cnss_tcdev_get_cur_state,
+	.set_cur_state = cnss_tcdev_set_cur_state,
+};
+
+int cnss_thermal_cdev_register(struct device *dev, unsigned long max_state,
+			       int tcdev_id)
+{
+	struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+	char cdev_node_name[THERMAL_NAME_LENGTH] = "";
+	struct device_node *dev_node;
+	int ret = 0;
+
+	if (!priv) {
+		cnss_pr_err("Platform driver is not initialized!\n");
+		return -ENODEV;
+	}
+
+	cnss_tcdev = kzalloc(sizeof(*cnss_tcdev), GFP_KERNEL);
+	if (!cnss_tcdev) {
+		cnss_pr_err("Failed to allocate cnss_tcdev object!\n");
+		return -ENOMEM;
+	}
+
+	cnss_tcdev->tcdev_id = tcdev_id;
+	cnss_tcdev->max_thermal_state = max_state;
+
+	snprintf(cdev_node_name, THERMAL_NAME_LENGTH,
+		 "qcom,cnss_cdev%d", tcdev_id);
+
+	dev_node = of_find_node_by_name(NULL, cdev_node_name);
+	if (!dev_node) {
+		cnss_pr_err("Failed to get cooling device node\n");
+		kfree(cnss_tcdev);
+		return -EINVAL;
+	}
+
+	cnss_pr_dbg("tcdev node->name=%s\n", dev_node->name);
+
+	if (of_find_property(dev_node, "#cooling-cells", NULL)) {
+		cnss_tcdev->tcdev = thermal_of_cooling_device_register(dev_node,
+								       cdev_node_name,
+								       cnss_tcdev,
+								       &cnss_cooling_ops);
+		if (IS_ERR_OR_NULL(cnss_tcdev->tcdev)) {
+			ret = PTR_ERR(cnss_tcdev->tcdev);
+			cnss_pr_err("Cooling device register failed: %d, for cdev id %d\n",
+				    ret, cnss_tcdev->tcdev_id);
+			kfree(cnss_tcdev);
+		} else {
+			cnss_pr_dbg("Cooling device registered for cdev id %d",
+				    cnss_tcdev->tcdev_id);
+			mutex_lock(&priv->tcdev_lock);
+			list_add(&cnss_tcdev->tcdev_list,
+				 &priv->cnss_tcdev_list);
+			mutex_unlock(&priv->tcdev_lock);
+		}
+	} else {
+		cnss_pr_dbg("Cooling device registration not supported");
+		kfree(cnss_tcdev);
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(cnss_thermal_cdev_register);
+
+void cnss_thermal_cdev_unregister(struct device *dev, int tcdev_id)
+{
+	struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+
+	if (!priv) {
+		cnss_pr_err("Platform driver is not initialized!\n");
+		return;
+	}
+
+	mutex_lock(&priv->tcdev_lock);
+	while (!list_empty(&priv->cnss_tcdev_list)) {
+		cnss_tcdev = list_first_entry(&priv->cnss_tcdev_list,
+					      struct cnss_thermal_cdev,
+					      tcdev_list);
+		thermal_cooling_device_unregister(cnss_tcdev->tcdev);
+		list_del(&cnss_tcdev->tcdev_list);
+		kfree(cnss_tcdev);
+	}
+	mutex_unlock(&priv->tcdev_lock);
+}
+EXPORT_SYMBOL(cnss_thermal_cdev_unregister);
+
+int cnss_get_curr_therm_cdev_state(struct device *dev,
+				   unsigned long *thermal_state,
+				   int tcdev_id)
+{
+	struct cnss_plat_data *priv = cnss_get_plat_priv(NULL);
+	struct cnss_thermal_cdev *cnss_tcdev = NULL;
+
+	if (!priv) {
+		cnss_pr_err("Platform driver is not initialized!\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&priv->tcdev_lock);
+	list_for_each_entry(cnss_tcdev, &priv->cnss_tcdev_list, tcdev_list) {
+		if (cnss_tcdev->tcdev_id != tcdev_id)
+			continue;
+
+		*thermal_state = cnss_tcdev->curr_thermal_state;
+		mutex_unlock(&priv->tcdev_lock);
+		cnss_pr_dbg("Cooling device current state: %ld, for cdev id %d",
+			    cnss_tcdev->curr_thermal_state, tcdev_id);
+		return 0;
+	}
+	mutex_unlock(&priv->tcdev_lock);
+	cnss_pr_dbg("Cooling device ID not found: %d", tcdev_id);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(cnss_get_curr_therm_cdev_state);
+
 static int cnss_probe(struct platform_device *plat_dev)
 {
 	int ret = 0;
@@ -4371,6 +4753,10 @@ static int cnss_probe(struct platform_device *plat_dev)
 		goto out;
 	}
 
+	ret = cnss_plat_env_available();
+	if (ret)
+		goto out;
+
 	of_id = of_match_device(cnss_of_match_table, &plat_dev->dev);
 	if (!of_id || !of_id->data) {
 		cnss_pr_err("Failed to find of match device!\n");
@@ -4403,10 +4789,23 @@ static int cnss_probe(struct platform_device *plat_dev)
 		goto reset_plat_dev;
 	}
 
+	ret = cnss_get_pld_bus_ops_name(plat_priv);
+	if (ret)
+		cnss_pr_err("Failed to find bus ops name, err = %d\n",
+			    ret);
+
+	ret = cnss_get_rc_num(plat_priv);
+
+	if (ret)
+		cnss_pr_err("Failed to find PCIe RC number, err = %d\n", ret);
+
+	cnss_pr_dbg("rc_num=%d\n", plat_priv->rc_num);
+
 	plat_priv->bus_type = cnss_get_bus_type(plat_priv);
 	plat_priv->use_nv_mac = cnss_use_nv_mac(plat_priv);
 	plat_priv->driver_mode = CNSS_DRIVER_MODE_MAX;
 	cnss_set_plat_priv(plat_dev, plat_priv);
+	cnss_set_device_name(plat_priv);
 	platform_set_drvdata(plat_dev, plat_priv);
 	INIT_LIST_HEAD(&plat_priv->vreg_list);
 	INIT_LIST_HEAD(&plat_priv->clk_list);
@@ -4439,13 +4838,9 @@ static int cnss_probe(struct platform_device *plat_dev)
 	if (ret)
 		goto remove_sysfs;
 
-	ret = cnss_qmi_init(plat_priv);
-	if (ret)
-		goto deinit_event_work;
-
 	ret = cnss_dms_init(plat_priv);
 	if (ret)
-		goto deinit_qmi;
+		goto deinit_event_work;
 
 	ret = cnss_debugfs_create(plat_priv);
 	if (ret)
@@ -4472,9 +4867,8 @@ static int cnss_probe(struct platform_device *plat_dev)
 	cnss_register_coex_service(plat_priv);
 	cnss_register_ims_service(plat_priv);
 
-	ret = cnss_genl_init();
-	if (ret < 0)
-		cnss_pr_err("CNSS genl init failed %d\n", ret);
+	mutex_init(&plat_priv->tcdev_lock);
+	INIT_LIST_HEAD(&plat_priv->cnss_tcdev_list);
 
 	cnss_pr_info("Platform driver probed successfully.\n");
 
@@ -4486,8 +4880,6 @@ destroy_debugfs:
 	cnss_debugfs_destroy(plat_priv);
 deinit_dms:
 	cnss_dms_deinit(plat_priv);
-deinit_qmi:
-	cnss_qmi_deinit(plat_priv);
 deinit_event_work:
 	cnss_event_work_deinit(plat_priv);
 remove_sysfs:
@@ -4501,7 +4893,7 @@ free_res:
 reset_ctx:
 	platform_set_drvdata(plat_dev, NULL);
 reset_plat_dev:
-	cnss_set_plat_priv(plat_dev, NULL);
+	cnss_clear_plat_priv(plat_priv);
 out:
 	return ret;
 }
@@ -4530,7 +4922,7 @@ static int cnss_remove(struct platform_device *plat_dev)
 		mbox_free_channel(plat_priv->mbox_chan);
 
 	platform_set_drvdata(plat_dev, NULL);
-	plat_env = NULL;
+	cnss_clear_plat_priv(plat_priv);
 
 	return 0;
 }
@@ -4599,11 +4991,16 @@ static int __init cnss_initialize(void)
 	if (ret)
 		cnss_debug_deinit();
 
+	ret = cnss_genl_init();
+	if (ret < 0)
+		cnss_pr_err("CNSS genl init failed %d\n", ret);
+
 	return ret;
 }
 
 static void __exit cnss_exit(void)
 {
+	cnss_genl_exit();
 	platform_driver_unregister(&cnss_platform_driver);
 	cnss_debug_deinit();
 }

+ 34 - 0
cnss2/main.h

@@ -55,7 +55,17 @@
 #define CNSS_RAMDUMP_VERSION		0
 #define MAX_FIRMWARE_NAME_LEN		40
 #define FW_V2_NUMBER                    2
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define POWER_ON_RETRY_MAX_TIMES	2
+#else
 #define POWER_ON_RETRY_MAX_TIMES        4
+#endif
+#define POWER_ON_RETRY_DELAY_MS         500
+#define CNSS_FS_NAME			"cnss"
+#define CNSS_FS_NAME_SIZE		15
+#define CNSS_DEVICE_NAME_SIZE		16
+#define QRTR_NODE_FW_ID_BASE		7
+
 #define POWER_ON_RETRY_DELAY_MS         500
 #define WLFW_MAX_HANG_EVENT_DATA_SIZE   384
 
@@ -458,6 +468,15 @@ struct cnss_sol_gpio {
 	int host_sol_gpio;
 };
 
+struct cnss_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;
+};
+
 struct cnss_plat_data {
 	struct platform_device *plat_dev;
 	void *bus_priv;
@@ -490,6 +509,8 @@ struct cnss_plat_data {
 	u8 hds_enabled;
 	unsigned long driver_state;
 	struct list_head event_list;
+	struct list_head cnss_tcdev_list;
+	struct mutex tcdev_lock; /* mutex for cooling devices list access */
 	spinlock_t event_lock; /* spinlock for driver work event handling */
 	struct work_struct event_work;
 	struct workqueue_struct *event_wq;
@@ -578,6 +599,14 @@ struct cnss_plat_data {
 	uint32_t num_shadow_regs_v3;
 	bool sec_peri_feature_disable;
 	struct device_node *dev_node;
+	char device_name[CNSS_DEVICE_NAME_SIZE];
+	u32 plat_idx;
+	bool enumerate_done;
+	int qrtr_node_id;
+	unsigned int wlfw_service_instance_id;
+	const char *pld_bus_ops_name;
+	u32 on_chip_pmic_devices_count;
+	u32 *on_chip_pmic_board_ids;
 };
 
 #if IS_ENABLED(CONFIG_ARCH_QCOM)
@@ -605,6 +634,11 @@ int cnss_wlan_hw_enable(void);
 struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
 void cnss_pm_stay_awake(struct cnss_plat_data *plat_priv);
 void cnss_pm_relax(struct cnss_plat_data *plat_priv);
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num);
+int cnss_get_plat_env_count(void);
+struct cnss_plat_data *cnss_get_plat_env(int index);
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv);
+bool cnss_is_dual_wlan_enabled(void);
 int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
 			   enum cnss_driver_event_type type,
 			   u32 flags, void *data);

+ 487 - 24
cnss2/pci.c

@@ -35,13 +35,14 @@
 
 #define PCI_DMA_MASK_32_BIT		DMA_BIT_MASK(32)
 #define PCI_DMA_MASK_36_BIT		DMA_BIT_MASK(36)
-#define PCI_DMA_MASK_64_BIT		DMA_BIT_MASK(64)
+#define PCI_DMA_MASK_64_BIT		~0ULL
 
 #define MHI_NODE_NAME			"qcom,mhi"
 #define MHI_MSI_NAME			"MHI"
 
 #define QCA6390_PATH_PREFIX		"qca6390/"
 #define QCA6490_PATH_PREFIX		"qca6490/"
+#define QCN7605_PATH_PREFIX             "qcn7605/"
 #define KIWI_PATH_PREFIX		"kiwi/"
 #define MANGO_PATH_PREFIX		"mango/"
 #define PEACH_PATH_PREFIX		"peach/"
@@ -68,6 +69,8 @@
 #define CNSS_256KB_SIZE			0x40000
 #define DEVICE_RDDM_COOKIE		0xCAFECACE
 
+static bool cnss_driver_registered;
+
 static DEFINE_SPINLOCK(pci_link_down_lock);
 static DEFINE_SPINLOCK(pci_reg_window_lock);
 static DEFINE_SPINLOCK(time_sync_lock);
@@ -82,6 +85,8 @@ static DEFINE_SPINLOCK(time_sync_lock);
 #define FORCE_WAKE_DELAY_MAX_US			6000
 #define FORCE_WAKE_DELAY_TIMEOUT_US		60000
 
+#define REG_RETRY_MAX_TIMES		3
+
 #define MHI_SUSPEND_RETRY_MAX_TIMES		3
 #define MHI_SUSPEND_RETRY_DELAY_US		5000
 
@@ -244,6 +249,93 @@ static const struct mhi_channel_config cnss_mhi_channels[] = {
 #endif
 };
 
+static const struct mhi_channel_config cnss_mhi_channels_genoa[] = {
+	{
+		.num = 0,
+		.name = "LOOPBACK",
+		.num_elements = 32,
+		.event_ring = 1,
+		.dir = DMA_TO_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = false,
+	},
+	{
+		.num = 1,
+		.name = "LOOPBACK",
+		.num_elements = 32,
+		.event_ring = 1,
+		.dir = DMA_FROM_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = false,
+	},
+	{
+		.num = 4,
+		.name = "DIAG",
+		.num_elements = 64,
+		.event_ring = 1,
+		.dir = DMA_TO_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = false,
+	},
+	{
+		.num = 5,
+		.name = "DIAG",
+		.num_elements = 64,
+		.event_ring = 1,
+		.dir = DMA_FROM_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = false,
+	},
+	{
+		.num = 16,
+		.name = "IPCR",
+		.num_elements = 64,
+		.event_ring = 1,
+		.dir = DMA_TO_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = false,
+	},
+	{
+		.num = 17,
+		.name = "IPCR",
+		.num_elements = 64,
+		.event_ring = 1,
+		.dir = DMA_FROM_DEVICE,
+		.ee_mask = 0x4,
+		.pollcfg = 0,
+		.doorbell = MHI_DB_BRST_DISABLE,
+		.lpm_notify = false,
+		.offload_channel = false,
+		.doorbell_mode_switch = false,
+		.auto_queue = true,
+	},
+};
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
 static struct mhi_event_config cnss_mhi_events[] = {
 #else
@@ -322,6 +414,20 @@ static const struct mhi_controller_config cnss_mhi_config_default = {
 	.m2_no_db = true,
 };
 
+static const struct mhi_controller_config cnss_mhi_config_genoa = {
+	.max_channels = 32,
+	.timeout_ms = 10000,
+	.use_bounce_buf = false,
+	.buf_len = 0x8000,
+	.num_channels = ARRAY_SIZE(cnss_mhi_channels_genoa),
+	.ch_cfg = cnss_mhi_channels_genoa,
+	.num_events = ARRAY_SIZE(cnss_mhi_events) -
+		CNSS_MHI_SATELLITE_EVT_COUNT,
+	.event_cfg = cnss_mhi_events,
+	.m2_no_db = true,
+	.bhie_offset = 0x0324,
+};
+
 static const struct mhi_controller_config cnss_mhi_config_no_satellite = {
 	.max_channels = 32,
 	.timeout_ms = 10000,
@@ -667,6 +773,9 @@ static struct cnss_print_optimize print_optimize;
 #define SYSPM_REG_SIZE ARRAY_SIZE(syspm_reg_access_seq)
 
 static int cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv);
+static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev);
+static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev);
+
 
 #if IS_ENABLED(CONFIG_MHI_BUS_MISC)
 static void cnss_mhi_debug_reg_dump(struct cnss_pci_data *pci_priv)
@@ -826,6 +935,9 @@ static void cnss_pci_select_window(struct cnss_pci_data *pci_priv, u32 offset)
 			       QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET);
 	}
 
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		window_enable = QCN7605_WINDOW_ENABLE_BIT | window;
+
 	if (window != pci_priv->remap_window) {
 		pci_priv->remap_window = window;
 		cnss_pr_dbg("Config PCIe remap window register to 0x%x\n",
@@ -1809,6 +1921,18 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv,
 	}
 }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
+static int cnss_mhi_pm_force_resume(struct cnss_pci_data *pci_priv)
+{
+	return mhi_pm_resume_force(pci_priv->mhi_ctrl);
+}
+#else
+static int cnss_mhi_pm_force_resume(struct cnss_pci_data *pci_priv)
+{
+	return mhi_pm_resume(pci_priv->mhi_ctrl);
+}
+#endif
+
 static int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
 				  enum cnss_mhi_state mhi_state)
 {
@@ -1881,7 +2005,10 @@ retry_mhi_suspend:
 			ret = cnss_mhi_pm_fast_resume(pci_priv, true);
 			cnss_pci_allow_l1(&pci_priv->pci_dev->dev);
 		} else {
-			ret = mhi_pm_resume(pci_priv->mhi_ctrl);
+			if (pci_priv->device_id == QCA6390_DEVICE_ID)
+				ret = cnss_mhi_pm_force_resume(pci_priv);
+			else
+				ret = mhi_pm_resume(pci_priv->mhi_ctrl);
 		}
 		mutex_unlock(&pci_priv->mhi_ctrl->pm_mutex);
 		break;
@@ -1933,11 +2060,158 @@ static int cnss_pci_config_msi_data(struct cnss_pci_data *pci_priv)
 	return 0;
 }
 
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define PLC_PCIE_NAME_LEN		14
+
+static struct cnss_plat_data *
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
+{
+	int plat_env_count = cnss_get_plat_env_count();
+	struct cnss_plat_data *plat_env;
+	struct cnss_pci_data *pci_priv;
+	int i = 0;
+
+	if (!driver_ops) {
+		cnss_pr_err("No cnss driver\n");
+		return NULL;
+	}
+
+	for (i = 0; i < plat_env_count; i++) {
+		plat_env = cnss_get_plat_env(i);
+		if (!plat_env)
+			continue;
+		if (driver_ops->name && plat_env->pld_bus_ops_name) {
+			/* driver_ops->name = PLD_PCIE_OPS_NAME
+			 * #ifdef MULTI_IF_NAME
+			 * #define PLD_PCIE_OPS_NAME "pld_pcie_" MULTI_IF_NAME
+			 * #else
+			 * #define PLD_PCIE_OPS_NAME "pld_pcie"
+			 * #endif
+			 */
+			if (memcmp(driver_ops->name,
+				   plat_env->pld_bus_ops_name,
+				   PLC_PCIE_NAME_LEN) == 0)
+				return plat_env;
+		}
+	}
+
+	cnss_pr_err("Invalid cnss driver name from ko %s\n", driver_ops->name);
+	/* in the dual wlan card case, the pld_bus_ops_name from dts
+	 * and driver_ops-> name from ko should match, otherwise
+	 * wlanhost driver don't know which plat_env it can use;
+	 * if doesn't find the match one, then get first available
+	 * instance insteadly.
+	 */
+
+	for (i = 0; i < plat_env_count; i++) {
+		plat_env = cnss_get_plat_env(i);
+
+		if (!plat_env)
+			continue;
+
+		pci_priv = plat_env->bus_priv;
+		if (!pci_priv) {
+			cnss_pr_err("pci_priv is NULL\n");
+			continue;
+		}
+
+		if (driver_ops == pci_priv->driver_ops)
+			return plat_env;
+	}
+	/* Doesn't find the existing instance,
+	 * so return the fist empty instance
+	 */
+	for (i = 0; i < plat_env_count; i++) {
+		plat_env = cnss_get_plat_env(i);
+
+		if (!plat_env)
+			continue;
+		pci_priv = plat_env->bus_priv;
+		if (!pci_priv) {
+			cnss_pr_err("pci_priv is NULL\n");
+			continue;
+		}
+
+		if (!pci_priv->driver_ops)
+			return plat_env;
+	}
+
+	return NULL;
+}
+
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
+{
+	int ret = 0;
+	u32 scratch = QCA6390_PCIE_SOC_PCIE_REG_PCIE_SCRATCH_2_SOC_PCIE_REG;
+	struct cnss_plat_data *plat_priv;
+
+	if (!pci_priv) {
+		cnss_pr_err("pci_priv is NULL\n");
+		return -ENODEV;
+	}
+
+	plat_priv = pci_priv->plat_priv;
+	/**
+	 * in the single wlan chipset case, plat_priv->qrtr_node_id always is 0,
+	 * wlan fw will use the hardcode 7 as the qrtr node id.
+	 * in the dual Hastings case, we will read qrtr node id
+	 * from device tree and pass to get plat_priv->qrtr_node_id,
+	 * which always is not zero. And then store this new value
+	 * to pcie register, wlan fw will read out this qrtr node id
+	 * from this register and overwrite to the hardcode one
+	 * while do initialization for ipc router.
+	 * without this change, two Hastings will use the same
+	 * qrtr node instance id, which will mess up qmi message
+	 * exchange. According to qrtr spec, every node should
+	 * have unique qrtr node id
+	 */
+	if (plat_priv->device_id == QCA6390_DEVICE_ID &&
+	    plat_priv->qrtr_node_id) {
+		u32 val;
+
+		cnss_pr_dbg("write 0x%x to SCRATCH REG\n",
+			    plat_priv->qrtr_node_id);
+		ret = cnss_pci_reg_write(pci_priv, scratch,
+					 plat_priv->qrtr_node_id);
+		if (ret) {
+			cnss_pr_err("Failed to write register offset 0x%x, err = %d\n",
+				    scratch, ret);
+			goto out;
+		}
+
+		ret = cnss_pci_reg_read(pci_priv, scratch, &val);
+		if (ret) {
+			cnss_pr_err("Failed to read SCRATCH REG");
+			goto out;
+		}
+
+		if (val != plat_priv->qrtr_node_id) {
+			cnss_pr_err("qrtr node id write to register doesn't match with readout value");
+			return -ERANGE;
+		}
+	}
+out:
+	return ret;
+}
+#else
+static struct cnss_plat_data *
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
+{
+	return cnss_bus_dev_to_plat_priv(NULL);
+}
+
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
+{
+	return 0;
+}
+#endif
+
 int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
 {
 	int ret = 0;
 	struct cnss_plat_data *plat_priv;
 	unsigned int timeout = 0;
+	int retry = 0;
 
 	if (!pci_priv) {
 		cnss_pr_err("pci_priv is NULL\n");
@@ -1963,6 +2237,15 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
 	else /* For perf builds the timeout is 10 (default) * 3 seconds */
 		pci_priv->mhi_ctrl->timeout_ms *= 3;
 
+retry:
+	ret = cnss_pci_store_qrtr_node_id(pci_priv);
+	if (ret) {
+		if (retry++ < REG_RETRY_MAX_TIMES)
+			goto retry;
+		else
+			return ret;
+	}
+
 	/* Start the timer to dump MHI/PBL/SBL debug data periodically */
 	mod_timer(&pci_priv->boot_debug_timer,
 		  jiffies + msecs_to_jiffies(BOOT_DEBUG_TIMEOUT_MS));
@@ -2312,6 +2595,25 @@ static void cnss_pci_stop_time_sync_update(struct cnss_pci_data *pci_priv)
 	cancel_delayed_work_sync(&pci_priv->time_sync_work);
 }
 
+int cnss_pci_set_therm_cdev_state(struct cnss_pci_data *pci_priv,
+				  unsigned long thermal_state,
+				  int tcdev_id)
+{
+	if (!pci_priv) {
+		cnss_pr_err("pci_priv is NULL!\n");
+		return -ENODEV;
+	}
+
+	if (!pci_priv->driver_ops || !pci_priv->driver_ops->set_therm_cdev_state) {
+		cnss_pr_err("driver_ops or set_therm_cdev_state is NULL\n");
+		return -EINVAL;
+	}
+
+	return pci_priv->driver_ops->set_therm_cdev_state(pci_priv->pci_dev,
+							 thermal_state,
+							 tcdev_id);
+}
+
 int cnss_pci_update_time_sync_period(struct cnss_pci_data *pci_priv,
 				     unsigned int time_sync_period)
 {
@@ -2844,7 +3146,9 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv)
 	    test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) {
 		del_timer(&pci_priv->dev_rddm_timer);
 		cnss_pci_collect_dump_info(pci_priv, false);
-		CNSS_ASSERT(0);
+
+		if (!plat_priv->recovery_enabled)
+			CNSS_ASSERT(0);
 	}
 
 	if (!cnss_is_device_powered_on(plat_priv)) {
@@ -2937,6 +3241,7 @@ int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -2967,6 +3272,7 @@ int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -2997,6 +3303,7 @@ int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -3027,6 +3334,7 @@ int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv)
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -3111,7 +3419,7 @@ reg_driver:
 int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
 {
 	int ret = 0;
-	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+	struct cnss_plat_data *plat_priv;
 	struct cnss_pci_data *pci_priv;
 	const struct pci_device_id *id_table = driver_ops->id_table;
 	unsigned int timeout;
@@ -3121,6 +3429,8 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
 		return -ENODEV;
 	}
 
+	plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
+
 	if (!plat_priv) {
 		cnss_pr_buf("plat_priv is not ready for register driver\n");
 		return -EAGAIN;
@@ -3216,10 +3526,11 @@ EXPORT_SYMBOL(cnss_wlan_register_driver);
 
 void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops)
 {
-	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+	struct cnss_plat_data *plat_priv;
 	int ret = 0;
 	unsigned int timeout;
 
+	plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
 	if (!plat_priv) {
 		cnss_pr_err("plat_priv is NULL\n");
 		return;
@@ -3426,7 +3737,8 @@ out:
 static int cnss_pci_suspend(struct device *dev)
 {
 	int ret = 0;
-	struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev));
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
 	struct cnss_plat_data *plat_priv;
 
 	if (!pci_priv)
@@ -3439,6 +3751,25 @@ static int cnss_pci_suspend(struct device *dev)
 	if (!cnss_is_device_powered_on(plat_priv))
 		goto out;
 
+	/* No mhi state bit set if only finish pcie enumeration,
+	 * so test_bit is not applicable to check if it is INIT state.
+	 */
+	if (pci_priv->mhi_state == CNSS_MHI_INIT) {
+		bool suspend = cnss_should_suspend_pwroff(pci_dev);
+
+		/* Do PCI link suspend and power off in the LPM case
+		 * if chipset didn't do that after pcie enumeration.
+		 */
+		if (!suspend) {
+			ret = cnss_suspend_pci_link(pci_priv);
+			if (ret)
+				cnss_pr_err("Failed to suspend PCI link, err = %d\n",
+					    ret);
+			cnss_power_off_device(plat_priv);
+			goto out;
+		}
+	}
+
 	if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks) &&
 	    pci_priv->drv_supported) {
 		pci_priv->drv_connected_last =
@@ -4614,6 +4945,19 @@ int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info)
 }
 EXPORT_SYMBOL(cnss_get_soc_info);
 
+int cnss_pci_get_user_msi_assignment(struct cnss_pci_data *pci_priv,
+				     char *user_name,
+				     int *num_vectors,
+				     u32 *user_base_data,
+				     u32 *base_vector)
+{
+	return cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev,
+					    user_name,
+					    num_vectors,
+					    user_base_data,
+					    base_vector);
+}
+
 static int cnss_pci_enable_msi(struct cnss_pci_data *pci_priv)
 {
 	int ret = 0;
@@ -4852,6 +5196,9 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
 	case PEACH_DEVICE_ID:
 		pci_priv->dma_bit_mask = PCI_DMA_MASK_36_BIT;
 		break;
+	case QCN7605_DEVICE_ID:
+		pci_priv->dma_bit_mask = PCI_DMA_MASK_64_BIT;
+		break;
 	default:
 		pci_priv->dma_bit_mask = PCI_DMA_MASK_32_BIT;
 		break;
@@ -5483,6 +5830,10 @@ void cnss_pci_add_fw_prefix_name(struct cnss_pci_data *pci_priv,
 	}
 
 	switch (pci_priv->device_id) {
+	case QCN7605_DEVICE_ID:
+		scnprintf(prefix_name, MAX_FIRMWARE_NAME_LEN,
+			  QCN7605_PATH_PREFIX "%s", name);
+		break;
 	case QCA6390_DEVICE_ID:
 		scnprintf(prefix_name, MAX_FIRMWARE_NAME_LEN,
 			  QCA6390_PATH_PREFIX "%s", name);
@@ -5897,6 +6248,21 @@ exit:
 	return ret;
 }
 
+static bool cnss_is_tme_supported(struct cnss_pci_data *pci_priv)
+{
+	if (!pci_priv) {
+		cnss_pr_dbg("pci_priv is NULL");
+		return false;
+	}
+
+	switch (pci_priv->device_id) {
+	case PEACH_DEVICE_ID:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
 {
 	int ret = 0;
@@ -5907,6 +6273,10 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
 	const struct mhi_controller_config *cnss_mhi_config =
 						&cnss_mhi_config_default;
 
+	ret = cnss_qmi_init(plat_priv);
+	if (ret)
+		return -EINVAL;
+
 	if (pci_priv->device_id == QCA6174_DEVICE_ID)
 		return 0;
 
@@ -5957,7 +6327,12 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
 	mhi_ctrl->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size;
 	if (!mhi_ctrl->rddm_size)
 		mhi_ctrl->rddm_size = RAMDUMP_SIZE_DEFAULT;
-	mhi_ctrl->sbl_size = SZ_512K;
+
+	if (plat_priv->device_id == QCN7605_DEVICE_ID)
+		mhi_ctrl->sbl_size = SZ_256K;
+	else
+		mhi_ctrl->sbl_size = SZ_512K;
+
 	mhi_ctrl->seg_len = SZ_512K;
 	mhi_ctrl->fbc_download = true;
 
@@ -5967,9 +6342,15 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
 
 	/* Satellite config only supported on KIWI V2 and later chipset */
 	if (plat_priv->device_id <= QCA6490_DEVICE_ID ||
-	    (plat_priv->device_id == KIWI_DEVICE_ID &&
-	     plat_priv->device_version.major_version == 1))
-		cnss_mhi_config = &cnss_mhi_config_no_satellite;
+			(plat_priv->device_id == KIWI_DEVICE_ID &&
+			 plat_priv->device_version.major_version == 1)) {
+		if (plat_priv->device_id == QCN7605_DEVICE_ID)
+			cnss_mhi_config = &cnss_mhi_config_genoa;
+		else
+			cnss_mhi_config = &cnss_mhi_config_no_satellite;
+	}
+
+	mhi_ctrl->tme_supported_image = cnss_is_tme_supported(pci_priv);
 
 	ret = mhi_register_controller(mhi_ctrl, cnss_mhi_config);
 	if (ret) {
@@ -6150,6 +6531,57 @@ static void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv)
 }
 #endif
 
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
+{
+	int ret = 0;
+
+	/* in the dual wlan card case, if call pci_register_driver after
+	 * finishing the first pcie device enumeration, it will cause
+	 * the cnss_pci_probe called in advance with the second wlan card,
+	 * and the sequence like this:
+	 * enter msm_pcie_enumerate -> pci_bus_add_devices -> cnss_pci_probe
+	 * -> exit msm_pcie_enumerate.
+	 * But the correct sequence we expected is like this:
+	 * enter msm_pcie_enumerate -> pci_bus_add_devices  ->
+	 * exit msm_pcie_enumerate -> cnss_pci_probe.
+	 * And this unexpected sequence will make the second wlan card do
+	 * pcie link suspend while the pcie enumeration not finished.
+	 * So need to add below logical to avoid doing pcie link suspend
+	 * if the enumeration has not finish.
+	 */
+	plat_priv->enumerate_done = true;
+
+	/* Now enumeration is finished, try to suspend PCIe link */
+	if (plat_priv->bus_priv) {
+		struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
+		struct pci_dev *pci_dev = pci_priv->pci_dev;
+
+		switch (pci_dev->device) {
+		case QCA6390_DEVICE_ID:
+			cnss_pci_set_wlaon_pwr_ctrl(pci_priv,
+						    false,
+						    true,
+						    false);
+
+			cnss_pci_suspend_pwroff(pci_dev);
+			break;
+		default:
+			cnss_pr_err("Unknown PCI device found: 0x%x\n",
+				    pci_dev->device);
+			ret = -ENODEV;
+		}
+	}
+
+	return ret;
+}
+#else
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
+{
+	return 0;
+}
+#endif
+
 /* Setting to use this cnss_pm_domain ops will let PM framework override the
  * ops from dev->bus->pm which is pci_dev_pm_ops from pci-driver.c. This ops
  * has to take care everything device driver needed which is currently done
@@ -6238,10 +6670,13 @@ static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev)
 static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev)
 {
 	struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
-	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+	int rc_num = pci_dev->bus->domain_nr;
+	struct cnss_plat_data *plat_priv;
 	int ret = 0;
 	bool suspend_pwroff = cnss_should_suspend_pwroff(pci_dev);
 
+	plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
+
 	if (suspend_pwroff) {
 		ret = cnss_suspend_pci_link(pci_priv);
 		if (ret)
@@ -6259,11 +6694,17 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
 {
 	int ret = 0;
 	struct cnss_pci_data *pci_priv;
-	struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
 	struct device *dev = &pci_dev->dev;
+	int rc_num = pci_dev->bus->domain_nr;
+	struct cnss_plat_data *plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
 
-	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
-		    id->vendor, pci_dev->device);
+	cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x rc_num %d\n",
+		    id->vendor, pci_dev->device, rc_num);
+	if (!plat_priv) {
+		cnss_pr_err("Find match plat_priv with rc number failure\n");
+		ret = -ENODEV;
+		goto out;
+	}
 
 	pci_priv = devm_kzalloc(dev, sizeof(*pci_priv), GFP_KERNEL);
 	if (!pci_priv) {
@@ -6332,11 +6773,16 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
 		break;
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
 	case PEACH_DEVICE_ID:
-		cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
+		if ((cnss_is_dual_wlan_enabled() &&
+		     plat_priv->enumerate_done) || !cnss_is_dual_wlan_enabled())
+			cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false,
+						    false);
+
 		timer_setup(&pci_priv->dev_rddm_timer,
 			    cnss_dev_rddm_timeout_hdlr, 0);
 		timer_setup(&pci_priv->boot_debug_timer,
@@ -6357,7 +6803,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
 	cnss_pci_config_regs(pci_priv);
 	if (EMULATION_HW)
 		goto out;
+	if (cnss_is_dual_wlan_enabled() && !plat_priv->enumerate_done)
+		goto probe_done;
 	cnss_pci_suspend_pwroff(pci_dev);
+
+probe_done:
 	set_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state);
 
 	return 0;
@@ -6397,6 +6847,7 @@ static void cnss_pci_remove(struct pci_dev *pci_dev)
 	switch (pci_dev->device) {
 	case QCA6290_DEVICE_ID:
 	case QCA6390_DEVICE_ID:
+	case QCN7605_DEVICE_ID:
 	case QCA6490_DEVICE_ID:
 	case KIWI_DEVICE_ID:
 	case MANGO_DEVICE_ID:
@@ -6427,6 +6878,7 @@ static const struct pci_device_id cnss_pci_id_table[] = {
 	{ QCA6174_VENDOR_ID, QCA6174_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
 	{ QCA6290_VENDOR_ID, QCA6290_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
 	{ QCA6390_VENDOR_ID, QCA6390_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
+	{ QCN7605_VENDOR_ID, QCN7605_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
 	{ QCA6490_VENDOR_ID, QCA6490_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
 	{ KIWI_VENDOR_ID, KIWI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
 	{ MANGO_VENDOR_ID, MANGO_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
@@ -6443,7 +6895,7 @@ static const struct dev_pm_ops cnss_pm_ops = {
 			   cnss_pci_runtime_idle)
 };
 
-struct pci_driver cnss_pci_driver = {
+static struct pci_driver cnss_pci_driver = {
 	.name     = "cnss_pci",
 	.id_table = cnss_pci_id_table,
 	.probe    = cnss_pci_probe,
@@ -6515,17 +6967,25 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
 			goto out;
 	}
 
-	ret = pci_register_driver(&cnss_pci_driver);
+	ret = cnss_try_suspend(plat_priv);
 	if (ret) {
-		cnss_pr_err("Failed to register to PCI framework, err = %d\n",
-			    ret);
+		cnss_pr_err("Failed to suspend, ret: %d\n", ret);
 		goto out;
 	}
 
-	if (!plat_priv->bus_priv) {
-		cnss_pr_err("Failed to probe PCI driver\n");
-		ret = -ENODEV;
-		goto unreg_pci;
+	if (!cnss_driver_registered) {
+		ret = pci_register_driver(&cnss_pci_driver);
+		if (ret) {
+			cnss_pr_err("Failed to register to PCI framework, err = %d\n",
+				    ret);
+			goto out;
+		}
+		if (!plat_priv->bus_priv) {
+			cnss_pr_err("Failed to probe PCI driver\n");
+			ret = -ENODEV;
+			goto unreg_pci;
+		}
+		cnss_driver_registered = true;
 	}
 
 	return 0;
@@ -6538,5 +6998,8 @@ out:
 
 void cnss_pci_deinit(struct cnss_plat_data *plat_priv)
 {
-	pci_unregister_driver(&cnss_pci_driver);
+	if (cnss_driver_registered) {
+		pci_unregister_driver(&cnss_pci_driver);
+		cnss_driver_registered = false;
+	}
 }

+ 14 - 0
cnss2/pci.h

@@ -23,7 +23,13 @@
 
 #define PM_OPTIONS_DEFAULT		0
 #define PCI_LINK_DOWN			0
+
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define LINK_TRAINING_RETRY_MAX_TIMES		2
+#else
 #define LINK_TRAINING_RETRY_MAX_TIMES		3
+#endif
+
 #define LINK_TRAINING_RETRY_DELAY_MS		500
 #define MSI_USERS			4
 
@@ -295,4 +301,12 @@ void cnss_pci_handle_linkdown(struct cnss_pci_data *pci_priv);
 
 int cnss_pci_update_time_sync_period(struct cnss_pci_data *pci_priv,
 				     unsigned int time_sync_period);
+int cnss_pci_set_therm_cdev_state(struct cnss_pci_data *pci_priv,
+				  unsigned long thermal_state,
+				  int tcdev_id);
+int cnss_pci_get_user_msi_assignment(struct cnss_pci_data *pci_priv,
+				     char *user_name,
+				     int *num_vectors,
+				     u32 *user_base_data,
+				     u32 *base_vector);
 #endif /* _CNSS_PCI_H */

+ 55 - 23
cnss2/power.c

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/clk.h>
@@ -74,9 +74,11 @@ static struct cnss_clk_cfg cnss_clk_list[] = {
 #define WLAN_EN_SLEEP			"wlan_en_sleep"
 #define WLAN_VREGS_PROP			"wlan_vregs"
 
+/* unit us */
 #define BOOTSTRAP_DELAY			1000
 #define WLAN_ENABLE_DELAY		1000
-#define WLAN_ENABLE_DELAY_ROME		10000
+/* unit ms */
+#define WLAN_ENABLE_DELAY_ROME		10
 
 #define TCS_CMD_DATA_ADDR_OFFSET	0x4
 #define TCS_OFFSET			0xC8
@@ -1024,7 +1026,7 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv,
 
 			if (plat_priv->device_id == QCA6174_DEVICE_ID ||
 			    plat_priv->device_id == 0)
-				udelay(WLAN_ENABLE_DELAY_ROME);
+				mdelay(WLAN_ENABLE_DELAY_ROME);
 			else
 				udelay(WLAN_ENABLE_DELAY);
 
@@ -1607,6 +1609,7 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 {
 	struct device *dev = &plat_priv->plat_dev->dev;
 	int ret;
+	u32 cfg_arr_size = 0, *cfg_arr = NULL;
 
 	/* common DT Entries */
 	plat_priv->pdc_init_table_len =
@@ -1616,13 +1619,16 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 		plat_priv->pdc_init_table =
 			kcalloc(plat_priv->pdc_init_table_len,
 				sizeof(char *), GFP_KERNEL);
-		ret =
-		of_property_read_string_array(dev->of_node,
-					      "qcom,pdc_init_table",
-					      plat_priv->pdc_init_table,
-					      plat_priv->pdc_init_table_len);
-		if (ret < 0)
-			cnss_pr_err("Failed to get PDC Init Table\n");
+		if (plat_priv->pdc_init_table) {
+			ret = of_property_read_string_array(dev->of_node,
+							    "qcom,pdc_init_table",
+							    plat_priv->pdc_init_table,
+							    plat_priv->pdc_init_table_len);
+			if (ret < 0)
+				cnss_pr_err("Failed to get PDC Init Table\n");
+		} else {
+			cnss_pr_err("Failed to alloc PDC Init Table mem\n");
+		}
 	} else {
 		cnss_pr_dbg("PDC Init Table not configured\n");
 	}
@@ -1634,13 +1640,16 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 		plat_priv->vreg_pdc_map =
 			kcalloc(plat_priv->vreg_pdc_map_len,
 				sizeof(char *), GFP_KERNEL);
-		ret =
-		of_property_read_string_array(dev->of_node,
-					      "qcom,vreg_pdc_map",
-					      plat_priv->vreg_pdc_map,
-					      plat_priv->vreg_pdc_map_len);
-		if (ret < 0)
-			cnss_pr_err("Failed to get VReg PDC Mapping\n");
+		if (plat_priv->vreg_pdc_map) {
+			ret = of_property_read_string_array(dev->of_node,
+							    "qcom,vreg_pdc_map",
+							    plat_priv->vreg_pdc_map,
+							    plat_priv->vreg_pdc_map_len);
+			if (ret < 0)
+				cnss_pr_err("Failed to get VReg PDC Mapping\n");
+		} else {
+			cnss_pr_err("Failed to alloc VReg PDC mem\n");
+		}
 	} else {
 		cnss_pr_dbg("VReg PDC Mapping not configured\n");
 	}
@@ -1651,12 +1660,16 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 	if (plat_priv->pmu_vreg_map_len > 0) {
 		plat_priv->pmu_vreg_map = kcalloc(plat_priv->pmu_vreg_map_len,
 						  sizeof(char *), GFP_KERNEL);
-		ret =
-		of_property_read_string_array(dev->of_node, "qcom,pmu_vreg_map",
-					      plat_priv->pmu_vreg_map,
-					      plat_priv->pmu_vreg_map_len);
-		if (ret < 0)
-			cnss_pr_err("Fail to get PMU VReg Mapping\n");
+		if (plat_priv->pmu_vreg_map) {
+			ret = of_property_read_string_array(dev->of_node,
+							    "qcom,pmu_vreg_map",
+							    plat_priv->pmu_vreg_map,
+							    plat_priv->pmu_vreg_map_len);
+			if (ret < 0)
+				cnss_pr_err("Fail to get PMU VReg Mapping\n");
+		} else {
+			cnss_pr_err("Failed to alloc PMU VReg mem\n");
+		}
 	} else {
 		cnss_pr_dbg("PMU VReg Mapping not configured\n");
 	}
@@ -1676,6 +1689,25 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv)
 		if (ret)
 			cnss_pr_dbg("VReg for QCA6490 Int Power Amp not configured\n");
 	}
+	ret = of_property_count_u32_elems(plat_priv->plat_dev->dev.of_node,
+					  "qcom,on-chip-pmic-support");
+	if (ret > 0) {
+		cfg_arr_size = ret;
+		cfg_arr = kcalloc(cfg_arr_size, sizeof(*cfg_arr), GFP_KERNEL);
+		if (cfg_arr) {
+			ret = of_property_read_u32_array(plat_priv->plat_dev->dev.of_node,
+							 "qcom,on-chip-pmic-support",
+							 cfg_arr, cfg_arr_size);
+			if (!ret) {
+				plat_priv->on_chip_pmic_devices_count = cfg_arr_size;
+				plat_priv->on_chip_pmic_board_ids = cfg_arr;
+			}
+		} else {
+			cnss_pr_err("Failed to alloc cfg table mem\n");
+		}
+	} else {
+		cnss_pr_dbg("On chip PMIC device ids not configured\n");
+	}
 }
 
 int cnss_update_cpr_info(struct cnss_plat_data *plat_priv)

+ 119 - 26
cnss2/qmi.c

@@ -40,6 +40,7 @@
 #endif
 #define HW_V1_NUMBER			"v1"
 #define HW_V2_NUMBER			"v2"
+#define CE_MSI_NAME                     "CE"
 
 #define QMI_WLFW_TIMEOUT_MS		(plat_priv->ctrl_params.qmi_timeout)
 #define QMI_WLFW_TIMEOUT_JF		msecs_to_jiffies(QMI_WLFW_TIMEOUT_MS)
@@ -597,6 +598,22 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv)
 	if (resp->ol_cpr_cfg_valid)
 		cnss_aop_ol_cpr_cfg_setup(plat_priv, &resp->ol_cpr_cfg);
 
+	/* Disable WLAN PDC in AOP firmware for boards which support on chip PMIC
+	 * so AOP will ignore SW_CTRL changes and do not update regulator votes.
+	 **/
+	for (i = 0; i < plat_priv->on_chip_pmic_devices_count; i++) {
+		if (plat_priv->board_info.board_id ==
+		    plat_priv->on_chip_pmic_board_ids[i]) {
+			cnss_pr_dbg("Disabling WLAN PDC for board_id: %02x\n",
+				    plat_priv->board_info.board_id);
+			ret = cnss_aop_send_msg(plat_priv,
+						"{class: wlan_pdc, ss: rf, res: pdc, enable: 0}");
+			if (ret < 0)
+				cnss_pr_dbg("Failed to Send AOP Msg");
+			break;
+		}
+	}
+
 	cnss_pr_dbg("Target capability: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, otp_version: 0x%x\n",
 		    plat_priv->chip_info.chip_id,
 		    plat_priv->chip_info.chip_family,
@@ -622,6 +639,21 @@ out:
 	return ret;
 }
 
+static char *cnss_bdf_type_to_str(enum cnss_bdf_type bdf_type)
+{
+	switch (bdf_type) {
+	case CNSS_BDF_BIN:
+	case CNSS_BDF_ELF:
+		return "BDF";
+	case CNSS_BDF_REGDB:
+		return "REGDB";
+	case CNSS_BDF_HDS:
+		return "HDS";
+	default:
+		return "UNKNOWN";
+	}
+}
+
 static int cnss_get_bdf_file_name(struct cnss_plat_data *plat_priv,
 				  u32 bdf_type, char *filename,
 				  u32 filename_len)
@@ -841,8 +873,8 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
 	unsigned int remaining;
 	int ret = 0;
 
-	cnss_pr_dbg("Sending BDF download message, state: 0x%lx, type: %d\n",
-		    plat_priv->driver_state, bdf_type);
+	cnss_pr_dbg("Sending QMI_WLFW_BDF_DOWNLOAD_REQ_V01 message for bdf_type: %d (%s), state: 0x%lx\n",
+		    bdf_type, cnss_bdf_type_to_str(bdf_type), plat_priv->driver_state);
 
 	req = kzalloc(sizeof(*req), GFP_KERNEL);
 	if (!req)
@@ -867,14 +899,16 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
 					      &plat_priv->plat_dev->dev);
 
 	if (ret) {
-		cnss_pr_err("Failed to load BDF: %s, ret: %d\n", filename, ret);
+		cnss_pr_err("Failed to load %s: %s, ret: %d\n",
+			    cnss_bdf_type_to_str(bdf_type), filename, ret);
 		goto err_req_fw;
 	}
 
 	temp = fw_entry->data;
 	remaining = fw_entry->size;
 
-	cnss_pr_dbg("Downloading BDF: %s, size: %u\n", filename, remaining);
+	cnss_pr_dbg("Downloading %s: %s, size: %u\n",
+		    cnss_bdf_type_to_str(bdf_type), filename, remaining);
 
 	while (remaining) {
 		req->valid = 1;
@@ -900,8 +934,8 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
 		ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn,
 				   wlfw_bdf_download_resp_msg_v01_ei, resp);
 		if (ret < 0) {
-			cnss_pr_err("Failed to initialize txn for BDF download request, err: %d\n",
-				    ret);
+			cnss_pr_err("Failed to initialize txn for QMI_WLFW_BDF_DOWNLOAD_REQ_V01 request for %s, error: %d\n",
+				    cnss_bdf_type_to_str(bdf_type), ret);
 			goto err_send;
 		}
 
@@ -912,21 +946,22 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
 			 wlfw_bdf_download_req_msg_v01_ei, req);
 		if (ret < 0) {
 			qmi_txn_cancel(&txn);
-			cnss_pr_err("Failed to send respond BDF download request, err: %d\n",
-				    ret);
+			cnss_pr_err("Failed to send QMI_WLFW_BDF_DOWNLOAD_REQ_V01 request for %s, error: %d\n",
+				    cnss_bdf_type_to_str(bdf_type), ret);
 			goto err_send;
 		}
 
 		ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF);
 		if (ret < 0) {
-			cnss_pr_err("Failed to wait for response of BDF download request, err: %d\n",
-				    ret);
+			cnss_pr_err("Timeout while waiting for FW response for QMI_WLFW_BDF_DOWNLOAD_REQ_V01 request for %s, err: %d\n",
+				    cnss_bdf_type_to_str(bdf_type), ret);
 			goto err_send;
 		}
 
 		if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
-			cnss_pr_err("BDF download request failed, result: %d, err: %d\n",
-				    resp->resp.result, resp->resp.error);
+			cnss_pr_err("FW response for QMI_WLFW_BDF_DOWNLOAD_REQ_V01 request for %s failed, result: %d, err: %d\n",
+				    cnss_bdf_type_to_str(bdf_type), resp->resp.result,
+				    resp->resp.error);
 			ret = -resp->resp.result;
 			goto err_send;
 		}
@@ -1533,7 +1568,7 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv,
 	struct wlfw_wlan_cfg_req_msg_v01 *req;
 	struct wlfw_wlan_cfg_resp_msg_v01 *resp;
 	struct qmi_txn txn;
-	u32 i;
+	u32 i, ce_id, num_vectors, user_base_data, base_vector;
 	int ret = 0;
 
 	if (!plat_priv)
@@ -1583,16 +1618,34 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv,
 	if (plat_priv->device_id != KIWI_DEVICE_ID &&
 	    plat_priv->device_id != MANGO_DEVICE_ID &&
 	    plat_priv->device_id != PEACH_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;
+		if (plat_priv->device_id == QCN7605_DEVICE_ID &&
+		    config->num_shadow_reg_cfg) {
+			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_shadow_reg_cfg_s_v01) *
+			       req->shadow_reg_len);
+		} else {
+			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);
+			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 {
 		req->shadow_reg_v3_valid = 1;
 		if (config->num_shadow_reg_v3_cfg >
@@ -1607,8 +1660,33 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv,
 			    plat_priv->num_shadow_regs_v3);
 
 		memcpy(req->shadow_reg_v3, config->shadow_reg_v3_cfg,
-		       sizeof(struct wlfw_shadow_reg_v3_cfg_s_v01)
-		       * req->shadow_reg_v3_len);
+		       sizeof(struct wlfw_shadow_reg_v3_cfg_s_v01) *
+		       req->shadow_reg_v3_len);
+	}
+
+	if (config->rri_over_ddr_cfg_valid) {
+		req->rri_over_ddr_cfg_valid = 1;
+		req->rri_over_ddr_cfg.base_addr_low =
+			config->rri_over_ddr_cfg.base_addr_low;
+		req->rri_over_ddr_cfg.base_addr_high =
+			config->rri_over_ddr_cfg.base_addr_high;
+	}
+	if (config->send_msi_ce) {
+		ret = cnss_bus_get_msi_assignment(plat_priv,
+						  CE_MSI_NAME,
+						  &num_vectors,
+						  &user_base_data,
+						  &base_vector);
+		if (!ret) {
+			req->msi_cfg_valid = 1;
+			req->msi_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
+			for (ce_id = 0; ce_id < QMI_WLFW_MAX_NUM_CE_V01;
+					ce_id++) {
+				req->msi_cfg[ce_id].ce_id = ce_id;
+				req->msi_cfg[ce_id].msi_vector =
+					(ce_id % num_vectors) + base_vector;
+			}
+		}
 	}
 
 	ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn,
@@ -3211,9 +3289,25 @@ static struct qmi_ops qmi_wlfw_ops = {
 	.del_server = wlfw_del_server,
 };
 
+static int cnss_qmi_add_lookup(struct cnss_plat_data *plat_priv)
+{
+	unsigned int id = WLFW_SERVICE_INS_ID_V01;
+
+	/* In order to support dual wlan card attach case,
+	 * need separate qmi service instance id for each dev
+	 */
+	if (cnss_is_dual_wlan_enabled() && plat_priv->qrtr_node_id != 0 &&
+	    plat_priv->wlfw_service_instance_id != 0)
+		id = plat_priv->wlfw_service_instance_id;
+
+	return qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
+			      WLFW_SERVICE_VERS_V01, id);
+}
+
 int cnss_qmi_init(struct cnss_plat_data *plat_priv)
 {
 	int ret = 0;
+	cnss_get_qrtr_info(plat_priv);
 
 	ret = qmi_handle_init(&plat_priv->qmi_wlfw,
 			      QMI_WLFW_MAX_RECV_BUF_SIZE,
@@ -3224,8 +3318,7 @@ int cnss_qmi_init(struct cnss_plat_data *plat_priv)
 		goto out;
 	}
 
-	ret = qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
-			     WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01);
+	ret = cnss_qmi_add_lookup(plat_priv);
 	if (ret < 0)
 		cnss_pr_err("Failed to add WLFW QMI lookup, err: %d\n", ret);
 

+ 3 - 0
cnss2/reg.h

@@ -124,6 +124,7 @@
 #define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_STATUS_SOC_PCIE_REG 0x1E04058
 #define QCA6390_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x01E05090
 #define PEACH_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x01E01100
+#define QCA6390_PCIE_SOC_PCIE_REG_PCIE_SCRATCH_2_SOC_PCIE_REG 0x01E0405C
 #define QCA6390_PCIE_PCIE_PARF_LTSSM 0x01E081B0
 #define QCA6390_PCIE_PCIE_PARF_PM_STTS 0x01E08024
 #define QCA6390_PCIE_PCIE_PARF_PM_STTS_1 0x01E08028
@@ -360,4 +361,6 @@
 
 #define GCC_GCC_SPARE_REG_1 0x1E40310
 #define GCC_PRE_ARES_DEBUG_TIMER_VAL 0x1E40270
+
+#define QCN7605_WINDOW_ENABLE_BIT       0x80000000
 #endif

+ 4 - 1
icnss2/debug.c

@@ -422,6 +422,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
 			continue;
 		case ICNSS_SLATE_UP:
 			seq_puts(s, "ICNSS SLATE UP");
+			continue;
+		case ICNSS_LOW_POWER:
+			seq_puts(s, "ICNSS LOW POWER");
 		}
 
 		seq_printf(s, "UNKNOWN-%d", i);
@@ -472,7 +475,7 @@ static int icnss_stats_show(struct seq_file *s, void *data)
 	ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
 	ICNSS_STATS_DUMP(s, priv, pm_relax);
 
-	if (priv->device_id != WCN6750_DEVICE_ID) {
+	if (priv->device_id == ADRASTEA_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);

+ 217 - 48
icnss2/main.c

@@ -45,7 +45,7 @@
 #include <linux/soc/qcom/pdr.h>
 #include <linux/remoteproc.h>
 #include <trace/hooks/remoteproc.h>
-#include <../drivers/soc/qcom/slatecom_interface.h>
+#include <linux/soc/qcom/slatecom_interface.h>
 #include "main.h"
 #include "qmi.h"
 #include "debug.h"
@@ -83,8 +83,13 @@ module_param(qmi_timeout, ulong, 0600);
 #define WLFW_TIMEOUT                    msecs_to_jiffies(3000)
 #endif
 
+#define ICNSS_RECOVERY_TIMEOUT		60000
+#define ICNSS_WPSS_SSR_TIMEOUT          5000
+#define ICNSS_CAL_TIMEOUT		40000
+
 static struct icnss_priv *penv;
 static struct work_struct wpss_loader;
+static struct work_struct wpss_ssr_work;
 uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK;
 
 #define ICNSS_EVENT_PENDING			2989
@@ -132,14 +137,29 @@ static struct icnss_priv *icnss_get_plat_priv(void)
 	return penv;
 }
 
+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 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);
+	if (!priv)
+		return count;
+
 	icnss_pr_dbg("Received shutdown indication");
+
+	atomic_set(&priv->is_shutdown, true);
+	if (priv->wpss_supported && priv->device_id == ADRASTEA_DEVICE_ID)
+		icnss_wpss_unload(priv);
 	return count;
 }
 
@@ -449,7 +469,7 @@ static int icnss_send_smp2p(struct icnss_priv *priv,
 	unsigned int value = 0;
 	int ret;
 
-	if (IS_ERR(priv->smp2p_info[smp2p_entry].smem_state))
+	if (!priv || IS_ERR(priv->smp2p_info[smp2p_entry].smem_state))
 		return -EINVAL;
 
 	/* No Need to check FW_DOWN for ICNSS_RESET_MSG */
@@ -502,6 +522,15 @@ static int icnss_send_smp2p(struct icnss_priv *priv,
 	return ret;
 }
 
+bool icnss_is_low_power(void)
+{
+	if (!penv)
+		return false;
+	else
+		return test_bit(ICNSS_LOW_POWER, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_low_power);
+
 static irqreturn_t fw_error_fatal_handler(int irq, void *ctx)
 {
 	struct icnss_priv *priv = ctx;
@@ -522,6 +551,10 @@ static irqreturn_t fw_crash_indication_handler(int irq, void *ctx)
 	icnss_pr_err("Received early crash indication from FW\n");
 
 	if (priv) {
+		if (priv->wpss_self_recovery_enabled)
+			mod_timer(&priv->wpss_ssr_timer,
+				  jiffies + msecs_to_jiffies(ICNSS_WPSS_SSR_TIMEOUT));
+
 		set_bit(ICNSS_FW_DOWN, &priv->state);
 		icnss_ignore_fw_timeout(true);
 
@@ -863,7 +896,8 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
 		}
 	}
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		if (!icnss_get_temperature(priv, &temp)) {
 			icnss_pr_dbg("Temperature: %d\n", temp);
 			if (temp < WLAN_EN_TEMP_THRESHOLD)
@@ -895,6 +929,9 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
 		}
 	}
 
+	if (priv->device_id == WCN6450_DEVICE_ID)
+		icnss_hw_power_off(priv);
+
 	ret = wlfw_cap_send_sync_msg(priv);
 	if (ret < 0) {
 		ignore_assert = true;
@@ -905,7 +942,8 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
 	if (ret)
 		goto fail;
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		ret = wlfw_device_info_send_msg(priv);
 		if (ret < 0) {
 			ignore_assert = true;
@@ -945,7 +983,15 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv,
 			goto device_info_failure;
 	}
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6450_DEVICE_ID) {
+		ret = icnss_wlfw_qdss_dnld_send_sync(priv);
+		if (ret < 0)
+			icnss_pr_info("Failed to download qdss config file for WCN6450, ret = %d\n",
+				      ret);
+	}
+
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		if (!priv->fw_soc_wake_ack_irq)
 			register_soc_wake_notif(&priv->pdev->dev);
 
@@ -1071,6 +1117,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv)
 	clear_bit(ICNSS_PDR, &priv->state);
 	clear_bit(ICNSS_REJUVENATE, &priv->state);
 	clear_bit(ICNSS_PD_RESTART, &priv->state);
+	clear_bit(ICNSS_LOW_POWER, &priv->state);
 	priv->early_crash_ind = false;
 	priv->is_ssr = false;
 
@@ -1124,11 +1171,13 @@ static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data)
 	if (!priv)
 		return -ENODEV;
 
+	del_timer(&priv->recovery_timer);
 	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)
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID)
 		icnss_free_qdss_mem(priv);
 
 	icnss_pr_info("WLAN FW is ready: 0x%lx\n", priv->state);
@@ -1169,14 +1218,21 @@ static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data)
 
 	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 (priv->device_id == WCN6750_DEVICE_ID) {
+		ret = icnss_wlfw_qdss_dnld_send_sync(priv);
+		if (ret < 0)
+			icnss_pr_info("Failed to download qdss config file for WCN6750, ret = %d\n",
+				      ret);
+	}
 
-	if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state))
+	if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state)) {
+		mod_timer(&priv->recovery_timer,
+			  jiffies + msecs_to_jiffies(ICNSS_CAL_TIMEOUT));
 		ret = wlfw_wlan_mode_send_sync_msg(priv,
 			(enum wlfw_driver_mode_enum_v01)ICNSS_CALIBRATION);
-	else
+	} else {
 		icnss_driver_event_fw_ready_ind(priv, NULL);
+	}
 
 	return ret;
 }
@@ -1560,7 +1616,8 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
 	if (priv->force_err_fatal)
 		ICNSS_ASSERT(0);
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		icnss_send_smp2p(priv, ICNSS_RESET_MSG,
 				 ICNSS_SMP2P_OUT_SOC_WAKE);
 		icnss_send_smp2p(priv, ICNSS_RESET_MSG,
@@ -1759,6 +1816,19 @@ static int icnss_subsys_restart_level(struct icnss_priv *priv, void *data)
 	return ret;
 }
 
+static void icnss_wpss_self_recovery(struct work_struct *wpss_load_work)
+{
+	int ret;
+	struct icnss_priv *priv = icnss_get_plat_priv();
+
+	rproc_shutdown(priv->rproc);
+	ret = rproc_boot(priv->rproc);
+	if (ret) {
+		icnss_pr_err("Failed to self recover wpss rproc, ret: %d", ret);
+		rproc_put(priv->rproc);
+	}
+}
+
 static void icnss_driver_event_work(struct work_struct *work)
 {
 	struct icnss_priv *priv =
@@ -2063,20 +2133,25 @@ static int icnss_wpss_notifier_nb(struct notifier_block *nb,
 	if (code != QCOM_SSR_BEFORE_SHUTDOWN)
 		goto out;
 
+	if (priv->wpss_self_recovery_enabled)
+		del_timer(&priv->wpss_ssr_timer);
+
 	priv->is_ssr = true;
 
 	icnss_pr_info("WPSS went down, state: 0x%lx, crashed: %d\n",
 		      priv->state, notif->crashed);
 
+	if (priv->device_id == ADRASTEA_DEVICE_ID)
+		icnss_update_state_send_modem_shutdown(priv, data);
+
 	set_bit(ICNSS_FW_DOWN, &priv->state);
+	icnss_ignore_fw_timeout(true);
 
 	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)
@@ -2094,6 +2169,10 @@ static int icnss_wpss_notifier_nb(struct notifier_block *nb,
 	}
 	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
 				ICNSS_EVENT_SYNC, event_data);
+
+	if (notif->crashed)
+		mod_timer(&priv->recovery_timer,
+			  jiffies + msecs_to_jiffies(ICNSS_RECOVERY_TIMEOUT));
 out:
 	icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state);
 	return NOTIFY_OK;
@@ -2112,14 +2191,28 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
 	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;
-	}
+	switch (code) {
+	case QCOM_SSR_BEFORE_SHUTDOWN:
+		if (!notif->crashed &&
+		    priv->low_power_support) { /* Hibernate */
+			if (test_bit(ICNSS_MODE_ON, &priv->state))
+				icnss_driver_event_post(
+					priv, ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
+					ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
+			set_bit(ICNSS_LOW_POWER, &priv->state);
+		}
+		break;
+	case QCOM_SSR_AFTER_SHUTDOWN:
+		/* Collect ramdump only when there was a crash. */
+		if (notif->crashed) {
+			icnss_pr_info("Collecting msa0 segment dump\n");
+			icnss_msa0_ramdump(priv);
+		}
 
-	if (code != QCOM_SSR_BEFORE_SHUTDOWN)
 		goto out;
+	default:
+		goto out;
+	}
 
 	priv->is_ssr = true;
 
@@ -2171,6 +2264,10 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
 	}
 	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
 				ICNSS_EVENT_SYNC, event_data);
+
+	if (notif->crashed)
+		mod_timer(&priv->recovery_timer,
+			  jiffies + msecs_to_jiffies(ICNSS_RECOVERY_TIMEOUT));
 out:
 	icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state);
 	return NOTIFY_OK;
@@ -2351,6 +2448,9 @@ static void icnss_pdr_notifier_cb(int state, char *service_path, void *priv_cb)
 	struct icnss_uevent_fw_down_data fw_down_data = {0};
 	enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;
 
+	if (!priv)
+		return;
+
 	icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
 		     state, priv->state);
 
@@ -2393,6 +2493,12 @@ static void icnss_pdr_notifier_cb(int state, char *service_path, void *priv_cb)
 			}
 		}
 		clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+
+		if (event_data->crashed)
+			mod_timer(&priv->recovery_timer,
+				  jiffies +
+				  msecs_to_jiffies(ICNSS_RECOVERY_TIMEOUT));
+
 		icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
 					ICNSS_EVENT_SYNC, event_data);
 		break;
@@ -2527,7 +2633,8 @@ static int icnss_register_ramdump_devices(struct icnss_priv *priv)
 		return -ENOMEM;
 	}
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		priv->m3_dump_phyareg = icnss_create_ramdump_device(priv,
 						ICNSS_M3_SEGMENT(
 						ICNSS_M3_SEGMENT_PHYAREG));
@@ -2883,7 +2990,7 @@ out:
 }
 EXPORT_SYMBOL(icnss_unregister_driver);
 
-static struct icnss_msi_config msi_config = {
+static struct icnss_msi_config msi_config_wcn6750 = {
 	.total_vectors = 28,
 	.total_users = 2,
 	.users = (struct icnss_msi_user[]) {
@@ -2892,9 +2999,20 @@ static struct icnss_msi_config msi_config = {
 	},
 };
 
+static struct icnss_msi_config msi_config_wcn6450 = {
+	.total_vectors = 10,
+	.total_users = 1,
+	.users = (struct icnss_msi_user[]) {
+		{ .name = "CE", .num_vectors = 10, .base_vector = 0 },
+	},
+};
+
 static int icnss_get_msi_assignment(struct icnss_priv *priv)
 {
-	priv->msi_config = &msi_config;
+	if (priv->device_id == WCN6750_DEVICE_ID)
+		priv->msi_config = &msi_config_wcn6750;
+	else
+		priv->msi_config = &msi_config_wcn6450;
 
 	return 0;
 }
@@ -3336,7 +3454,7 @@ int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
 		      const char *host_version)
 {
 	struct icnss_priv *priv = dev_get_drvdata(dev);
-	int temp = 0;
+	int temp = 0, ret = 0;
 
 	if (test_bit(ICNSS_FW_DOWN, &priv->state) ||
 	    !test_bit(ICNSS_FW_READY, &priv->state)) {
@@ -3363,7 +3481,15 @@ int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config,
 		}
 	}
 
-	return icnss_send_wlan_enable_to_fw(priv, config, mode, host_version);
+	if (priv->device_id == WCN6450_DEVICE_ID)
+		icnss_hw_power_off(priv);
+
+	ret = icnss_send_wlan_enable_to_fw(priv, config, mode, host_version);
+
+	if (priv->device_id == WCN6450_DEVICE_ID)
+		icnss_hw_power_on(priv);
+
+	return ret;
 }
 EXPORT_SYMBOL(icnss_wlan_enable);
 
@@ -3799,15 +3925,6 @@ static void icnss_wpss_load(struct work_struct *wpss_load_work)
 	}
 }
 
-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)
@@ -3840,7 +3957,7 @@ static ssize_t wlan_en_delay_store(struct device *dev,
 	struct icnss_priv *priv = dev_get_drvdata(dev);
 	uint32_t wlan_en_delay  = 0;
 
-	if (priv->device_id != WCN6750_DEVICE_ID)
+	if (priv->device_id == ADRASTEA_DEVICE_ID)
 		return count;
 
 	if (sscanf(buf, "%du", &wlan_en_delay) != 1) {
@@ -3990,6 +4107,12 @@ static int icnss_resource_parse(struct icnss_priv *priv)
 			}
 		}
 
+		if (of_property_read_bool(pdev->dev.of_node,
+					  "qcom,is_low_power")) {
+			priv->low_power_support = true;
+			icnss_pr_dbg("Deep Sleep/Hibernate mode supported\n");
+		}
+
 		if (of_property_read_u32(pdev->dev.of_node, "qcom,rf_subtype",
 					 &priv->rf_subtype) == 0) {
 			priv->is_rf_subtype_valid = true;
@@ -4001,7 +4124,8 @@ static int icnss_resource_parse(struct icnss_priv *priv)
 			priv->is_slate_rfa = true;
 			icnss_pr_err("SLATE rfa is enabled\n");
 		}
-	} else if (priv->device_id == WCN6750_DEVICE_ID) {
+	} else if (priv->device_id == WCN6750_DEVICE_ID ||
+		   priv->device_id == WCN6450_DEVICE_ID) {
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						   "msi_addr");
 		if (!res) {
@@ -4038,7 +4162,7 @@ static int icnss_resource_parse(struct icnss_priv *priv)
 			     priv->msi_base_data, int_prop);
 
 		icnss_get_msi_assignment(priv);
-		for (i = 0; i < msi_config.total_vectors; i++) {
+		for (i = 0; i < priv->msi_config->total_vectors; i++) {
 			res = platform_get_resource(priv->pdev,
 						    IORESOURCE_IRQ, i);
 			if (!res) {
@@ -4189,7 +4313,8 @@ static int icnss_smmu_dt_parse(struct icnss_priv *priv)
 		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)
+			if (priv->device_id == WCN6750_DEVICE_ID ||
+			    priv->device_id == WCN6450_DEVICE_ID)
 				iommu_set_fault_handler(priv->iommu_domain,
 						icnss_smmu_fault_handler,
 						priv);
@@ -4255,16 +4380,19 @@ void icnss_add_fw_prefix_name(struct icnss_priv *priv, char *prefix_name,
 	if (priv->device_id == ADRASTEA_DEVICE_ID)
 		scnprintf(prefix_name, ICNSS_MAX_FILE_NAME,
 			  ADRASTEA_PATH_PREFIX "%s", name);
-	else
+	else if (priv->device_id == WCN6750_DEVICE_ID)
 		scnprintf(prefix_name, ICNSS_MAX_FILE_NAME,
 			  QCA6750_PATH_PREFIX "%s", name);
-
+	else if (priv->device_id == WCN6450_DEVICE_ID)
+		scnprintf(prefix_name, ICNSS_MAX_FILE_NAME,
+			  WCN6450_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, },
+	{ .name = "wcn6450", .driver_data = WCN6450_DEVICE_ID, },
 	{ },
 };
 
@@ -4275,6 +4403,9 @@ static const struct of_device_id icnss_dt_match[] = {
 	{
 		.compatible = "qcom,icnss",
 		.data = (void *)&icnss_platform_id_table[1]},
+	{
+		.compatible = "qcom,wcn6450",
+		.data = (void *)&icnss_platform_id_table[2]},
 	{ },
 };
 
@@ -4305,6 +4436,10 @@ static void icnss_read_device_configs(struct icnss_priv *priv)
 				  "wlan-ipa-disabled")) {
 		set_bit(ICNSS_IPA_DISABLED, &priv->device_config);
 	}
+
+	if (of_property_read_bool(priv->pdev->dev.of_node,
+				  "qcom,wpss-self-recovery"))
+		priv->wpss_self_recovery_enabled = true;
 }
 
 static inline void icnss_runtime_pm_init(struct icnss_priv *priv)
@@ -4444,7 +4579,8 @@ static int icnss_probe(struct platform_device *pdev)
 	if (priv->is_slate_rfa)
 		init_completion(&priv->slate_boot_complete);
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		priv->soc_wake_wq = alloc_workqueue("icnss_soc_wake_event",
 						    WQ_UNBOUND|WQ_HIGHPRI, 1);
 		if (!priv->soc_wake_wq) {
@@ -4478,6 +4614,15 @@ static int icnss_probe(struct platform_device *pdev)
 		INIT_WORK(&wpss_loader, icnss_wpss_load);
 	}
 
+	timer_setup(&priv->recovery_timer,
+		    icnss_recovery_timeout_hdlr, 0);
+
+	if (priv->wpss_self_recovery_enabled) {
+		INIT_WORK(&wpss_ssr_work, icnss_wpss_self_recovery);
+		timer_setup(&priv->wpss_ssr_timer,
+			    icnss_wpss_ssr_timeout_hdlr, 0);
+	}
+
 	INIT_LIST_HEAD(&priv->icnss_tcdev_list);
 
 	icnss_pr_info("Platform driver probed successfully\n");
@@ -4528,6 +4673,11 @@ static int icnss_remove(struct platform_device *pdev)
 
 	icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state);
 
+	del_timer(&priv->recovery_timer);
+
+	if (priv->wpss_self_recovery_enabled)
+		del_timer(&priv->wpss_ssr_timer);
+
 	device_init_wakeup(&priv->pdev->dev, false);
 
 	icnss_debugfs_destroy(priv);
@@ -4552,7 +4702,8 @@ static int icnss_remove(struct platform_device *pdev)
 		icnss_pdr_unregister_notifier(priv);
 	}
 
-	if (priv->device_id == WCN6750_DEVICE_ID) {
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID) {
 		icnss_genl_exit();
 		icnss_runtime_pm_deinit(priv);
 		if (!IS_ERR_OR_NULL(priv->mbox_chan))
@@ -4586,6 +4737,23 @@ static int icnss_remove(struct platform_device *pdev)
 	return 0;
 }
 
+void icnss_recovery_timeout_hdlr(struct timer_list *t)
+{
+	struct icnss_priv *priv = from_timer(priv, t, recovery_timer);
+
+	icnss_pr_err("Timeout waiting for FW Ready 0x%lx\n", priv->state);
+	ICNSS_ASSERT(0);
+}
+
+void icnss_wpss_ssr_timeout_hdlr(struct timer_list *t)
+{
+	struct icnss_priv *priv = from_timer(priv, t, wpss_ssr_timer);
+
+	icnss_pr_err("Timeout waiting for WPSS SSR notification 0x%lx\n",
+		      priv->state);
+	schedule_work(&wpss_ssr_work);
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int icnss_pm_suspend(struct device *dev)
 {
@@ -4608,7 +4776,8 @@ static int icnss_pm_suspend(struct device *dev)
 	ret = priv->ops->pm_suspend(dev);
 
 	if (ret == 0) {
-		if (priv->device_id == WCN6750_DEVICE_ID) {
+		if (priv->device_id == WCN6750_DEVICE_ID ||
+		    priv->device_id == WCN6450_DEVICE_ID) {
 			if (test_bit(ICNSS_PD_RESTART, &priv->state) ||
 			    !test_bit(ICNSS_MODE_ON, &priv->state))
 				return 0;
@@ -4717,7 +4886,7 @@ 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) {
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
 		icnss_pr_err("Ignore runtime suspend:\n");
 		goto out;
 	}
@@ -4751,8 +4920,8 @@ 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");
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		icnss_pr_err("Ignore runtime resume\n");
 		goto out;
 	}
 
@@ -4778,8 +4947,8 @@ 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");
+	if (priv->device_id == ADRASTEA_DEVICE_ID) {
+		icnss_pr_err("Ignore runtime idle\n");
 		goto out;
 	}
 

+ 10 - 0
icnss2/main.h

@@ -19,14 +19,17 @@
 #endif
 #include "wlan_firmware_service_v01.h"
 #include <linux/mailbox_client.h>
+#include <linux/timer.h>
 
 #define WCN6750_DEVICE_ID 0x6750
+#define WCN6450_DEVICE_ID 0x6450
 #define ADRASTEA_DEVICE_ID 0xabcd
 #define THERMAL_NAME_LENGTH 20
 #define ICNSS_SMEM_VALUE_MASK 0xFFFFFFFF
 #define ICNSS_SMEM_SEQ_NO_POS 16
 #define QCA6750_PATH_PREFIX    "qca6750/"
 #define ADRASTEA_PATH_PREFIX   "adrastea/"
+#define WCN6450_PATH_PREFIX    "wcn6450/"
 #define ICNSS_MAX_FILE_NAME      35
 #define ICNSS_PCI_EP_WAKE_OFFSET 4
 #define ICNSS_DISABLE_M3_SSR 0
@@ -127,6 +130,7 @@ enum icnss_driver_state {
 	ICNSS_QMI_DMS_CONNECTED,
 	ICNSS_SLATE_SSR_REGISTERED,
 	ICNSS_SLATE_UP,
+	ICNSS_LOW_POWER,
 };
 
 struct ce_irq_list {
@@ -497,10 +501,14 @@ struct icnss_priv {
 	struct workqueue_struct *soc_update_wq;
 	unsigned long device_config;
 	bool wpss_supported;
+	u8 low_power_support;
 	bool is_rf_subtype_valid;
 	u32 rf_subtype;
 	u8 is_slate_rfa;
 	struct completion slate_boot_complete;
+	struct timer_list recovery_timer;
+	struct timer_list wpss_ssr_timer;
+	bool wpss_self_recovery_enabled;
 };
 
 struct icnss_reg_info {
@@ -528,5 +536,7 @@ 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);
+void icnss_recovery_timeout_hdlr(struct timer_list *t);
+void icnss_wpss_ssr_timeout_hdlr(struct timer_list *t);
 #endif
 

+ 15 - 1
icnss2/power.c

@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -38,6 +39,13 @@ static struct icnss_battery_level icnss_battery_level[] = {
 	{0, 2850000},
 };
 
+static struct icnss_vreg_cfg icnss_wcn6450_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},
+	{"vdd-aon", 1256000, 1352000, 0, 0, 0, false, true},
+};
+
 static struct icnss_clk_cfg icnss_clk_list[] = {
 	{"rf_clk", 0, 0},
 };
@@ -48,6 +56,7 @@ static struct icnss_clk_cfg icnss_adrestea_clk_list[] = {
 
 #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_VREG_EVROS_LIST_SIZE	ARRAY_SIZE(icnss_wcn6450_vreg_list)
 #define ICNSS_CLK_LIST_SIZE		ARRAY_SIZE(icnss_clk_list)
 #define ICNSS_CLK_ADRESTEA_LIST_SIZE	ARRAY_SIZE(icnss_adrestea_clk_list)
 
@@ -311,6 +320,10 @@ static struct icnss_vreg_cfg *get_vreg_list(u32 *vreg_list_size,
 		*vreg_list_size = ICNSS_VREG_ADRESTEA_LIST_SIZE;
 		return icnss_adrestea_vreg_list;
 
+	case WCN6450_DEVICE_ID:
+		*vreg_list_size = ICNSS_VREG_EVROS_LIST_SIZE;
+		return icnss_wcn6450_vreg_list;
+
 	default:
 		icnss_pr_err("Unsupported device_id 0x%x\n", device_id);
 		*vreg_list_size = 0;
@@ -524,7 +537,8 @@ int icnss_get_clk(struct icnss_priv *priv)
 	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) {
+	} else if (priv->device_id == WCN6750_DEVICE_ID ||
+		   priv->device_id == WCN6450_DEVICE_ID) {
 		clk_cfg = icnss_clk_list;
 		clk_list_size = ICNSS_CLK_LIST_SIZE;
 	}

+ 19 - 3
icnss2/qmi.c

@@ -1,7 +1,7 @@
 // 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.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #define pr_fmt(fmt) "icnss2_qmi: " fmt
@@ -55,6 +55,9 @@
 #define DMS_MAC_NOT_PROVISIONED		16
 #define BDWLAN_SIZE			6
 #define UMC_CHIP_ID                    0x4320
+#define MAX_SHADOW_REG_RESERVED		2
+#define MAX_NUM_SHADOW_REG_V3		(QMI_WLFW_MAX_NUM_SHADOW_REG_V3_USAGE_V01 - \
+					MAX_SHADOW_REG_RESERVED)
 
 #ifdef CONFIG_ICNSS2_DEBUG
 bool ignore_fw_timeout;
@@ -546,7 +549,8 @@ int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
 			req->rejuvenate_enable_valid = 1;
 			req->rejuvenate_enable = 1;
 		}
-	} else if (priv->device_id == WCN6750_DEVICE_ID) {
+	} else if (priv->device_id == WCN6750_DEVICE_ID ||
+		   priv->device_id == WCN6450_DEVICE_ID) {
 		req->fw_init_done_enable_valid = 1;
 		req->fw_init_done_enable = 1;
 		req->cal_done_enable_valid = 1;
@@ -2969,7 +2973,8 @@ int icnss_register_fw_service(struct icnss_priv *priv)
 	if (ret < 0)
 		return ret;
 
-	if (priv->device_id == WCN6750_DEVICE_ID)
+	if (priv->device_id == WCN6750_DEVICE_ID ||
+	    priv->device_id == WCN6450_DEVICE_ID)
 		ret = qmi_add_lookup(&priv->qmi, WLFW_SERVICE_ID_V01,
 				     WLFW_SERVICE_VERS_V01,
 				     WLFW_SERVICE_WCN_INS_ID_V01);
@@ -3060,6 +3065,17 @@ int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
 
 		memcpy(req.shadow_reg, config->shadow_reg_cfg,
 		       sizeof(struct wlfw_msi_cfg_s_v01) * req.shadow_reg_len);
+	} else if (priv->device_id == WCN6450_DEVICE_ID) {
+		req.shadow_reg_v3_valid = 1;
+		if (config->num_shadow_reg_v3_cfg >
+			MAX_NUM_SHADOW_REG_V3)
+			req.shadow_reg_v3_len = MAX_NUM_SHADOW_REG_V3;
+		else
+			req.shadow_reg_v3_len = config->num_shadow_reg_v3_cfg;
+
+		memcpy(req.shadow_reg_v3, config->shadow_reg_v3_cfg,
+		       sizeof(struct wlfw_shadow_reg_v3_cfg_s_v01)
+		       * req.shadow_reg_v3_len);
 	}
 
 	ret = wlfw_wlan_cfg_send_sync_msg(priv, &req);

+ 11 - 6
inc/cnss2.h

@@ -15,12 +15,6 @@
 #define CNSS_MAX_DEV_MEM_NUM		4
 #define CNSS_CHIP_VER_ANY		0
 
-/*
- * Temporary change for compilation, will be removed
- * after WLAN host driver switched to use new APIs
- */
-#define CNSS_API_WITH_DEV
-
 #define CNSS_SSR_DRIVER_DUMP_MAX_REGIONS 32
 
 enum cnss_bus_width_type {
@@ -179,6 +173,9 @@ struct cnss_wlan_driver {
 	int (*collect_driver_dump)(struct pci_dev *pdev,
 				   struct cnss_ssr_driver_dump_entry *input_array,
 				   size_t *num_entries_loaded);
+	int (*set_therm_cdev_state)(struct pci_dev *pci_dev,
+				    unsigned long thermal_state,
+				    int tcdev_id);
 };
 
 struct cnss_ce_tgt_pipe_cfg {
@@ -227,6 +224,7 @@ struct cnss_wlan_enable_cfg {
 	struct cnss_rri_over_ddr_cfg rri_over_ddr_cfg;
 	u32 num_shadow_reg_v3_cfg;
 	struct cnss_shadow_reg_v3_cfg *shadow_reg_v3_cfg;
+	bool send_msi_ce;
 };
 
 enum cnss_driver_mode {
@@ -353,4 +351,11 @@ extern int cnss_send_buffer_to_afcmem(struct device *dev, char *afcdb,
 extern int cnss_reset_afcmem(struct device *dev, uint8_t slotid);
 extern bool cnss_get_fw_cap(struct device *dev, enum cnss_fw_caps fw_cap);
 extern int cnss_set_wfc_mode(struct device *dev, struct cnss_wfc_cfg cfg);
+extern int cnss_thermal_cdev_register(struct device *dev,
+				      unsigned long max_state,
+				      int tcdev_id);
+extern void cnss_thermal_cdev_unregister(struct device *dev, int tcdev_id);
+extern int cnss_get_curr_therm_cdev_state(struct device *dev,
+					  unsigned long *thermal_state,
+					  int tcdev_id);
 #endif /* _NET_CNSS2_H */

+ 8 - 4
inc/icnss2.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #ifndef _ICNSS_WLAN_H_
 #define _ICNSS_WLAN_H_
@@ -13,10 +14,6 @@
 #define ICNSS_MAX_TIMESTAMP_LEN        32
 #define ICNSS_WLFW_MAX_BUILD_ID_LEN    128
 
-#ifndef ICNSS_API_WITH_DEV
-#define ICNSS_API_WITH_DEV
-#endif
-
 #define DEVICE_NAME_MAX		10
 enum icnss_uevent {
 	ICNSS_UEVENT_FW_CRASHED,
@@ -98,6 +95,10 @@ struct icnss_shadow_reg_v2_cfg {
 	u32 addr;
 };
 
+struct icnss_shadow_reg_v3_cfg {
+	u32 addr;
+};
+
 struct icnss_rri_over_ddr_cfg {
 	u32 base_addr_low;
 	u32 base_addr_high;
@@ -112,6 +113,8 @@ struct icnss_wlan_enable_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;
+	u32 num_shadow_reg_v3_cfg;
+	struct icnss_shadow_reg_v3_cfg *shadow_reg_v3_cfg;
 	bool rri_over_ddr_cfg_valid;
 	struct icnss_rri_over_ddr_cfg rri_over_ddr_cfg;
 };
@@ -180,6 +183,7 @@ 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_low_power(void);
 extern bool icnss_is_rejuvenate(void);
 extern int icnss_trigger_recovery(struct device *dev);
 extern void icnss_block_shutdown(bool status);