summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHsiu-Chang Chen <hsiuchangchen@google.com>2022-04-11 17:56:32 +0800
committerHsiu-Chang Chen <hsiuchangchen@google.com>2022-04-11 17:56:32 +0800
commita669d625291d0f932429991e9855e2fc1f93f4f0 (patch)
tree9c23e05dd0f9e73ddb3b51cc9808da0444119e19
parent88f2e5ee91325812e4afed47005830105531d47b (diff)
downloadcnss2-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.c15
-rw-r--r--cnss2/bus.h1
-rw-r--r--cnss2/debug.c17
-rw-r--r--cnss2/main.c234
-rw-r--r--cnss2/main.h23
-rw-r--r--cnss2/pci.c110
-rw-r--r--cnss2/pci.h1
-rw-r--r--cnss2/pci_qcom.c2
-rw-r--r--cnss2/power.c31
-rw-r--r--cnss2/qmi.c6
-rw-r--r--cnss2/reg.h1
-rw-r--r--qmi/qmi_interface.c4
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);