From 1d66a24813d54f95369ee4062f62e692c8a92772 Mon Sep 17 00:00:00 2001 From: Dundi Raviteja Date: Mon, 30 Jan 2023 12:03:06 +0530 Subject: [PATCH 01/29] icnss2: Update slatecom_interface.h file path slatecom_interface.h file is moved to include/ in kernel, update the same in icnss2 also. Change-Id: Ibd07235636066b48221de9928283bfdf24b90221 CRs-Fixed: 3390989 --- icnss2/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icnss2/main.c b/icnss2/main.c index ab4ee2f85e..ca58cf1c78 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -45,7 +45,7 @@ #include #include #include -#include <../drivers/soc/qcom/slatecom_interface.h> +#include #include "main.h" #include "qmi.h" #include "debug.h" From f2346285bc0009795abbe0d0c58d5861216eee55 Mon Sep 17 00:00:00 2001 From: Chaoli Zhou Date: Wed, 18 Jan 2023 11:31:32 +0800 Subject: [PATCH 02/29] cnss2: Support dual wlan cards managed by cnss2 platform driver Currently, the cnss2 platform driver just can support one wlan device attached, so do the following changes to cover dual wlan card attach case. Firstly, delay the second device go to pcie link suspend and power off state to make sure it really finished the pcie enumeration. Then pcie enumeration succeeds with multi-devices. Secondly, supporting to write the qrtr node instance id to PCIE register for wlan fw reading, which can fix qmi message exchange failure if active two Hastings devices. Thirdly, change the usage of plat_env to support dual Hastings. Change-Id: Ica41a23d4e983b91c0ff1b4e76b380803fb877ab --- Kbuild | 4 + cnss2/Kconfig | 8 ++ cnss2/debug.c | 11 +- cnss2/main.c | 245 ++++++++++++++++++++++++++++++++++++++----- cnss2/main.h | 21 ++++ cnss2/pci.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++--- cnss2/pci.h | 6 ++ cnss2/qmi.c | 19 +++- cnss2/reg.h | 1 + 9 files changed, 551 insertions(+), 44 deletions(-) diff --git a/Kbuild b/Kbuild index 0643c05047..e273439896 100644 --- a/Kbuild +++ b/Kbuild @@ -36,6 +36,10 @@ ifeq ($(CONFIG_CNSS2_CONDITIONAL_POWEROFF),y) KBUILD_CPPFLAGS += -DCONFIG_CNSS2_CONDITIONAL_POWEROFF 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 diff --git a/cnss2/Kconfig b/cnss2/Kconfig index a71b48f3a4..09747fc31e 100644 --- a/cnss2/Kconfig +++ b/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 diff --git a/cnss2/debug.c b/cnss2/debug.c index 59054ca7c3..0ab9f578f4 100644 --- a/cnss2/debug.c +++ b/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 @@ -944,8 +944,15 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv) { int ret = 0; struct dentry *root_dentry; + char name[CNSS_FS_NAME_SIZE]; - root_dentry = debugfs_create_dir("cnss", 0); + 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(name, 0); if (IS_ERR(root_dentry)) { ret = PTR_ERR(root_dentry); cnss_pr_err("Unable to create debugfs %d\n", ret); diff --git a/cnss2/main.c b/cnss2/main.c index 5516746c04..81bc2c1a43 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -88,7 +88,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,22 +117,174 @@ 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) +{ + 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++; + } +} + +struct cnss_plat_data *cnss_get_plat_priv(struct platform_device + *plat_dev) +{ + 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; } -bool cnss_check_driver_loading_allowed(void) -{ - return cnss_allow_driver_loading; -} - 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 @@ -2482,7 +2640,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; @@ -3861,8 +4019,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]; - ret = sysfs_create_link(kernel_kobj, &dev->kobj, "cnss"); + 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_name); if (ret) { cnss_pr_err("Failed to create cnss link, err = %d\n", ret); @@ -3870,7 +4041,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 +4051,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) @@ -4371,6 +4556,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 +4592,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 +4641,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,10 +4670,6 @@ 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); - cnss_pr_info("Platform driver probed successfully.\n"); return 0; @@ -4486,8 +4680,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 +4693,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 +4722,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 +4791,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(); } diff --git a/cnss2/main.h b/cnss2/main.h index 460b73d696..1b4bf3e4cc 100644 --- a/cnss2/main.h +++ b/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 @@ -578,6 +588,12 @@ 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; }; #if IS_ENABLED(CONFIG_ARCH_QCOM) @@ -605,6 +621,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); diff --git a/cnss2/pci.c b/cnss2/pci.c index 2825a44d03..4084f05bfb 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -68,6 +68,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 +84,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 @@ -667,6 +671,8 @@ 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); + #if IS_ENABLED(CONFIG_MHI_BUS_MISC) static void cnss_mhi_debug_reg_dump(struct cnss_pci_data *pci_priv) @@ -1933,11 +1939,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 +2116,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)); @@ -3111,7 +3273,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 +3283,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 +3380,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; @@ -5907,6 +6072,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; @@ -6150,6 +6319,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 +6458,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 +6482,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) { @@ -6336,7 +6565,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, 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 +6590,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; @@ -6443,7 +6680,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 +6752,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 +6783,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; + } } diff --git a/cnss2/pci.h b/cnss2/pci.h index 1b35cc687c..40de540ae7 100644 --- a/cnss2/pci.h +++ b/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 diff --git a/cnss2/qmi.c b/cnss2/qmi.c index b7c88a5c0f..bfc9b1c542 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -3211,9 +3211,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 +3240,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); diff --git a/cnss2/reg.h b/cnss2/reg.h index fa55bf42dc..e670979f81 100644 --- a/cnss2/reg.h +++ b/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 From 3a589a650e88725d84d9aca1811c792473211b1c Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 1 Feb 2023 15:26:29 +0530 Subject: [PATCH 03/29] icnss2: Remove wlan driver on device shutdown This change shutdown wpss on device shutdown for adrastea targets. With this change icnss driver unloads wlan host driver in wpss shutdown callback. Change-Id: I6d7d2563e694786b8d74725d77dc8c25943198e9 CRs-Fixed: 3392307 --- icnss2/main.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index ca58cf1c78..54179f72e8 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -132,14 +132,26 @@ 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); 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; } @@ -2068,15 +2080,17 @@ static int icnss_wpss_notifier_nb(struct notifier_block *nb, 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) @@ -3799,15 +3813,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) From 54df2d964c9f35ecc9c5c0f2bb7692cadf093e1c Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 1 Feb 2023 16:43:08 +0530 Subject: [PATCH 04/29] icnss2: Add ASSERT if fw ready got timed out Recovery timer is used to detect FW_READY timeout(60 Sec) during SSR/PDR recovery and assert. Same timer starts before cold boot calibration also and expires in 40 sec if FW_READY is not received. Change-Id: Ie4e2a327aa4f4df49b8684f9c178b9b5f4d4fb15 CRs-Fixed: 3221566 --- icnss2/main.c | 37 +++++++++++++++++++++++++++++++++++-- icnss2/main.h | 3 +++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index 54179f72e8..72f3de7898 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -83,6 +83,9 @@ module_param(qmi_timeout, ulong, 0600); #define WLFW_TIMEOUT msecs_to_jiffies(3000) #endif +#define ICNSS_RECOVERY_TIMEOUT 60000 +#define ICNSS_CAL_TIMEOUT 40000 + static struct icnss_priv *penv; static struct work_struct wpss_loader; uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK; @@ -1136,6 +1139,7 @@ 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); @@ -1184,11 +1188,14 @@ static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data) if (icnss_wlfw_qdss_dnld_send_sync(priv)) icnss_pr_info("Failed to download qdss configuration file"); - if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state)) + 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; } @@ -2108,6 +2115,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; @@ -2185,6 +2196,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; @@ -2409,6 +2424,11 @@ static void icnss_pdr_notifier_cb(int state, char *service_path, void *priv_cb) clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state); icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); + + if (event_data->crashed) + mod_timer(&priv->recovery_timer, + jiffies + + msecs_to_jiffies(ICNSS_RECOVERY_TIMEOUT)); break; case SERVREG_SERVICE_STATE_UP: clear_bit(ICNSS_FW_DOWN, &priv->state); @@ -4483,6 +4503,9 @@ 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); + INIT_LIST_HEAD(&priv->icnss_tcdev_list); icnss_pr_info("Platform driver probed successfully\n"); @@ -4533,6 +4556,8 @@ static int icnss_remove(struct platform_device *pdev) icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state); + del_timer(&priv->recovery_timer); + device_init_wakeup(&priv->pdev->dev, false); icnss_debugfs_destroy(priv); @@ -4591,6 +4616,14 @@ 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); +} + #ifdef CONFIG_PM_SLEEP static int icnss_pm_suspend(struct device *dev) { diff --git a/icnss2/main.h b/icnss2/main.h index 9269e15589..c862781da8 100644 --- a/icnss2/main.h +++ b/icnss2/main.h @@ -19,6 +19,7 @@ #endif #include "wlan_firmware_service_v01.h" #include +#include #define WCN6750_DEVICE_ID 0x6750 #define ADRASTEA_DEVICE_ID 0xabcd @@ -501,6 +502,7 @@ struct icnss_priv { u32 rf_subtype; u8 is_slate_rfa; struct completion slate_boot_complete; + struct timer_list recovery_timer; }; struct icnss_reg_info { @@ -528,5 +530,6 @@ 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); #endif From 172b8aee5229b58cf135325528832d9545982245 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 1 Feb 2023 16:54:45 +0530 Subject: [PATCH 05/29] icnss2: trigger wpss self recovery Icnss driver receives early crash indication when fatal error happens in wpss. In few cases rproc framework didn't receive any notification regarding wpss fatal crash due to which icnss driver wpss notfier is not getting called. This prevents wpss ssr recovery. To recover wpss from this scenerio icnss driver tries to do self recovery of wpss subystem. Change-Id: Ie22dfb6a6218aafd1fec84fb14f3efb16c60bf5e CRs-Fixed: 3265930 --- icnss2/main.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ icnss2/main.h | 3 +++ 2 files changed, 47 insertions(+) diff --git a/icnss2/main.c b/icnss2/main.c index 72f3de7898..22aa75767b 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -84,10 +84,12 @@ module_param(qmi_timeout, ulong, 0600); #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 @@ -536,6 +538,10 @@ static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) icnss_pr_err("Received early crash indication from FW\n"); + if (priv->wpss_self_recovery_enabled) + mod_timer(&priv->wpss_ssr_timer, + jiffies + msecs_to_jiffies(ICNSS_WPSS_SSR_TIMEOUT)); + if (priv) { set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_fw_timeout(true); @@ -1778,6 +1784,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 = @@ -2082,6 +2101,9 @@ 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", @@ -4330,6 +4352,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) @@ -4506,6 +4532,12 @@ static int icnss_probe(struct platform_device *pdev) 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"); @@ -4558,6 +4590,9 @@ static int icnss_remove(struct platform_device *pdev) 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); @@ -4624,6 +4659,15 @@ void icnss_recovery_timeout_hdlr(struct timer_list *t) 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) { diff --git a/icnss2/main.h b/icnss2/main.h index c862781da8..1a09894946 100644 --- a/icnss2/main.h +++ b/icnss2/main.h @@ -503,6 +503,8 @@ struct icnss_priv { 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 { @@ -531,5 +533,6 @@ 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 From 2ac21974774dc2cbc46ee6e2778115ab8a8a60b4 Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Thu, 2 Feb 2023 15:11:25 +0530 Subject: [PATCH 06/29] cnss2: icnss2: Remove unused macro Remove unused temporary macros CNSS_API_WITH_DEV and ICNSS_API_WITH_DEV. Change-Id: I5dff354c08b6772d85d9fe0814e1b3a501810df9 CRs-Fixed: 3394987 --- inc/cnss2.h | 6 ------ inc/icnss2.h | 5 +---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/inc/cnss2.h b/inc/cnss2.h index 698b0a3793..8ac483e9dd 100644 --- a/inc/cnss2.h +++ b/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 { diff --git a/inc/icnss2.h b/inc/icnss2.h index 4a421547c2..4adbb13faa 100644 --- a/inc/icnss2.h +++ b/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, From 6c9f906a94c222693c403590235a81e46eacabc6 Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Tue, 8 Nov 2022 15:52:05 +0530 Subject: [PATCH 07/29] icnss2: Add Low power mode to icnss driver state Deep Sleep is a system wide low power mode during which Modem would be brought down and DDR would be in self refresh mode (suspend-to-RAM) thus platform driver on receiving deep sleep indication goes to a state similar to SSR apart from the fact that FW is not crashed but deliberately brought down. In msm-5.15, there are no seperate notifications used for deep sleep, notifications used for hibernate like BEFORE_SHUTDOWN, AFTER_SHUTDOWN, BEFORE_POWERUP and AFTER_POWERUP are used for deep sleep also. In this commit add only low power mode state to icnss driver state. Change-Id: I4b12c5a8841e3cfc3e5e44ab6d9dff141e472876 CRs-Fixed: 3394501 --- icnss2/debug.c | 3 +++ icnss2/main.c | 20 ++++++++++++++++---- icnss2/main.h | 1 + inc/icnss2.h | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/icnss2/debug.c b/icnss2/debug.c index c023272c05..44308f1a05 100644 --- a/icnss2/debug.c +++ b/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); diff --git a/icnss2/main.c b/icnss2/main.c index 22aa75767b..a5ee81f0e2 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -519,6 +519,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; @@ -1092,6 +1101,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; @@ -2159,14 +2169,16 @@ 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) { + switch (code) { + case QCOM_SSR_BEFORE_SHUTDOWN: + break; + case QCOM_SSR_AFTER_SHUTDOWN: icnss_pr_info("Collecting msa0 segment dump\n"); icnss_msa0_ramdump(priv); goto out; - } - - if (code != QCOM_SSR_BEFORE_SHUTDOWN) + default: goto out; + } priv->is_ssr = true; diff --git a/icnss2/main.h b/icnss2/main.h index 1a09894946..bd36ac98dd 100644 --- a/icnss2/main.h +++ b/icnss2/main.h @@ -128,6 +128,7 @@ enum icnss_driver_state { ICNSS_QMI_DMS_CONNECTED, ICNSS_SLATE_SSR_REGISTERED, ICNSS_SLATE_UP, + ICNSS_LOW_POWER, }; struct ce_irq_list { diff --git a/inc/icnss2.h b/inc/icnss2.h index 4adbb13faa..51e4e39f22 100644 --- a/inc/icnss2.h +++ b/inc/icnss2.h @@ -177,6 +177,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); From 26e2742f2f7416549da3fe74952bff57cfd2c3d4 Mon Sep 17 00:00:00 2001 From: Surabhi Vishnoi Date: Tue, 8 Nov 2022 16:17:34 +0530 Subject: [PATCH 08/29] icnss2: Add support for Hibernate low power mode Hibernate is a system wide power mode during which Modem would be brought down along with DDR and the RAM contents would be saved on the disk (suspend-to-Disk). Modem would be do graceful shutdown in Hibernate mode, so BEFORE_SUBSYS_DOWN notification will be received. Based on dtsi node "is_low_power", if BEFORE_SUBSYS_DOWN with notif->crashed bit not set is received, platform driver will trigger an idle shutdown and set low power mode. On AFTER_SUBSYS_DOWN notification, low power mode will be cleared if it was set and ramdump collection will happen only in case of crash. Change-Id: I7b618e7c23950e4055079cde237ed75d6861620f CRs-Fixed: 3394501 --- icnss2/main.c | 25 +++++++++++++++++++++++-- icnss2/main.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index a5ee81f0e2..d09b6df554 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -2171,10 +2171,25 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, 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: - icnss_pr_info("Collecting msa0 segment dump\n"); - icnss_msa0_ramdump(priv); + /* Collect ramdump only when there was a crash. */ + if (notif->crashed) { + icnss_pr_info("Collecting msa0 segment dump\n"); + icnss_msa0_ramdump(priv); + } + + if (test_bit(ICNSS_LOW_POWER, &priv->state) && + priv->low_power_support) + clear_bit(ICNSS_LOW_POWER, &priv->state); goto out; default: goto out; @@ -4049,6 +4064,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; diff --git a/icnss2/main.h b/icnss2/main.h index bd36ac98dd..d7b0c6f404 100644 --- a/icnss2/main.h +++ b/icnss2/main.h @@ -499,6 +499,7 @@ 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; From 8e0ff471795acc42469b3fb248f6a4007de6ea8a Mon Sep 17 00:00:00 2001 From: Ajit Vaishya Date: Tue, 8 Nov 2022 18:52:51 +0530 Subject: [PATCH 09/29] icnss2: Don't clear LP on subsystem AFTER SHUTDOWN Event As per Hibernate Entry (i.e. SUBSYS_BEFORE_SHUTDOWN with flag crashed is not set) and based on low power supports it set the state to ICNSS_LOW_POWER. While processing the SUBSYS_AFTER_SHUTDOWN, its reset the ICNSS_LOW_POWER state based on previous state already set state and low power supported. But before system freeze callback triggered, SUBSYS_AFTER_SHUTDOWN event received to wlan and due to which ICNSS_LOW_POWER state is clear and freeze callback failed, thus its leads to prevent Hibernate freeze on WLAN. This fix is to provides WLAN entered into Hibernate freeze state by not clearing ICNSS_LOW_POWER state in SUBSYS_AFTER_SHUTDOWN. Change-Id: I7b34d54c7ce5314e5092dbe2c0bc65ffe13d014d CRs-Fixed: 3394501 --- icnss2/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index d09b6df554..87cb6cc18f 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -2187,9 +2187,6 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, icnss_msa0_ramdump(priv); } - if (test_bit(ICNSS_LOW_POWER, &priv->state) && - priv->low_power_support) - clear_bit(ICNSS_LOW_POWER, &priv->state); goto out; default: goto out; From 6c09e62157ceaeb1ad92eff4f7e3dc559d865bba Mon Sep 17 00:00:00 2001 From: Gangadhar Kavalastramath Date: Wed, 1 Feb 2023 09:57:44 +0530 Subject: [PATCH 10/29] cnss2: Add code changes to support thermal mitigation Add cnss2 code changes to register with thermal framework to support thermal mitigation. Change-Id: I9d427603bfc7e6bb5d732fa3e29ff66c965e153b CRs-Fixed: 3386762 --- cnss2/bus.c | 18 +++++ cnss2/bus.h | 3 + cnss2/main.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++ cnss2/main.h | 11 +++ cnss2/pci.c | 19 +++++ cnss2/pci.h | 3 + inc/cnss2.h | 10 +++ 7 files changed, 257 insertions(+) diff --git a/cnss2/bus.c b/cnss2/bus.c index 0cd94ad6c4..a3084b1323 100644 --- a/cnss2/bus.c +++ b/cnss2/bus.c @@ -635,6 +635,24 @@ 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; + } +} + #if IS_ENABLED(CONFIG_MHI_BUS_MISC) void cnss_bus_disable_mhi_satellite_cfg(struct cnss_plat_data *plat_priv) { diff --git a/cnss2/bus.h b/cnss2/bus.h index c7c1055106..ae4c6e1ca2 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -77,4 +77,7 @@ 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); #endif /* _CNSS_BUS_H */ diff --git a/cnss2/main.c b/cnss2/main.c index 81bc2c1a43..49effed485 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)) #include @@ -4543,6 +4544,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; @@ -4670,6 +4860,9 @@ static int cnss_probe(struct platform_device *plat_dev) cnss_register_coex_service(plat_priv); cnss_register_ims_service(plat_priv); + mutex_init(&plat_priv->tcdev_lock); + INIT_LIST_HEAD(&plat_priv->cnss_tcdev_list); + cnss_pr_info("Platform driver probed successfully.\n"); return 0; diff --git a/cnss2/main.h b/cnss2/main.h index 1b4bf3e4cc..216c71b8fd 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -468,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; @@ -500,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; diff --git a/cnss2/pci.c b/cnss2/pci.c index 4084f05bfb..f4081d2858 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -2474,6 +2474,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) { diff --git a/cnss2/pci.h b/cnss2/pci.h index 40de540ae7..036b380982 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -301,4 +301,7 @@ 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); #endif /* _CNSS_PCI_H */ diff --git a/inc/cnss2.h b/inc/cnss2.h index 8ac483e9dd..ec32ef74b1 100644 --- a/inc/cnss2.h +++ b/inc/cnss2.h @@ -173,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 { @@ -347,4 +350,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 */ From 2684b4a9d80a1bd1e873d1ab2594f3fc712dff67 Mon Sep 17 00:00:00 2001 From: Lin Bai Date: Wed, 8 Feb 2023 13:42:27 +0800 Subject: [PATCH 11/29] cnss2: Avoid OS assert if SSR enabled Current implementation tries to collect FW dump when cnss shutdown, and then ASSERT after collection complete. Do not invoke CNSS_ASSERT if SSR enabled. Change-Id: I8d427603bfc7e6bb5d732fa3e29ff66c965e153b CRs-Fixed: 3399930 --- cnss2/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cnss2/pci.c b/cnss2/pci.c index f4081d2858..01780d8007 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -3025,7 +3025,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)) { From 13c9e92c31db97fad2edc79f5abd8df2acefc954 Mon Sep 17 00:00:00 2001 From: Lin Bai Date: Wed, 8 Feb 2023 18:39:33 +0800 Subject: [PATCH 12/29] cnss2: log the debug cmd for runtime_pm debugfs User may configure such runtime_pm debugfs unexpected during test, add the log to explicitly show what is configured. Change-Id: I7d427603bfc7e6bb5d732fa3e29ff66c965e153b CRs-Fixed: 3399930 --- cnss2/debug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cnss2/debug.c b/cnss2/debug.c index 0ab9f578f4..cb7560e850 100644 --- a/cnss2/debug.c +++ b/cnss2/debug.c @@ -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")) { From 640497899b85eb1a157768b63b6c264c338467cd Mon Sep 17 00:00:00 2001 From: Anuj Khera Date: Tue, 24 Jan 2023 18:18:55 +0530 Subject: [PATCH 13/29] cnss2: Send AOP msg to disable WLAN PDC in case of specific Board ID Send the AOP msg to disable WLAN PDC for boards which support on chip PMIC. Change-Id: I572799a727000b3f124f01919c013c637dee0e10 CRs-Fixed: 3386507 --- cnss2/main.c | 1 + cnss2/main.h | 2 ++ cnss2/power.c | 19 ++++++++++++++++++- cnss2/qmi.c | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cnss2/main.c b/cnss2/main.c index 49effed485..8fa4b18caf 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -4281,6 +4281,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) diff --git a/cnss2/main.h b/cnss2/main.h index 216c71b8fd..fa3bc86a9a 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -605,6 +605,8 @@ struct cnss_plat_data { 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) diff --git a/cnss2/power.c b/cnss2/power.c index 801bea131c..02cf16d019 100644 --- a/cnss2/power.c +++ b/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 @@ -1607,6 +1607,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 = @@ -1676,6 +1677,22 @@ 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); + + 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_dbg("On chip PMIC device ids not configured\n"); + } } int cnss_update_cpr_info(struct cnss_plat_data *plat_priv) diff --git a/cnss2/qmi.c b/cnss2/qmi.c index bfc9b1c542..20b7c63e44 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -597,6 +597,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, From 05abe90286c5a284fc73e2c98aa427206c13791d Mon Sep 17 00:00:00 2001 From: Jia Ding Date: Tue, 14 Feb 2023 13:27:31 +0800 Subject: [PATCH 14/29] Revert "wlan_platform: Update Makefile to build CNSS drivers for Atherton_target" cnss driver is supposed to be supported across platforms regardless of CONFIG_PCI_MSM. Hence using CONFIG_PCI_MSM to decide if cnss2 needs to be compiled is not correct. This reverts Change-Id: I9ab82aeca945d29d3608e65ecbe69902f8a5b47b. Change-Id: I1ab90e80694b0e45270f6b47666781d784f8ab76 CRs-Fixed: 3405347 --- Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 2ccbf2c82c..37c155124e 100644 --- a/Makefile +++ b/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 From 8fdf81fea53945c5dc10ccbad72accdcc2e0ecbd Mon Sep 17 00:00:00 2001 From: Chaoli Zhou Date: Mon, 13 Feb 2023 14:04:50 +0800 Subject: [PATCH 15/29] cnss2: Support conditional power off in the LPM for HST/HSP Should power off HST/HSP if it only finished pcie enumeration without WLAN driver load before do LPM. Otherwise, it will cause the mhi state switch from INIT to SUSPEND failure that block LPM. Change-Id: Ia2c5fbf0a2bf9c088be548eb533d7836f45a3cfd CRs-Fixed: 3283893 --- cnss2/pci.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cnss2/pci.c b/cnss2/pci.c index 01780d8007..921fbded48 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -672,6 +672,7 @@ static struct cnss_print_optimize print_optimize; 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) @@ -3612,7 +3613,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) @@ -3625,6 +3627,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 = From f9c51b9eb2d29ed4da5c347191708e12dec77a97 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Thu, 9 Feb 2023 23:39:31 +0530 Subject: [PATCH 16/29] cnss2: Enhance debugging information Add change to print file type which is getting downloaded from cnss2 platform driver to wlan fw. Change-Id: I737c9c04bc02a3cd320362f230a0862321ec312c CRs-Fixed: 3401928 --- cnss2/qmi.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/cnss2/qmi.c b/cnss2/qmi.c index 20b7c63e44..3219ace2a0 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -638,6 +638,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) @@ -857,8 +872,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) @@ -883,14 +898,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; @@ -916,8 +933,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; } @@ -928,21 +945,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; } From d1e484f2916beda488dabdf65ffb56d2f6c64384 Mon Sep 17 00:00:00 2001 From: Xiaoning Ma Date: Thu, 9 Feb 2023 07:10:24 +0000 Subject: [PATCH 17/29] cnss2: Support make target 'modules' Yocto module.bbclass calls 'make modules' from wlan platform root dir to compile external modules, it gets the error "make: *** No rule to make target 'modules'. Stop." Update Makefile to support make target 'modules'. Change-Id: I5b20d71eeedb5dac987d42768d609b32bace1b79 CRs-Fixed: 3401306 --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 37c155124e..f49b8e35c2 100644 --- a/Makefile +++ b/Makefile @@ -26,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 From ef7ea762d7c0700ad97233aec7ce6b3e45f8bef9 Mon Sep 17 00:00:00 2001 From: Chaoli Zhou Date: Wed, 15 Feb 2023 11:42:59 +0800 Subject: [PATCH 18/29] cnss2: Force PM resume for qca6390 For whatever reason, the qca6390 is not in M3 state during PM resume phase, but still functional if do force resume. So calling mhi_pm_resume_force() instead of the mhi_pm_resume() to unblock PM resume for kernel 5.15. Reference link: https://lore.kernel.org/regressions/871r5p0x2u.fsf@codeaurora.org/ As for lower kernel version, like 5.4, it will do local mhi pm state check in the mhi_pm_resume() instead of target pm state check, so has no issue with qca6390. Change-Id: I54c2cef457405720386af6a6f7d4617f3d081875 CRs-Fixed: 3402571 --- cnss2/pci.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cnss2/pci.c b/cnss2/pci.c index 921fbded48..831df48533 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -1816,6 +1816,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) { @@ -1888,7 +1900,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; From 5c7494ce4adb4b30258f491ed6f4e8e2d257e469 Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Tue, 20 Dec 2022 10:52:08 +0530 Subject: [PATCH 19/29] cnss2: Set TME-Lite support flag for Peach Peach supports TME-Lite to validate FW image. TME-Lite supported FW image format is different from currently used FW images and it requires different way of downloading in MHI layer. Set TME-Lite supported flag in MHI controller for Peach to inform MHI about FW image format. Change-Id: I615570914c9cec297aa422ce683b4d1c6c75ea1c CRs-Fixed: 3364546 --- cnss2/pci.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cnss2/pci.c b/cnss2/pci.c index 831df48533..f7ef9ce2ef 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -6119,6 +6119,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; @@ -6197,6 +6212,8 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) plat_priv->device_version.major_version == 1)) 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) { cnss_pr_err("Failed to register to MHI bus, err = %d\n", ret); From b4a9230200b10ac9f29d711a95352d53f5a1a93b Mon Sep 17 00:00:00 2001 From: Chaoli Zhou Date: Wed, 22 Feb 2023 16:12:08 +0800 Subject: [PATCH 20/29] cnss2: Support compile flag CONFIG_CNSS_REQ_FW_DIRECT Support CONFIG_CNSS_REQ_FW_DIRECT compile flag passing from external to use request_firmware_direct instead of firmware_request_nowarn to avoid regdb.bin search failure with qcn7605 that finally cause kernel panic on the LV platform. Change-Id: I2431692465298c841bf63add44187c15e68a9742 CRs-Fixed: 3413712 --- Kbuild | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Kbuild b/Kbuild index e273439896..051f85f3fe 100644 --- a/Kbuild +++ b/Kbuild @@ -36,6 +36,10 @@ 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 From 01cb81be76d76fe466529a82b35b2f2ace196e5a Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Fri, 24 Feb 2023 20:44:32 +0530 Subject: [PATCH 21/29] icnss2: Use event_data before posting event In icnss_pdr_notifier_cb() API, event_data gets free while processing PD_SERVICE_DOWN event. In the same function event_data->crashed is checking after posting PD_SERVICE_DOWN event which may cause using event_data even after free. This commit updates checking for event_data->crashed before posting PD_SERVICE_DOWN event. Change-Id: Ie82a0b7b4ceb40063318ab2d926b9c14412fb2c9 CRs-Fixed: 3415299 --- icnss2/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index 87cb6cc18f..566a132099 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -2468,13 +2468,14 @@ static void icnss_pdr_notifier_cb(int state, char *service_path, void *priv_cb) } } clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state); - icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - ICNSS_EVENT_SYNC, event_data); 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; case SERVREG_SERVICE_STATE_UP: clear_bit(ICNSS_FW_DOWN, &priv->state); From ecd2be21ed2ec2818afe705170d3dbce5ef4b192 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Thu, 23 Feb 2023 12:33:59 +0530 Subject: [PATCH 22/29] icnss2: Add null check for priv variable Add null check for priv variable to avoid null pointer dereference. Change-Id: Ib1746d564f27f8096f66761946d37a2a3faf2e57 CRs-Fixed: 3378034 --- icnss2/main.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index 566a132099..4a037aeee7 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -152,6 +152,9 @@ static ssize_t icnss_sysfs_store(struct kobject *kobj, { struct icnss_priv *priv = icnss_get_plat_priv(); + if (!priv) + return count; + icnss_pr_dbg("Received shutdown indication"); atomic_set(&priv->is_shutdown, true); @@ -466,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 */ @@ -547,11 +550,11 @@ static irqreturn_t fw_crash_indication_handler(int irq, void *ctx) icnss_pr_err("Received early crash indication from FW\n"); - if (priv->wpss_self_recovery_enabled) - mod_timer(&priv->wpss_ssr_timer, - jiffies + msecs_to_jiffies(ICNSS_WPSS_SSR_TIMEOUT)); - 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); @@ -2426,6 +2429,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); From aec346c6bc2cfbe468b37ca5d1eb6616eddb53ca Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 15 Feb 2023 11:34:54 +0530 Subject: [PATCH 23/29] icnss2: Add support for WLAN WCN6450 device Add corresponding driver support for WCN6450 device. This change adds compatible dtsi entry, msi config, v3 shadow register config and vreg regulator details along with enabling bootup sequence for WCN6450 device. Change-Id: I406c92cdc2815ffd1108331d382c4972befad998 CRs-Fixed: 3404702 --- icnss2/debug.c | 2 +- icnss2/main.c | 72 +++++++++++++++++++++++++++++++++++--------------- icnss2/main.h | 2 ++ icnss2/power.c | 16 ++++++++++- icnss2/qmi.c | 22 ++++++++++++--- inc/icnss2.h | 6 +++++ 6 files changed, 93 insertions(+), 27 deletions(-) diff --git a/icnss2/debug.c b/icnss2/debug.c index 44308f1a05..ac1c9d72e6 100644 --- a/icnss2/debug.c +++ b/icnss2/debug.c @@ -475,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); diff --git a/icnss2/main.c b/icnss2/main.c index 4a037aeee7..b254558966 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -896,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) @@ -938,7 +939,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; @@ -978,7 +980,8 @@ 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 == WCN6750_DEVICE_ID || + priv->device_id == WCN6450_DEVICE_ID) { if (!priv->fw_soc_wake_ack_irq) register_soc_wake_notif(&priv->pdev->dev); @@ -1163,7 +1166,8 @@ static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data) 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); @@ -1598,7 +1602,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, @@ -2614,7 +2619,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)); @@ -2970,7 +2976,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[]) { @@ -2979,9 +2985,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; } @@ -3918,7 +3935,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) { @@ -4085,7 +4102,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) { @@ -4122,7 +4140,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) { @@ -4273,7 +4291,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); @@ -4339,16 +4358,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, }, { }, }; @@ -4359,6 +4381,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]}, { }, }; @@ -4532,7 +4557,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) { @@ -4654,7 +4680,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)) @@ -4727,7 +4754,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; @@ -4836,7 +4864,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; } @@ -4870,8 +4898,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; } @@ -4897,8 +4925,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; } diff --git a/icnss2/main.h b/icnss2/main.h index d7b0c6f404..48bebcf7d2 100644 --- a/icnss2/main.h +++ b/icnss2/main.h @@ -22,12 +22,14 @@ #include #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 diff --git a/icnss2/power.c b/icnss2/power.c index adbea8329b..d45fb8adaf 100644 --- a/icnss2/power.c +++ b/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 #include @@ -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; } diff --git a/icnss2/qmi.c b/icnss2/qmi.c index 5ee261f709..63d8526e82 100644 --- a/icnss2/qmi.c +++ b/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); diff --git a/inc/icnss2.h b/inc/icnss2.h index 51e4e39f22..c32bec8f4e 100644 --- a/inc/icnss2.h +++ b/inc/icnss2.h @@ -95,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; @@ -109,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; }; From b9deac9d5c0678b11f0fbee59d7212d10928bef0 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 22 Feb 2023 12:43:28 +0530 Subject: [PATCH 24/29] icnss2: Disable APPS proxy votes before WLAN_EN WCN6450 has strict power on sequence where WLAN_EN needs to be enable first followed by regulators. To achieve this remove Host Proxy votes before sending TARGET_CAP and MODE_ON QMI request in which FW is suppose to do WLAN_EN high. Change-Id: I315269048c6499153d4d2daf6ee6a829f6fd2ccc CRs-Fixed: 3413588 --- icnss2/main.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index b254558966..f1883598df 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -929,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; @@ -3440,7 +3443,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)) { @@ -3467,7 +3470,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); From dcbdb2f5fb4bad1f0a2b175a14c793b81417545f Mon Sep 17 00:00:00 2001 From: Wu Gao Date: Mon, 27 Feb 2023 15:25:35 +0800 Subject: [PATCH 25/29] cnss2: Use mdelay to avoid compilation issue The function - udelay is used only for very small delays (< 2 msec) on some arm architecture. Otherwise there is compilation issue like "__bad_udelay", since input is out of range. This change uses mdelay to replace udelay and avoid compilation issue. Change-Id: I42df519e7004d89ae6bb394f65b0d61ab5073784 CRs-Fixed: 3418093 --- cnss2/power.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cnss2/power.c b/cnss2/power.c index 02cf16d019..e9e575dbf3 100644 --- a/cnss2/power.c +++ b/cnss2/power.c @@ -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); From 3b1df6da052f64013d169082cc5b4550dc471f1f Mon Sep 17 00:00:00 2001 From: Sai Kumar Kunchala Date: Fri, 17 Feb 2023 19:24:51 +0530 Subject: [PATCH 26/29] cnss2: Add NULL check for 'cfg_arr' pointer Add NULL check for 'cfg_arr' pointer before passing to 'of_property_read_string_array' function Change-Id: Ief30b57145af5842cfd8e9533fcc5d15ab5ea0f1 CRs-Fixed: 3409579 --- cnss2/power.c | 67 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/cnss2/power.c b/cnss2/power.c index e9e575dbf3..b32710dfe8 100644 --- a/cnss2/power.c +++ b/cnss2/power.c @@ -1619,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"); } @@ -1637,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"); } @@ -1654,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"); } @@ -1684,13 +1694,16 @@ void cnss_power_misc_params_init(struct cnss_plat_data *plat_priv) if (ret > 0) { cfg_arr_size = ret; cfg_arr = kcalloc(cfg_arr_size, sizeof(*cfg_arr), GFP_KERNEL); - - 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; + 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"); From f6f770735190989320df27745caaa7b0852cf566 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 22 Feb 2023 12:32:22 +0530 Subject: [PATCH 27/29] icnss2: Add change to download qdss file for WCN6450 Add change to download qdss file after bdf download for WCN6450 device. Change-Id: I3b46c3411d8af7835af3907f472e82906f924d08 CRs-Fixed: 3413562 --- icnss2/main.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/icnss2/main.c b/icnss2/main.c index f1883598df..9c07d1e9af 100644 --- a/icnss2/main.c +++ b/icnss2/main.c @@ -983,6 +983,13 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv, goto device_info_failure; } + 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) @@ -1211,8 +1218,12 @@ 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)) { mod_timer(&priv->recovery_timer, From 97d02063a9e4176d8dd6ce9787c40e0e57407d8f Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Wed, 1 Mar 2023 15:28:21 +0530 Subject: [PATCH 28/29] cnss2: Avoid IDLE Shutdown during recovery There can be scenario where CNSS driver receives IDLE shutdown request from WLAN Host driver when SSR/Recovery is in progress. In such case return -EBUSY to Host driver and avoid doing IDLE Shutdown. Change-Id: I68bea49d23e741f405acc1f2c9133cb7452d7952 CRs-Fixed: 3418977 --- cnss2/main.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/cnss2/main.c b/cnss2/main.c index 8fa4b18caf..2ad0904193 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -1371,8 +1371,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"); @@ -1386,21 +1384,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); From 4f03c58310f5e08f5a550ceb911491997576976b Mon Sep 17 00:00:00 2001 From: Gangadhar Kavalastramath Date: Tue, 29 Nov 2022 16:28:17 +0530 Subject: [PATCH 29/29] cnss2: Add support for QCN7605 Add support for QCN7605 in cnss2 driver Change-Id: I529d76f8f6fa6eeea2117630b2a1e9d05ce5f5b4 CRs-Fixed: 3359985 --- cnss2/bus.c | 23 ++++++++ cnss2/bus.h | 6 ++ cnss2/main.c | 17 ++++++ cnss2/pci.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++-- cnss2/pci.h | 5 ++ cnss2/qmi.c | 68 +++++++++++++++++++---- cnss2/reg.h | 2 + inc/cnss2.h | 1 + 8 files changed, 256 insertions(+), 17 deletions(-) diff --git a/cnss2/bus.c b/cnss2/bus.c index a3084b1323..e7558189e8 100644 --- a/cnss2/bus.c +++ b/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: @@ -653,6 +654,28 @@ int cnss_bus_set_therm_cdev_state(struct cnss_plat_data *plat_priv, } } +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) { diff --git a/cnss2/bus.h b/cnss2/bus.h index ae4c6e1ca2..57d1154f82 100644 --- a/cnss2/bus.h +++ b/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 @@ -80,4 +81,9 @@ 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 */ diff --git a/cnss2/main.c b/cnss2/main.c index 2ad0904193..e9c9f8c2f8 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -595,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; @@ -798,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, @@ -805,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; @@ -2335,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); @@ -3373,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: @@ -3395,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: diff --git a/cnss2/pci.c b/cnss2/pci.c index f7ef9ce2ef..eed743ca16 100644 --- a/cnss2/pci.c +++ b/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/" @@ -248,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 @@ -326,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, @@ -833,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", @@ -3136,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: @@ -3166,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: @@ -3196,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: @@ -3226,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: @@ -4836,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; @@ -5074,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; @@ -5705,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); @@ -6198,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; @@ -6208,9 +6342,13 @@ 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); @@ -6635,6 +6773,7 @@ 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: @@ -6708,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: @@ -6738,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 }, diff --git a/cnss2/pci.h b/cnss2/pci.h index 036b380982..73136f29ee 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -304,4 +304,9 @@ int cnss_pci_update_time_sync_period(struct cnss_pci_data *pci_priv, 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 */ diff --git a/cnss2/qmi.c b/cnss2/qmi.c index 3219ace2a0..c889e2f1fe 100644 --- a/cnss2/qmi.c +++ b/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) @@ -1567,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) @@ -1617,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; - memcpy(req->shadow_reg_v2, config->shadow_reg_v2_cfg, - sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) - * req->shadow_reg_v2_len); + if (config->num_shadow_reg_v2_cfg > + QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01) + req->shadow_reg_v2_len = + QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01; + else + req->shadow_reg_v2_len = + config->num_shadow_reg_v2_cfg; + + memcpy(req->shadow_reg_v2, config->shadow_reg_v2_cfg, + sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) * + req->shadow_reg_v2_len); + } } else { req->shadow_reg_v3_valid = 1; if (config->num_shadow_reg_v3_cfg > @@ -1641,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, diff --git a/cnss2/reg.h b/cnss2/reg.h index e670979f81..64cd779cc5 100644 --- a/cnss2/reg.h +++ b/cnss2/reg.h @@ -361,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 diff --git a/inc/cnss2.h b/inc/cnss2.h index ec32ef74b1..53f4d9c0fe 100644 --- a/inc/cnss2.h +++ b/inc/cnss2.h @@ -224,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 {