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