diff options
author | Hsiu-Chang Chen <hsiuchangchen@google.com> | 2022-04-11 17:56:32 +0800 |
---|---|---|
committer | Hsiu-Chang Chen <hsiuchangchen@google.com> | 2022-04-11 17:56:32 +0800 |
commit | a669d625291d0f932429991e9855e2fc1f93f4f0 (patch) | |
tree | 9c23e05dd0f9e73ddb3b51cc9808da0444119e19 | |
parent | 88f2e5ee91325812e4afed47005830105531d47b (diff) | |
download | cnss2-a669d625291d0f932429991e9855e2fc1f93f4f0.tar.gz |
wcn6740: Update cnss/mhi/qmi/qrtr drivers
Migrate wlan codes to Rel4(ES1.1)
Bug: 228562103
Test: Basic functions
Change-Id: I48ab3c27af57d199363e47207153ad856f38b926
-rw-r--r-- | cnss2/bus.c | 15 | ||||
-rw-r--r-- | cnss2/bus.h | 1 | ||||
-rw-r--r-- | cnss2/debug.c | 17 | ||||
-rw-r--r-- | cnss2/main.c | 234 | ||||
-rw-r--r-- | cnss2/main.h | 23 | ||||
-rw-r--r-- | cnss2/pci.c | 110 | ||||
-rw-r--r-- | cnss2/pci.h | 1 | ||||
-rw-r--r-- | cnss2/pci_qcom.c | 2 | ||||
-rw-r--r-- | cnss2/power.c | 31 | ||||
-rw-r--r-- | cnss2/qmi.c | 6 | ||||
-rw-r--r-- | cnss2/reg.h | 1 | ||||
-rw-r--r-- | qmi/qmi_interface.c | 4 |
12 files changed, 421 insertions, 24 deletions
diff --git a/cnss2/bus.c b/cnss2/bus.c index ef1f26e..04f050c 100644 --- a/cnss2/bus.c +++ b/cnss2/bus.c @@ -131,6 +131,21 @@ int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) } } +int cnss_bus_handle_dev_sol_irq(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_handle_dev_sol_irq(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv) { if (!plat_priv) diff --git a/cnss2/bus.h b/cnss2/bus.h index f6634b2..581b78d 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -32,6 +32,7 @@ void cnss_bus_deinit(struct cnss_plat_data *plat_priv); void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv, char *prefix_name, char *name); int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); +int cnss_bus_handle_dev_sol_irq(struct cnss_plat_data *plat_priv); int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv); int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv); void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv); diff --git a/cnss2/debug.c b/cnss2/debug.c index 957d501..cb84c4d 100644 --- a/cnss2/debug.c +++ b/cnss2/debug.c @@ -146,11 +146,21 @@ static int cnss_stats_show_state(struct seq_file *s, return 0; } +static int cnss_stats_show_gpio_state(struct seq_file *s, + struct cnss_plat_data *plat_priv) +{ + seq_printf(s, "\nHost SOL: %d", cnss_get_host_sol_value(plat_priv)); + seq_printf(s, "\nDev SOL: %d", cnss_get_dev_sol_value(plat_priv)); + + return 0; +} + static int cnss_stats_show(struct seq_file *s, void *data) { struct cnss_plat_data *plat_priv = s->private; cnss_stats_show_state(s, plat_priv); + cnss_stats_show_gpio_state(s, plat_priv); return 0; } @@ -207,6 +217,10 @@ static ssize_t cnss_dev_boot_debug_write(struct file *fp, CNSS_DRIVER_EVENT_POWER_DOWN, 0, NULL); clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state); + } else if (sysfs_streq(cmd, "assert_host_sol")) { + ret = cnss_set_host_sol_value(plat_priv, 1); + } else if (sysfs_streq(cmd, "deassert_host_sol")) { + ret = cnss_set_host_sol_value(plat_priv, 0); } else { pci_priv = plat_priv->bus_priv; if (!pci_priv) @@ -717,9 +731,6 @@ static int cnss_show_quirks_state(struct seq_file *s, case FBC_BYPASS: seq_puts(s, "FBC_BYPASS"); continue; - case ENABLE_DAEMON_SUPPORT: - seq_puts(s, "DAEMON_SUPPORT"); - continue; case DISABLE_DRV: seq_puts(s, "DISABLE_DRV"); continue; diff --git a/cnss2/main.c b/cnss2/main.c index d6b349e..4fe9d6f 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/pm_wakeup.h> #include <linux/reboot.h> #include <linux/rwsem.h> @@ -23,6 +24,7 @@ #include "bus.h" #include "debug.h" #include "genl.h" +#include "reg.h" #define CNSS_DUMP_FORMAT_VER 0x11 #define CNSS_DUMP_FORMAT_VER_V2 0x22 @@ -64,6 +66,8 @@ enum cnss_cal_db_op { static struct cnss_plat_data *plat_env; +static bool cnss_allow_driver_loading; + static DECLARE_RWSEM(cnss_pm_sem); static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = { @@ -91,6 +95,11 @@ static void cnss_set_plat_priv(struct platform_device *plat_dev, 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; @@ -1209,6 +1218,206 @@ static inline int cnss_register_esoc(struct cnss_plat_data *plat_priv) static inline void cnss_unregister_esoc(struct cnss_plat_data *plat_priv) {} #endif +int cnss_enable_dev_sol_irq(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + int ret = 0; + + if (sol_gpio->dev_sol_gpio < 0 || sol_gpio->dev_sol_irq <= 0) + return 0; + + ret = enable_irq_wake(sol_gpio->dev_sol_irq); + if (ret) + cnss_pr_err("Failed to enable device SOL as wake IRQ, err = %d\n", + ret); + + return ret; +} + +int cnss_disable_dev_sol_irq(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + int ret = 0; + + if (sol_gpio->dev_sol_gpio < 0 || sol_gpio->dev_sol_irq <= 0) + return 0; + + ret = disable_irq_wake(sol_gpio->dev_sol_irq); + if (ret) + cnss_pr_err("Failed to disable device SOL as wake IRQ, err = %d\n", + ret); + + return ret; +} + +int cnss_get_dev_sol_value(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + if (sol_gpio->dev_sol_gpio < 0) + return -EINVAL; + + return gpio_get_value(sol_gpio->dev_sol_gpio); +} + +static irqreturn_t cnss_dev_sol_handler(int irq, void *data) +{ + struct cnss_plat_data *plat_priv = data; + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + sol_gpio->dev_sol_counter++; + cnss_pr_dbg("WLAN device SOL IRQ (%u) is asserted #%u\n", + irq, sol_gpio->dev_sol_counter); + + /* Make sure abort current suspend */ + cnss_pm_stay_awake(plat_priv); + cnss_pm_relax(plat_priv); + pm_system_wakeup(); + + cnss_bus_handle_dev_sol_irq(plat_priv); + + return IRQ_HANDLED; +} + +static int cnss_init_dev_sol_gpio(struct cnss_plat_data *plat_priv) +{ + struct device *dev = &plat_priv->plat_dev->dev; + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + int ret = 0; + + sol_gpio->dev_sol_gpio = of_get_named_gpio(dev->of_node, + "wlan-dev-sol-gpio", 0); + if (sol_gpio->dev_sol_gpio < 0) + goto out; + + cnss_pr_dbg("Get device SOL GPIO (%d) from device node\n", + sol_gpio->dev_sol_gpio); + + ret = gpio_request(sol_gpio->dev_sol_gpio, "wlan_dev_sol_gpio"); + if (ret) { + cnss_pr_err("Failed to request device SOL GPIO, err = %d\n", + ret); + goto out; + } + + gpio_direction_input(sol_gpio->dev_sol_gpio); + sol_gpio->dev_sol_irq = gpio_to_irq(sol_gpio->dev_sol_gpio); + + ret = request_irq(sol_gpio->dev_sol_irq, cnss_dev_sol_handler, + IRQF_TRIGGER_FALLING, "wlan_dev_sol_irq", plat_priv); + if (ret) { + cnss_pr_err("Failed to request device SOL IRQ, err = %d\n", ret); + goto free_gpio; + } + + return 0; + +free_gpio: + gpio_free(sol_gpio->dev_sol_gpio); +out: + return ret; +} + +static void cnss_deinit_dev_sol_gpio(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + if (sol_gpio->dev_sol_gpio < 0) + return; + + free_irq(sol_gpio->dev_sol_irq, plat_priv); + gpio_free(sol_gpio->dev_sol_gpio); +} + +int cnss_set_host_sol_value(struct cnss_plat_data *plat_priv, int value) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + if (sol_gpio->host_sol_gpio < 0) + return -EINVAL; + + if (value) + cnss_pr_dbg("Assert host SOL GPIO\n"); + gpio_set_value(sol_gpio->host_sol_gpio, value); + + return 0; +} + +int cnss_get_host_sol_value(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + if (sol_gpio->host_sol_gpio < 0) + return -EINVAL; + + return gpio_get_value(sol_gpio->host_sol_gpio); +} + +static int cnss_init_host_sol_gpio(struct cnss_plat_data *plat_priv) +{ + struct device *dev = &plat_priv->plat_dev->dev; + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + int ret = 0; + + sol_gpio->host_sol_gpio = of_get_named_gpio(dev->of_node, + "wlan-host-sol-gpio", 0); + if (sol_gpio->host_sol_gpio < 0) + goto out; + + cnss_pr_dbg("Get host SOL GPIO (%d) from device node\n", + sol_gpio->host_sol_gpio); + + ret = gpio_request(sol_gpio->host_sol_gpio, "wlan_host_sol_gpio"); + if (ret) { + cnss_pr_err("Failed to request host SOL GPIO, err = %d\n", + ret); + goto out; + } + + gpio_direction_output(sol_gpio->host_sol_gpio, 0); + + return 0; + +out: + return ret; +} + +static void cnss_deinit_host_sol_gpio(struct cnss_plat_data *plat_priv) +{ + struct cnss_sol_gpio *sol_gpio = &plat_priv->sol_gpio; + + if (sol_gpio->host_sol_gpio < 0) + return; + + gpio_free(sol_gpio->host_sol_gpio); +} + +static int cnss_init_sol_gpio(struct cnss_plat_data *plat_priv) +{ + int ret; + + ret = cnss_init_dev_sol_gpio(plat_priv); + if (ret) + goto out; + + ret = cnss_init_host_sol_gpio(plat_priv); + if (ret) + goto deinit_dev_sol; + + return 0; + +deinit_dev_sol: + cnss_deinit_dev_sol_gpio(plat_priv); +out: + return ret; +} + +static void cnss_deinit_sol_gpio(struct cnss_plat_data *plat_priv) +{ + cnss_deinit_host_sol_gpio(plat_priv); + cnss_deinit_dev_sol_gpio(plat_priv); +} + #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) { @@ -3185,6 +3394,10 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) { int ret; + ret = cnss_init_sol_gpio(plat_priv); + if (ret) + return ret; + timer_setup(&plat_priv->fw_boot_timer, cnss_bus_fw_boot_timeout_hdlr, 0); @@ -3224,6 +3437,8 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) cnss_pr_err("QMI IPC connection call back register failed, err = %d\n", ret); + plat_priv->sram_dump = kcalloc(SRAM_DUMP_SIZE, 1, GFP_KERNEL); + return 0; } @@ -3241,6 +3456,8 @@ static void cnss_misc_deinit(struct cnss_plat_data *plat_priv) unregister_pm_notifier(&cnss_pm_notifier); del_timer(&plat_priv->fw_boot_timer); wakeup_source_unregister(plat_priv->recovery_ws); + cnss_deinit_sol_gpio(plat_priv); + kfree(plat_priv->sram_dump); } static void cnss_init_control_params(struct cnss_plat_data *plat_priv) @@ -3514,6 +3731,20 @@ static struct platform_driver cnss_platform_driver = { }, }; +static bool cnss_check_compatible_node(void) +{ + struct device_node *dn = NULL; + + for_each_matching_node(dn, cnss_of_match_table) { + if (of_device_is_available(dn)) { + cnss_allow_driver_loading = true; + return true; + } + } + + return false; +} + /** * cnss_is_valid_dt_node_found - Check if valid device tree node present * @@ -3544,6 +3775,9 @@ static int __init cnss_initialize(void) if (!cnss_is_valid_dt_node_found()) return -ENODEV; + if (!cnss_check_compatible_node()) + return ret; + cnss_debug_init(); ret = platform_driver_register(&cnss_platform_driver); if (ret) diff --git a/cnss2/main.h b/cnss2/main.h index a44a784..fd9a739 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -25,7 +25,7 @@ #include <linux/time64.h> #ifdef CONFIG_CNSS_OUT_OF_TREE #include "cnss2.h" -#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2) +#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2) || IS_ENABLED(CONFIG_QCOM_MINIDUMP) #include "memory_dump.h" #endif #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) || \ @@ -34,7 +34,7 @@ #endif #else #include <net/cnss2.h> -#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2) +#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2) || IS_ENABLED(CONFIG_QCOM_MINIDUMP) #include <soc/qcom/memory_dump.h> #endif #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) || \ @@ -115,6 +115,7 @@ struct cnss_clk_info { struct cnss_pinctrl_info { struct pinctrl *pinctrl; struct pinctrl_state *bootstrap_active; + struct pinctrl_state *sol_default; struct pinctrl_state *wlan_en_active; struct pinctrl_state *wlan_en_sleep; int bt_en_gpio; @@ -357,7 +358,6 @@ enum cnss_debug_quirks { ENABLE_WALTEST, ENABLE_PCI_LINK_DOWN_PANIC, FBC_BYPASS, - ENABLE_DAEMON_SUPPORT, DISABLE_DRV, DISABLE_IO_COHERENCY, IGNORE_PCI_LINK_FAILURE, @@ -434,6 +434,13 @@ enum cnss_timeout_type { CNSS_TIMEOUT_DAEMON_CONNECTION, }; +struct cnss_sol_gpio { + int dev_sol_gpio; + int dev_sol_irq; + u32 dev_sol_counter; + int host_sol_gpio; +}; + struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; @@ -441,6 +448,7 @@ struct cnss_plat_data { struct list_head vreg_list; struct list_head clk_list; struct cnss_pinctrl_info pinctrl_info; + struct cnss_sol_gpio sol_gpio; #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) struct cnss_subsys_info subsys_info; #endif @@ -507,6 +515,7 @@ struct cnss_plat_data { u8 use_fw_path_with_prefix; char firmware_name[MAX_FIRMWARE_NAME_LEN]; char fw_fallback_name[MAX_FIRMWARE_NAME_LEN]; + u8 *sram_dump; struct completion rddm_complete; struct completion recovery_complete; struct cnss_control_params ctrl_params; @@ -586,6 +595,13 @@ int cnss_get_pinctrl(struct cnss_plat_data *plat_priv); int cnss_power_on_device(struct cnss_plat_data *plat_priv); void cnss_power_off_device(struct cnss_plat_data *plat_priv); bool cnss_is_device_powered_on(struct cnss_plat_data *plat_priv); +int cnss_enable_dev_sol_irq(struct cnss_plat_data *plat_priv); +int cnss_disable_dev_sol_irq(struct cnss_plat_data *plat_priv); +int cnss_get_dev_sol_value(struct cnss_plat_data *plat_priv); +int cnss_init_dev_sol_irq(struct cnss_plat_data *plat_priv); +int cnss_deinit_dev_sol_irq(struct cnss_plat_data *plat_priv); +int cnss_set_host_sol_value(struct cnss_plat_data *plat_priv, int value); +int cnss_get_host_sol_value(struct cnss_plat_data *plat_priv); int cnss_register_subsys(struct cnss_plat_data *plat_priv); void cnss_unregister_subsys(struct cnss_plat_data *plat_priv); int cnss_register_ramdump(struct cnss_plat_data *plat_priv); @@ -616,4 +632,5 @@ int cnss_set_feature_list(struct cnss_plat_data *plat_priv, int cnss_get_feature_list(struct cnss_plat_data *plat_priv, u64 *feature_list); int cnss_get_input_gpio_value(struct cnss_plat_data *plat_priv, int gpio_num); +bool cnss_check_driver_loading_allowed(void); #endif /* _CNSS_MAIN_H */ diff --git a/cnss2/pci.c b/cnss2/pci.c index 48d6d50..b46635e 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -14,6 +14,7 @@ #include <linux/suspend.h> #include <linux/memblock.h> #include <linux/completion.h> +#include <linux/sched.h> #ifdef CONFIG_WCN_GOOGLE #include <linux/dma-direct.h> #endif @@ -64,6 +65,7 @@ #endif #define RAMDUMP_SIZE_DEFAULT 0x420000 +#define CNSS_256KB_SIZE 0x40000 #define DEVICE_RDDM_COOKIE 0xCAFECACE static DEFINE_SPINLOCK(pci_link_down_lock); @@ -1421,28 +1423,65 @@ static void cnss_pci_dump_bl_sram_mem(struct cnss_pci_data *pci_priv) } } +static void cnss_pci_dump_sram(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + u32 i, mem_addr; + u32 *dump_ptr; + + plat_priv = pci_priv->plat_priv; + + if (plat_priv->device_id != QCA6490_DEVICE_ID || + cnss_get_host_build_type() != QMI_HOST_BUILD_TYPE_PRIMARY_V01) + return; + + if (!plat_priv->sram_dump) { + cnss_pr_err("SRAM dump memory is not allocated\n"); + return; + } + + if (cnss_pci_check_link_status(pci_priv)) + return; + + cnss_pr_dbg("Dumping SRAM at 0x%lx\n", plat_priv->sram_dump); + + for (i = 0; i < SRAM_DUMP_SIZE; i += sizeof(u32)) { + mem_addr = SRAM_START + i; + dump_ptr = (u32 *)(plat_priv->sram_dump + i); + if (cnss_pci_reg_read(pci_priv, mem_addr, dump_ptr)) { + cnss_pr_err("SRAM Dump failed at 0x%x\n", mem_addr); + break; + } + /* Relinquish CPU after dumping 256KB chunks*/ + if (!(i % CNSS_256KB_SIZE)) + cond_resched(); + } +} + static int cnss_pci_handle_mhi_poweron_timeout(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; cnss_fatal_err("MHI power up returns timeout\n"); - if (cnss_mhi_scan_rddm_cookie(pci_priv, DEVICE_RDDM_COOKIE)) { - /* Wait for RDDM if RDDM cookie is set. If RDDM times out, - * PBL/SBL error region may have been erased so no need to - * dump them either. - */ + if (cnss_mhi_scan_rddm_cookie(pci_priv, DEVICE_RDDM_COOKIE) || + cnss_get_dev_sol_value(plat_priv) > 0) { + /* Wait for RDDM if RDDM cookie is set or device SOL GPIO is + * high. If RDDM times out, PBL/SBL error region may have been + * erased so no need to dump them either. + */ if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) && !pci_priv->pci_link_down_ind) { mod_timer(&pci_priv->dev_rddm_timer, jiffies + msecs_to_jiffies(DEV_RDDM_TIMEOUT)); } } else { - cnss_pr_dbg("RDDM cookie is not set\n"); + cnss_pr_dbg("RDDM cookie is not set and device SOL is low\n"); cnss_mhi_debug_reg_dump(pci_priv); cnss_pci_soc_scratch_reg_dump(pci_priv); /* Dump PBL/SBL error log if RDDM cookie is not set */ cnss_pci_dump_bl_sram_mem(pci_priv); + cnss_pci_dump_sram(pci_priv); return -ETIMEDOUT; } @@ -2751,8 +2790,13 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) #endif unsigned int timeout; + if (!cnss_check_driver_loading_allowed()) { + cnss_pr_info("No cnss2 dtsi entry present"); + return -ENODEV; + } + if (!plat_priv) { - cnss_pr_info("plat_priv is not ready for register driver\n"); + cnss_pr_buf("plat_priv is not ready for register driver\n"); return -EAGAIN; } @@ -4487,6 +4531,17 @@ static void cnss_pci_dump_debug_reg(struct cnss_pci_data *pci_priv) cnss_pci_dump_ce_reg(pci_priv, CNSS_CE_10); } +static int cnss_pci_assert_host_sol(struct cnss_pci_data *pci_priv) +{ + if (cnss_get_host_sol_value(pci_priv->plat_priv)) + return -EINVAL; + + cnss_pr_dbg("Assert host SOL GPIO to retry RDDM, expecting link down\n"); + cnss_set_host_sol_value(pci_priv->plat_priv, 1); + + return 0; +} + int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) { int ret; @@ -4527,6 +4582,8 @@ int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) return 0; } cnss_fatal_err("Failed to trigger RDDM, err = %d\n", ret); + if (!cnss_pci_assert_host_sol(pci_priv)) + return 0; cnss_pci_dump_debug_reg(pci_priv); cnss_schedule_recovery(&pci_priv->pci_dev->dev, CNSS_REASON_DEFAULT); @@ -4635,6 +4692,10 @@ static void cnss_pci_send_hang_event(struct cnss_pci_data *pci_priv) length = HANG_DATA_LENGTH; } break; + case KIWI_DEVICE_ID: + offset = plat_priv->hang_data_addr_offset; + length = plat_priv->hang_event_data_len; + break; default: cnss_pr_err("Skip Hang Event Data as unsupported Device ID received: %d\n", pci_priv->device_id); @@ -4714,6 +4775,10 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) } else { if (cnss_pci_check_link_status(pci_priv)) return; + /* Inside panic handler, reduce timeout for RDDM to avoid + * unnecessary hypervisor watchdog bite. + */ + pci_priv->mhi_ctrl->timeout_ms /= 2; } cnss_mhi_debug_reg_dump(pci_priv); @@ -4726,6 +4791,8 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) if (ret) { cnss_fatal_err("Failed to download RDDM image, err = %d\n", ret); + if (!cnss_pci_assert_host_sol(pci_priv)) + return; cnss_pci_dump_debug_reg(pci_priv); return; } @@ -4999,6 +5066,9 @@ static void cnss_dev_rddm_timeout_hdlr(struct timer_list *t) cnss_fatal_err("Timeout waiting for RDDM notification\n"); + if (!cnss_pci_assert_host_sol(pci_priv)) + return; + if (mhi_get_exec_env(pci_priv->mhi_ctrl) == MHI_EE_PBL) cnss_pr_err("Unable to collect ramdumps due to abrupt reset\n"); @@ -5038,6 +5108,25 @@ static void cnss_boot_debug_timeout_hdlr(struct timer_list *t) jiffies + msecs_to_jiffies(BOOT_DEBUG_TIMEOUT_MS)); } +static int cnss_pci_handle_mhi_sys_err(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + cnss_ignore_qmi_failure(true); + set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + del_timer(&plat_priv->fw_boot_timer); + mod_timer(&pci_priv->dev_rddm_timer, + jiffies + msecs_to_jiffies(DEV_RDDM_TIMEOUT)); + cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); + + return 0; +} + +int cnss_pci_handle_dev_sol_irq(struct cnss_pci_data *pci_priv) +{ + return cnss_pci_handle_mhi_sys_err(pci_priv); +} + static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, enum mhi_callback reason) { @@ -5068,12 +5157,7 @@ static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, cnss_reason = CNSS_REASON_DEFAULT; break; case MHI_CB_SYS_ERROR: - cnss_ignore_qmi_failure(true); - set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); - del_timer(&plat_priv->fw_boot_timer); - mod_timer(&pci_priv->dev_rddm_timer, - jiffies + msecs_to_jiffies(DEV_RDDM_TIMEOUT)); - cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); + cnss_pci_handle_mhi_sys_err(pci_priv); return; case MHI_CB_EE_RDDM: cnss_ignore_qmi_failure(true); diff --git a/cnss2/pci.h b/cnss2/pci.h index 16d4b65..97ded45 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -236,6 +236,7 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv); int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv); void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); +int cnss_pci_handle_dev_sol_irq(struct cnss_pci_data *pci_priv); int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic); void cnss_pci_device_crashed(struct cnss_pci_data *pci_priv); diff --git a/cnss2/pci_qcom.c b/cnss2/pci_qcom.c index f9242d5..04390d1 100644 --- a/cnss2/pci_qcom.c +++ b/cnss2/pci_qcom.c @@ -514,7 +514,7 @@ int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, u8 *buf, u32 len) { - return 0; + return msm_pcie_reg_dump(pci_priv->pci_dev, buf, len); } #if IS_ENABLED(CONFIG_ARCH_QCOM) diff --git a/cnss2/power.c b/cnss2/power.c index a057e18..6059abc 100644 --- a/cnss2/power.c +++ b/cnss2/power.c @@ -57,6 +57,9 @@ static struct cnss_clk_cfg cnss_clk_list[] = { #define BOOTSTRAP_GPIO "qcom,enable-bootstrap-gpio" #define BOOTSTRAP_ACTIVE "bootstrap_active" +#define HOST_SOL_GPIO "wlan-host-sol-gpio" +#define DEV_SOL_GPIO "wlan-dev-sol-gpio" +#define SOL_DEFAULT "sol_default" #define WLAN_EN_GPIO "wlan-en-gpio" #define BT_EN_GPIO "qcom,bt-en-gpio" #define XO_CLK_GPIO "qcom,xo-clk-gpio" @@ -728,6 +731,20 @@ int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) } } + if (of_find_property(dev->of_node, HOST_SOL_GPIO, NULL) && + of_find_property(dev->of_node, DEV_SOL_GPIO, NULL)) { + pinctrl_info->sol_default = + pinctrl_lookup_state(pinctrl_info->pinctrl, + SOL_DEFAULT); + if (IS_ERR_OR_NULL(pinctrl_info->sol_default)) { + ret = PTR_ERR(pinctrl_info->sol_default); + cnss_pr_err("Failed to get sol default state, err = %d\n", + ret); + goto out; + } + cnss_pr_dbg("Got sol default state\n"); + } + if (of_find_property(dev->of_node, WLAN_EN_GPIO, NULL)) { pinctrl_info->wlan_en_active = pinctrl_lookup_state(pinctrl_info->pinctrl, @@ -849,6 +866,17 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, } udelay(BOOTSTRAP_DELAY); } + if (!IS_ERR_OR_NULL(pinctrl_info->sol_default)) { + ret = pinctrl_select_state + (pinctrl_info->pinctrl, + pinctrl_info->sol_default); + if (ret) { + cnss_pr_err("Failed to select sol default state, err = %d\n", + ret); + goto out; + } + cnss_pr_dbg("Selected sol default state\n"); + } cnss_set_xo_clk_gpio_state(plat_priv, true); if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { ret = pinctrl_select_state @@ -1040,6 +1068,8 @@ int cnss_power_on_device(struct cnss_plat_data *plat_priv) } plat_priv->powered_on = true; + cnss_enable_dev_sol_irq(plat_priv); + cnss_set_host_sol_value(plat_priv, 0); return 0; @@ -1058,6 +1088,7 @@ void cnss_power_off_device(struct cnss_plat_data *plat_priv) return; } + cnss_disable_dev_sol_irq(plat_priv); cnss_select_pinctrl_state(plat_priv, false); cnss_clk_off(plat_priv, &plat_priv->clk_list); cnss_vreg_off_type(plat_priv, CNSS_VREG_PRIM); diff --git a/cnss2/qmi.c b/cnss2/qmi.c index e8b6797..d1fa4b2 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -955,7 +955,7 @@ int cnss_wlfw_qdss_data_send_sync(struct cnss_plat_data *plat_priv, char *file_n goto fail; } - ret = qmi_txn_wait(&txn, plat_priv->ctrl_params.qmi_timeout); + ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF); if (ret < 0) { cnss_pr_err("QDSS trace resp wait failed with rc %d\n", @@ -1122,7 +1122,7 @@ int cnss_wlfw_qdss_dnld_send_sync(struct cnss_plat_data *plat_priv) goto err_send; } - ret = qmi_txn_wait(&txn, plat_priv->ctrl_params.qmi_timeout); + ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF); if (ret < 0) { cnss_pr_err("Failed to wait for response of QDSS download request, err: %d\n", ret); @@ -1212,7 +1212,7 @@ static int wlfw_send_qdss_trace_mode_req goto out; } - rc = qmi_txn_wait(&txn, plat_priv->ctrl_params.qmi_timeout); + rc = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF); if (rc < 0) { cnss_pr_err("QDSS Mode resp wait failed with rc %d\n", rc); diff --git a/cnss2/reg.h b/cnss2/reg.h index 0ac689d..c774d18 100644 --- a/cnss2/reg.h +++ b/cnss2/reg.h @@ -96,6 +96,7 @@ #define PBL_BOOTSTRAP_STATUS 0x01910008 #define SRAM_START 0x01400000 #define SRAM_END 0x01800000 +#define SRAM_DUMP_SIZE 0x400000 #define QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG 0x01E04234 #define QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG_VAL 0xDEAD1234 diff --git a/qmi/qmi_interface.c b/qmi/qmi_interface.c index 2bebc90..4ceb572 100644 --- a/qmi/qmi_interface.c +++ b/qmi/qmi_interface.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 Linaro Ltd. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ + #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> @@ -411,7 +413,7 @@ static void qmi_invoke_handler(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, break; } - if (!handler->fn) + if (!handler->fn || !handler->decoded_size) return; dest = kzalloc(handler->decoded_size, GFP_KERNEL); |