From 9e70a33492f9438c5e4c9901cfdbce28cfbad45c Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Tue, 30 Jul 2019 15:27:56 -0700 Subject: [PATCH] net: cnss2: Update CNSS2 driver This is a update of the CNSS2 driver and associated files as of msm-4.19 commit c3714c8e90ab ("cnss2: fix memory leaks"). Change-Id: I09119dac5294a6cfd12b0a519d173f46ba6beffe Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/bus.c | 79 + drivers/net/wireless/cnss2/bus.h | 7 + drivers/net/wireless/cnss2/debug.c | 72 +- drivers/net/wireless/cnss2/genl.c | 14 +- drivers/net/wireless/cnss2/main.c | 199 ++- drivers/net/wireless/cnss2/main.h | 18 + drivers/net/wireless/cnss2/pci.c | 1471 +++++++++++++---- drivers/net/wireless/cnss2/pci.h | 25 + drivers/net/wireless/cnss2/qmi.c | 221 ++- drivers/net/wireless/cnss2/qmi.h | 2 + drivers/net/wireless/cnss2/reg.h | 267 +++ .../cnss2/wlan_firmware_service_v01.c | 158 ++ .../cnss2/wlan_firmware_service_v01.h | 37 +- include/net/cnss2.h | 8 + 14 files changed, 2157 insertions(+), 421 deletions(-) create mode 100644 drivers/net/wireless/cnss2/reg.h diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index 4c56ec5f0480..f97c3f0d3d1d 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -186,6 +186,36 @@ int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) } } +int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_qmi_send_get(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_qmi_send_put(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t) { struct cnss_plat_data *plat_priv = @@ -372,3 +402,52 @@ int cnss_bus_update_status(struct cnss_plat_data *plat_priv, return -EINVAL; } } + +int cnss_bus_is_device_down(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pcie_is_device_down(plat_priv->bus_priv); + default: + cnss_pr_dbg("Unsupported bus type: %d\n", + plat_priv->bus_type); + return 0; + } +} + +int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset, + u32 *val) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_debug_reg_read(plat_priv->bus_priv, offset, + val); + default: + cnss_pr_dbg("Unsupported bus type: %d\n", + plat_priv->bus_type); + return 0; + } +} + +int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset, + u32 val) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_debug_reg_write(plat_priv->bus_priv, offset, + val); + default: + cnss_pr_dbg("Unsupported bus type: %d\n", + plat_priv->bus_type); + return 0; + } +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index 8190e8b52e34..7c719f5f263e 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -30,6 +30,8 @@ int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv); void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv); u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv); int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv); +int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv); +int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv); void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t); void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv, bool in_panic); @@ -45,5 +47,10 @@ int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv, int modem_current_status); int cnss_bus_update_status(struct cnss_plat_data *plat_priv, enum cnss_driver_status status); +int cnss_bus_is_device_down(struct cnss_plat_data *plat_priv); +int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset, + u32 *val); +int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset, + u32 val); #endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index cc1e1f6efb2c..4d577aa1d285 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -5,9 +5,12 @@ #include #include #include "main.h" +#include "bus.h" #include "debug.h" #include "pci.h" +#define MMIO_REG_ACCESS_MEM_TYPE 0xFF + void *cnss_ipc_log_context; void *cnss_ipc_log_long_context; @@ -107,6 +110,9 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_IN_SUSPEND_RESUME: seq_puts(s, "IN_SUSPEND_RESUME"); continue; + case CNSS_IN_REBOOT: + seq_puts(s, "IN_REBOOT"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -194,7 +200,7 @@ static ssize_t cnss_dev_boot_debug_write(struct file *fp, ret = -EINVAL; } - if (ret) + if (ret < 0) return ret; return count; @@ -238,6 +244,8 @@ static int cnss_reg_read_debug_show(struct seq_file *s, void *data) mutex_lock(&plat_priv->dev_lock); if (!plat_priv->diag_reg_read_buf) { seq_puts(s, "\nUsage: echo > /cnss/reg_read\n"); + seq_puts(s, "Use mem_type = 0xff for register read by IO access, data_len will be ignored\n"); + seq_puts(s, "Use other mem_type for register read by QMI\n"); mutex_unlock(&plat_priv->dev_lock); return 0; } @@ -269,16 +277,11 @@ static ssize_t cnss_reg_read_debug_write(struct file *fp, char *sptr, *token; unsigned int len = 0; u32 reg_offset, mem_type; - u32 data_len = 0; + u32 data_len = 0, reg_val = 0; u8 *reg_buf = NULL; const char *delim = " "; int ret = 0; - if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { - cnss_pr_err("Firmware is not ready yet\n"); - return -EINVAL; - } - len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -313,6 +316,20 @@ static ssize_t cnss_reg_read_debug_write(struct file *fp, if (kstrtou32(token, 0, &data_len)) return -EINVAL; + if (mem_type == MMIO_REG_ACCESS_MEM_TYPE) { + ret = cnss_bus_debug_reg_read(plat_priv, reg_offset, ®_val); + if (ret) + return ret; + cnss_pr_dbg("Read 0x%x from register offset 0x%x\n", reg_val, + reg_offset); + return count; + } + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Firmware is not ready yet\n"); + return -EINVAL; + } + mutex_lock(&plat_priv->dev_lock); kfree(plat_priv->diag_reg_read_buf); plat_priv->diag_reg_read_buf = NULL; @@ -357,6 +374,8 @@ static const struct file_operations cnss_reg_read_debug_fops = { static int cnss_reg_write_debug_show(struct seq_file *s, void *data) { seq_puts(s, "\nUsage: echo > /cnss/reg_write\n"); + seq_puts(s, "Use mem_type = 0xff for register write by IO access\n"); + seq_puts(s, "Use other mem_type for register write by QMI\n"); return 0; } @@ -374,11 +393,6 @@ static ssize_t cnss_reg_write_debug_write(struct file *fp, const char *delim = " "; int ret = 0; - if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { - cnss_pr_err("Firmware is not ready yet\n"); - return -EINVAL; - } - len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -413,6 +427,20 @@ static ssize_t cnss_reg_write_debug_write(struct file *fp, if (kstrtou32(token, 0, ®_val)) return -EINVAL; + if (mem_type == MMIO_REG_ACCESS_MEM_TYPE) { + ret = cnss_bus_debug_reg_write(plat_priv, reg_offset, reg_val); + if (ret) + return ret; + cnss_pr_dbg("Wrote 0x%x to register offset 0x%x\n", reg_val, + reg_offset); + return count; + } + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Firmware is not ready yet\n"); + return -EINVAL; + } + ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, reg_offset, mem_type, sizeof(u32), (u8 *)®_val); @@ -477,12 +505,16 @@ static ssize_t cnss_runtime_pm_debug_write(struct file *fp, cnss_pci_pm_runtime_put_noidle(pci_priv); } else if (sysfs_streq(cmd, "mark_last_busy")) { cnss_pci_pm_runtime_mark_last_busy(pci_priv); + } else if (sysfs_streq(cmd, "resume_bus")) { + cnss_pci_resume_bus(pci_priv); + } else if (sysfs_streq(cmd, "suspend_bus")) { + cnss_pci_suspend_bus(pci_priv); } else { cnss_pr_err("Runtime PM debugfs command is invalid\n"); ret = -EINVAL; } - if (ret) + if (ret < 0) return ret; return count; @@ -493,11 +525,15 @@ static int cnss_runtime_pm_debug_show(struct seq_file *s, void *data) seq_puts(s, "\nUsage: echo > /cnss/runtime_pm\n"); seq_puts(s, " can be one of below:\n"); seq_puts(s, "usage_count: get runtime PM usage count\n"); + seq_puts(s, "reques_resume: do async runtime PM resume\n"); + seq_puts(s, "resume: do sync runtime PM resume\n"); seq_puts(s, "get: do runtime PM get\n"); seq_puts(s, "get_noresume: do runtime PM get noresume\n"); seq_puts(s, "put_noidle: do runtime PM put noidle\n"); seq_puts(s, "put_autosuspend: do runtime PM put autosuspend\n"); seq_puts(s, "mark_last_busy: do runtime PM mark last busy\n"); + seq_puts(s, "resume_bus: do bus resume only\n"); + seq_puts(s, "suspend_bus: do bus suspend only\n"); return 0; } @@ -555,6 +591,8 @@ static ssize_t cnss_control_params_debug_write(struct file *fp, plat_priv->ctrl_params.quirks = val; else if (strcmp(cmd, "mhi_timeout") == 0) plat_priv->ctrl_params.mhi_timeout = val; + else if (strcmp(cmd, "mhi_m2_timeout") == 0) + plat_priv->ctrl_params.mhi_m2_timeout = val; else if (strcmp(cmd, "qmi_timeout") == 0) plat_priv->ctrl_params.qmi_timeout = val; else if (strcmp(cmd, "bdf_type") == 0) @@ -613,6 +651,12 @@ static int cnss_show_quirks_state(struct seq_file *s, case DISABLE_DRV: seq_puts(s, "DISABLE_DRV"); continue; + case DISABLE_IO_COHERENCY: + seq_puts(s, "DISABLE_IO_COHERENCY"); + continue; + case IGNORE_PCI_LINK_FAILURE: + seq_puts(s, "IGNORE_PCI_LINK_FAILURE"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -636,6 +680,8 @@ static int cnss_control_params_debug_show(struct seq_file *s, void *data) seq_puts(s, "\nCurrent value:\n"); cnss_show_quirks_state(s, cnss_priv); seq_printf(s, "mhi_timeout: %u\n", cnss_priv->ctrl_params.mhi_timeout); + seq_printf(s, "mhi_m2_timeout: %u\n", + cnss_priv->ctrl_params.mhi_m2_timeout); seq_printf(s, "qmi_timeout: %u\n", cnss_priv->ctrl_params.qmi_timeout); seq_printf(s, "bdf_type: %u\n", cnss_priv->ctrl_params.bdf_type); seq_printf(s, "time_sync_period: %u\n", diff --git a/drivers/net/wireless/cnss2/genl.c b/drivers/net/wireless/cnss2/genl.c index 9ff1a933fee4..2819a1bdae29 100644 --- a/drivers/net/wireless/cnss2/genl.c +++ b/drivers/net/wireless/cnss2/genl.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -161,6 +162,7 @@ int cnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size) u32 seg_id = 0; u32 data_len = 0; u8 end = 0; + u8 retry; cnss_pr_dbg("type: %u, total_size: %x\n", type, total_size); @@ -171,8 +173,16 @@ int cnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size) data_len = remaining; end = 1; } - ret = cnss_genl_send_data(type, file_name, total_size, - seg_id, end, data_len, msg_buff); + + for (retry = 0; retry < 2; retry++) { + ret = cnss_genl_send_data(type, file_name, total_size, + seg_id, end, data_len, + msg_buff); + if (ret >= 0) + break; + msleep(100); + } + if (ret < 0) { cnss_pr_err("fail to send genl data, ret %d\n", ret); return ret; diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 2c8e6f6d760a..fef355672902 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -7,9 +7,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -29,13 +31,15 @@ #define FW_READY_TIMEOUT 20000 #define FW_ASSERT_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 +#define COLD_BOOT_CAL_SHUTDOWN_DELAY_MS 50 -#define CNSS_QUIRKS_DEFAULT 0 +#define CNSS_QUIRKS_DEFAULT BIT(DISABLE_IO_COHERENCY) #ifdef CONFIG_CNSS_EMULATION #define CNSS_MHI_TIMEOUT_DEFAULT 90000 #else #define CNSS_MHI_TIMEOUT_DEFAULT 0 #endif +#define CNSS_MHI_M2_TIMEOUT_DEFAULT 25 #define CNSS_QMI_TIMEOUT_DEFAULT 10000 #define CNSS_BDF_TYPE_DEFAULT CNSS_BDF_ELF #define CNSS_TIME_SYNC_PERIOD_DEFAULT 900000 @@ -283,6 +287,7 @@ EXPORT_SYMBOL(cnss_wlan_enable); int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + int ret = 0; if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; @@ -290,7 +295,10 @@ int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) if (test_bit(QMI_BYPASS, &plat_priv->ctrl_params.quirks)) return 0; - return cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_OFF); + ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_OFF); + cnss_bus_free_qdss_mem(plat_priv); + + return ret; } EXPORT_SYMBOL(cnss_wlan_disable); @@ -1063,6 +1071,12 @@ static int cnss_driver_recovery_hdlr(struct cnss_plat_data *plat_priv, goto out; } + if (test_bit(CNSS_IN_REBOOT, &plat_priv->driver_state)) { + cnss_pr_err("Reboot is in progress, ignore recovery\n"); + ret = -EINVAL; + goto out; + } + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { cnss_pr_err("Recovery is already in progress\n"); ret = -EINVAL; @@ -1153,7 +1167,7 @@ int cnss_force_fw_assert(struct device *dev) return -EOPNOTSUPP; } - if (cnss_pci_is_device_down(dev)) { + if (cnss_bus_is_device_down(plat_priv)) { cnss_pr_info("Device is already in bad state, ignore force assert\n"); return 0; } @@ -1189,7 +1203,7 @@ int cnss_force_collect_rddm(struct device *dev) return -EOPNOTSUPP; } - if (cnss_pci_is_device_down(dev)) { + if (cnss_bus_is_device_down(plat_priv)) { cnss_pr_info("Device is already in bad state, ignore force collect rddm\n"); return 0; } @@ -1222,6 +1236,54 @@ int cnss_force_collect_rddm(struct device *dev) } EXPORT_SYMBOL(cnss_force_collect_rddm); +int cnss_qmi_send_get(struct device *dev) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state)) + return 0; + + return cnss_bus_qmi_send_get(plat_priv); +} +EXPORT_SYMBOL(cnss_qmi_send_get); + +int cnss_qmi_send_put(struct device *dev) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state)) + return 0; + + return cnss_bus_qmi_send_put(plat_priv); +} +EXPORT_SYMBOL(cnss_qmi_send_put); + +int cnss_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + int ret; + + if (!plat_priv) + return -ENODEV; + + if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state)) + return -EINVAL; + + plat_priv->get_info_cb = cb; + plat_priv->get_info_cb_ctx = cb_ctx; + + ret = cnss_wlfw_get_info_send_sync(plat_priv, type, cmd, cmd_len); + if (ret) { + plat_priv->get_info_cb = NULL; + plat_priv->get_info_cb_ctx = NULL; + } + + return ret; +} +EXPORT_SYMBOL(cnss_qmi_send); + static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1268,8 +1330,10 @@ 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); cnss_bus_dev_shutdown(plat_priv); + msleep(COLD_BOOT_CAL_SHUTDOWN_DELAY_MS); complete(&plat_priv->cal_complete); clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); @@ -1516,6 +1580,25 @@ static void cnss_driver_event_work(struct work_struct *work) cnss_pm_relax(plat_priv); } +int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma, + phys_addr_t *pa, unsigned long attrs) +{ + struct sg_table sgt; + int ret; + + ret = dma_get_sgtable_attrs(dev, &sgt, va, dma, size, attrs); + if (ret) { + cnss_pr_err("Failed to get sgtable for va: 0x%pK, dma: %pa, size: 0x%zx, attrs: 0x%x\n", + va, &dma, size, attrs); + return -EINVAL; + } + + *pa = page_to_phys(sg_page(sgt.sgl)); + sg_free_table(&sgt); + + return 0; +} + int cnss_register_subsys(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1766,6 +1849,87 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv) } } +int cnss_minidump_add_region(struct cnss_plat_data *plat_priv, + enum cnss_fw_dump_type type, int seg_no, + void *va, phys_addr_t pa, size_t size) +{ + struct md_region md_entry; + int ret; + + switch (type) { + case CNSS_FW_IMAGE: + snprintf(md_entry.name, sizeof(md_entry.name), "FBC_%X", + seg_no); + break; + case CNSS_FW_RDDM: + snprintf(md_entry.name, sizeof(md_entry.name), "RDDM_%X", + seg_no); + break; + case CNSS_FW_REMOTE_HEAP: + snprintf(md_entry.name, sizeof(md_entry.name), "RHEAP_%X", + seg_no); + break; + default: + cnss_pr_err("Unknown dump type ID: %d\n", type); + return -EINVAL; + } + + md_entry.phys_addr = pa; + md_entry.virt_addr = (uintptr_t)va; + md_entry.size = size; + md_entry.id = MSM_DUMP_DATA_CNSS_WLAN; + + cnss_pr_dbg("Mini dump region: %s, va: %pK, pa: %pa, size: 0x%zx\n", + md_entry.name, va, &pa, size); + + ret = msm_minidump_add_region(&md_entry); + if (ret) + cnss_pr_err("Failed to add mini dump region, err = %d\n", ret); + + return ret; +} + +int cnss_minidump_remove_region(struct cnss_plat_data *plat_priv, + enum cnss_fw_dump_type type, int seg_no, + void *va, phys_addr_t pa, size_t size) +{ + struct md_region md_entry; + int ret; + + switch (type) { + case CNSS_FW_IMAGE: + snprintf(md_entry.name, sizeof(md_entry.name), "FBC_%X", + seg_no); + break; + case CNSS_FW_RDDM: + snprintf(md_entry.name, sizeof(md_entry.name), "RDDM_%X", + seg_no); + break; + case CNSS_FW_REMOTE_HEAP: + snprintf(md_entry.name, sizeof(md_entry.name), "RHEAP_%X", + seg_no); + break; + default: + cnss_pr_err("Unknown dump type ID: %d\n", type); + return -EINVAL; + } + + md_entry.phys_addr = pa; + md_entry.virt_addr = (uintptr_t)va; + md_entry.size = size; + md_entry.id = MSM_DUMP_DATA_CNSS_WLAN; + + cnss_pr_dbg("Remove mini dump region: %s, va: %pK, pa: %pa, size: 0x%zx\n", + md_entry.name, va, &pa, size); + + ret = msm_minidump_remove_region(&md_entry); + if (ret) + cnss_pr_err("Failed to remove mini dump region, err = %d\n", + ret); + + return ret; +} + static int cnss_register_bus_scale(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1835,7 +1999,7 @@ static ssize_t fs_ready_store(struct device *dev, if (fs_ready == FILE_SYSTEM_READY) { cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START, - CNSS_EVENT_SYNC, NULL); + 0, NULL); } return count; @@ -1884,6 +2048,19 @@ static void cnss_event_work_deinit(struct cnss_plat_data *plat_priv) destroy_workqueue(plat_priv->event_wq); } +static int cnss_reboot_notifier(struct notifier_block *nb, + unsigned long action, + void *data) +{ + struct cnss_plat_data *plat_priv = + container_of(nb, struct cnss_plat_data, reboot_nb); + + set_bit(CNSS_IN_REBOOT, &plat_priv->driver_state); + cnss_pr_dbg("Reboot is in progress with action %d\n", action); + + return NOTIFY_DONE; +} + static int cnss_misc_init(struct cnss_plat_data *plat_priv) { int ret; @@ -1891,7 +2068,15 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) timer_setup(&plat_priv->fw_boot_timer, cnss_bus_fw_boot_timeout_hdlr, 0); - register_pm_notifier(&cnss_pm_notifier); + ret = register_pm_notifier(&cnss_pm_notifier); + if (ret) + cnss_pr_err("Failed to register PM notifier, err = %d\n", ret); + + plat_priv->reboot_nb.notifier_call = cnss_reboot_notifier; + ret = register_reboot_notifier(&plat_priv->reboot_nb); + if (ret) + cnss_pr_err("Failed to register reboot notifier, err = %d\n", + ret); ret = device_init_wakeup(&plat_priv->plat_dev->dev, true); if (ret) @@ -1914,6 +2099,7 @@ static void cnss_misc_deinit(struct cnss_plat_data *plat_priv) complete_all(&plat_priv->cal_complete); complete_all(&plat_priv->power_up_complete); device_init_wakeup(&plat_priv->plat_dev->dev, false); + unregister_reboot_notifier(&plat_priv->reboot_nb); unregister_pm_notifier(&cnss_pm_notifier); del_timer(&plat_priv->fw_boot_timer); } @@ -1922,6 +2108,7 @@ static void cnss_init_control_params(struct cnss_plat_data *plat_priv) { plat_priv->ctrl_params.quirks = CNSS_QUIRKS_DEFAULT; plat_priv->ctrl_params.mhi_timeout = CNSS_MHI_TIMEOUT_DEFAULT; + plat_priv->ctrl_params.mhi_m2_timeout = CNSS_MHI_M2_TIMEOUT_DEFAULT; plat_priv->ctrl_params.qmi_timeout = CNSS_QMI_TIMEOUT_DEFAULT; plat_priv->ctrl_params.bdf_type = CNSS_BDF_TYPE_DEFAULT; plat_priv->ctrl_params.time_sync_period = CNSS_TIME_SYNC_PERIOD_DEFAULT; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 9bd16857140f..f64d04bcdaa0 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -18,6 +18,7 @@ #define MAX_NO_OF_MAC_ADDR 4 #define QMI_WLFW_MAX_TIMESTAMP_LEN 32 #define QMI_WLFW_MAX_NUM_MEM_SEG 32 +#define QMI_WLFW_MAX_BUILD_ID_LEN 128 #define CNSS_RDDM_TIMEOUT_MS 20000 #define RECOVERY_TIMEOUT 60000 #define TIME_CLOCK_FREQ_HZ 19200000 @@ -133,6 +134,7 @@ struct cnss_fw_mem { phys_addr_t pa; u8 valid; u32 type; + unsigned long attrs; }; struct wlfw_rf_chip_info { @@ -207,6 +209,7 @@ enum cnss_driver_state { CNSS_COEX_CONNECTED, CNSS_IMS_CONNECTED, CNSS_IN_SUSPEND_RESUME, + CNSS_IN_REBOOT, }; struct cnss_recovery_data { @@ -243,6 +246,8 @@ enum cnss_debug_quirks { FBC_BYPASS, ENABLE_DAEMON_SUPPORT, DISABLE_DRV, + DISABLE_IO_COHERENCY, + IGNORE_PCI_LINK_FAILURE, }; enum cnss_bdf_type { @@ -264,6 +269,7 @@ struct cnss_cal_info { struct cnss_control_params { unsigned long quirks; unsigned int mhi_timeout; + unsigned int mhi_m2_timeout; unsigned int qmi_timeout; unsigned int bdf_type; unsigned int time_sync_period; @@ -307,6 +313,7 @@ struct cnss_plat_data { struct cnss_esoc_info esoc_info; struct cnss_bus_bw_info bus_bw_info; struct notifier_block modem_nb; + struct notifier_block reboot_nb; struct cnss_platform_cap cap; struct pm_qos_request qos_request; struct cnss_device_version device_version; @@ -323,6 +330,7 @@ struct cnss_plat_data { struct wlfw_rf_board_info board_info; struct wlfw_soc_info soc_info; struct wlfw_fw_version_info fw_version_info; + char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN + 1]; u32 otp_version; u32 fw_mem_seg_len; struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; @@ -355,6 +363,8 @@ struct cnss_plat_data { struct qmi_handle ims_qmi; struct qmi_txn txn; u64 dynamic_feature; + void *get_info_cb_ctx; + int (*get_info_cb)(void *ctx, void *event, int event_len); }; #ifdef CONFIG_ARCH_QCOM @@ -404,5 +414,13 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); int cnss_get_cpr_info(struct cnss_plat_data *plat_priv); int cnss_update_cpr_info(struct cnss_plat_data *plat_priv); +int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma, + phys_addr_t *pa, unsigned long attrs); +int cnss_minidump_add_region(struct cnss_plat_data *plat_priv, + enum cnss_fw_dump_type type, int seg_no, + void *va, phys_addr_t pa, size_t size); +int cnss_minidump_remove_region(struct cnss_plat_data *plat_priv, + enum cnss_fw_dump_type type, int seg_no, + void *va, phys_addr_t pa, size_t size); #endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 0c2a18365631..82dd066819cd 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -16,6 +18,7 @@ #include "bus.h" #include "debug.h" #include "pci.h" +#include "reg.h" #define PCI_LINK_UP 1 #define PCI_LINK_DOWN 0 @@ -54,81 +57,18 @@ static DEFINE_SPINLOCK(pci_link_down_lock); static DEFINE_SPINLOCK(pci_reg_window_lock); +static DEFINE_SPINLOCK(time_sync_lock); +static DEFINE_SPINLOCK(pm_qos_lock); #define MHI_TIMEOUT_OVERWRITE_MS (plat_priv->ctrl_params.mhi_timeout) - -#define QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET 0x310C - -#define QCA6390_CE_SRC_RING_REG_BASE 0xA00000 -#define QCA6390_CE_DST_RING_REG_BASE 0xA01000 -#define QCA6390_CE_COMMON_REG_BASE 0xA18000 - -#define QCA6390_CE_SRC_RING_BASE_LSB_OFFSET 0x0 -#define QCA6390_CE_SRC_RING_BASE_MSB_OFFSET 0x4 -#define QCA6390_CE_SRC_RING_ID_OFFSET 0x8 -#define QCA6390_CE_SRC_RING_MISC_OFFSET 0x10 -#define QCA6390_CE_SRC_CTRL_OFFSET 0x58 -#define QCA6390_CE_SRC_R0_CE_CH_SRC_IS_OFFSET 0x5C -#define QCA6390_CE_SRC_RING_HP_OFFSET 0x400 -#define QCA6390_CE_SRC_RING_TP_OFFSET 0x404 - -#define QCA6390_CE_DEST_RING_BASE_LSB_OFFSET 0x0 -#define QCA6390_CE_DEST_RING_BASE_MSB_OFFSET 0x4 -#define QCA6390_CE_DEST_RING_ID_OFFSET 0x8 -#define QCA6390_CE_DEST_RING_MISC_OFFSET 0x10 -#define QCA6390_CE_DEST_CTRL_OFFSET 0xB0 -#define QCA6390_CE_CH_DST_IS_OFFSET 0xB4 -#define QCA6390_CE_CH_DEST_CTRL2_OFFSET 0xB8 -#define QCA6390_CE_DEST_RING_HP_OFFSET 0x400 -#define QCA6390_CE_DEST_RING_TP_OFFSET 0x404 - -#define QCA6390_CE_STATUS_RING_BASE_LSB_OFFSET 0x58 -#define QCA6390_CE_STATUS_RING_BASE_MSB_OFFSET 0x5C -#define QCA6390_CE_STATUS_RING_ID_OFFSET 0x60 -#define QCA6390_CE_STATUS_RING_MISC_OFFSET 0x68 -#define QCA6390_CE_STATUS_RING_HP_OFFSET 0x408 -#define QCA6390_CE_STATUS_RING_TP_OFFSET 0x40C - -#define QCA6390_CE_COMMON_GXI_ERR_INTS 0x14 -#define QCA6390_CE_COMMON_GXI_ERR_STATS 0x18 -#define QCA6390_CE_COMMON_GXI_WDOG_STATUS 0x2C -#define QCA6390_CE_COMMON_TARGET_IE_0 0x48 -#define QCA6390_CE_COMMON_TARGET_IE_1 0x4C - -#define QCA6390_CE_REG_INTERVAL 0x2000 - -#define SHADOW_REG_COUNT 36 -#define QCA6390_PCIE_SHADOW_REG_VALUE_0 0x8FC -#define QCA6390_PCIE_SHADOW_REG_VALUE_34 0x984 -#define QCA6390_PCIE_SHADOW_REG_VALUE_35 0x988 -#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL3 0x1F80118 -#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL4 0x1F8011C -#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL5 0x1F80120 - -#define SHADOW_REG_INTER_COUNT 43 -#define QCA6390_PCIE_SHADOW_REG_INTER_0 0x1E05000 -#define QCA6390_PCIE_SHADOW_REG_HUNG 0x1E050A8 - -#define QDSS_APB_DEC_CSR_BASE 0x1C01000 - -#define QDSS_APB_DEC_CSR_ETRIRQCTRL_OFFSET 0x6C -#define QDSS_APB_DEC_CSR_PRESERVEETF_OFFSET 0x70 -#define QDSS_APB_DEC_CSR_PRESERVEETR0_OFFSET 0x74 -#define QDSS_APB_DEC_CSR_PRESERVEETR1_OFFSET 0x78 - -#define MAX_UNWINDOWED_ADDRESS 0x80000 -#define WINDOW_ENABLE_BIT 0x40000000 -#define WINDOW_SHIFT 19 -#define WINDOW_VALUE_MASK 0x3F -#define WINDOW_START MAX_UNWINDOWED_ADDRESS -#define WINDOW_RANGE_MASK 0x7FFFF +#define MHI_M2_TIMEOUT_MS (plat_priv->ctrl_params.mhi_m2_timeout) #define FORCE_WAKE_DELAY_MIN_US 4000 #define FORCE_WAKE_DELAY_MAX_US 6000 #define FORCE_WAKE_DELAY_TIMEOUT_US 60000 -#define QCA6390_TIME_SYNC_ENABLE 0x80000000 -#define QCA6390_TIME_SYNC_CLEAR 0x0 +#define POWER_ON_RETRY_MAX_TIMES 3 +#define POWER_ON_RETRY_DELAY_MS 200 static struct cnss_pci_reg ce_src[] = { { "SRC_RING_BASE_LSB", QCA6390_CE_SRC_RING_BASE_LSB_OFFSET }, @@ -178,6 +118,240 @@ static struct cnss_pci_reg qdss_csr[] = { { NULL }, }; +static struct cnss_misc_reg wcss_reg_access_seq[] = { + {0, QCA6390_GCC_DEBUG_CLK_CTL, 0}, + {1, QCA6390_GCC_DEBUG_CLK_CTL, 0x802}, + {0, QCA6390_GCC_DEBUG_CLK_CTL, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_PLL_MODE, 0}, + {1, QCA6390_GCC_DEBUG_CLK_CTL, 0x805}, + {0, QCA6390_GCC_DEBUG_CLK_CTL, 0}, + {0, QCA6390_WCSS_WFSS_PMM_WFSS_PMM_R0_PMM_CTRL, 0}, + {0, QCA6390_WCSS_PMM_TOP_PMU_CX_CSR, 0}, + {0, QCA6390_WCSS_PMM_TOP_AON_INT_RAW_STAT, 0}, + {0, QCA6390_WCSS_PMM_TOP_AON_INT_EN, 0}, + {0, QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_STS, 0}, + {1, QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_CTL, 0xD}, + {0, QCA6390_WCSS_PMM_TOP_TESTBUS_STS, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG, 0}, + {1, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG, 0}, + {1, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0x8}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_STS, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_CTL, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_0, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_9, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS0, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS1, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS2, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS3, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS4, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS5, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS6, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE0, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE1, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE2, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE3, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE4, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE5, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE6, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING0, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING1, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING2, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING3, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING4, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING5, 0}, + {0, QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING6, 0}, + {1, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0x30040}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {1, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0x30105}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE, 0}, + {0, QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL, 0}, + {0, QCA6390_WCSS_CC_WCSS_UMAC_NOC_CBCR, 0}, + {0, QCA6390_WCSS_CC_WCSS_UMAC_AHB_CBCR, 0}, + {0, QCA6390_WCSS_CC_WCSS_UMAC_GDSCR, 0}, + {0, QCA6390_WCSS_CC_WCSS_WLAN1_GDSCR, 0}, + {0, QCA6390_WCSS_CC_WCSS_WLAN2_GDSCR, 0}, + {0, QCA6390_WCSS_PMM_TOP_PMM_INT_CLR, 0}, + {0, QCA6390_WCSS_PMM_TOP_AON_INT_STICKY_EN, 0}, +}; + +static struct cnss_misc_reg pcie_reg_access_seq[] = { + {0, QCA6390_PCIE_PCIE_WCSS_STATUS_FOR_DEBUG_LOW_PCIE_LOCAL_REG, 0}, + {0, QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG, 0}, + {1, QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG, 0x18}, + {0, QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG, 0}, + {0, QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG, 0}, + {0, QCA6390_PCIE_SOC_PCIE_WRAP_INTR_STATUS_SOC_PCIE_REG, 0}, + {0, QCA6390_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG, 0}, + {0, QCA6390_TLMM_GPIO_IN_OUT57, 0}, + {0, QCA6390_TLMM_GPIO_INTR_CFG57, 0}, + {0, QCA6390_TLMM_GPIO_INTR_STATUS57, 0}, + {0, QCA6390_TLMM_GPIO_IN_OUT59, 0}, + {0, QCA6390_TLMM_GPIO_INTR_CFG59, 0}, + {0, QCA6390_TLMM_GPIO_INTR_STATUS59, 0}, + {0, QCA6390_PCIE_PCIE_PARF_LTSSM, 0}, + {0, QCA6390_PCIE_PCIE_PARF_PM_STTS, 0}, + {0, QCA6390_PCIE_PCIE_PARF_PM_STTS_1, 0}, + {0, QCA6390_PCIE_PCIE_PARF_INT_STATUS, 0}, + {0, QCA6390_PCIE_PCIE_INT_ALL_STATUS, 0}, + {0, QCA6390_PCIE_PCIE_INT_ALL_MASK, 0}, + {0, QCA6390_PCIE_PCIE_PARF_BDF_TO_SID_CFG, 0}, + {0, QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_4, 0}, + {0, QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_3, 0}, + {0, QCA6390_PCIE_PCIE_PARF_MHI_CLOCK_RESET_CTRL, 0}, + {0, QCA6390_PCIE_PCIE_PARF_MHI_BASE_ADDR_LOWER, 0}, + {0, QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS, 0}, + {0, QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_CFG, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1SUB, 0}, + {0, QCA6390_PCIE_PCIE_CORE_CONFIG, 0}, + {0, QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_4, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L2, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1, 0}, + {0, QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2, 0}, + {0, QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_HIGH, 0}, + {0, QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_LOW, 0}, + {0, QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_HIGH, 0}, + {0, QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_LOW, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_STATUS_REG2, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_STATUS_REG2, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN2_CFG_REG1, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN1_CFG_REG1, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_APS_STATUS_REG1, 0}, + {0, QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_APS_STATUS_REG1, 0}, +}; + +static struct cnss_misc_reg wlaon_reg_access_seq[] = { + {0, QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_HI_REG, 0}, + {0, QCA6390_WLAON_SOC_POWER_CTRL, 0}, + {0, QCA6390_WLAON_PCIE_PWR_CTRL_REG, 0}, + {0, QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_LO_REG, 0}, + {0, QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_HI_REG, 0}, + {0, QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_LO_REG, 0}, + {0, QCA6390_WLAON_SOC_POWER_CTRL, 0}, + {0, QCA6390_WLAON_SOC_PWR_WDG_BARK_THRSHD, 0}, + {0, QCA6390_WLAON_SOC_PWR_WDG_BITE_THRSHD, 0}, + {0, QCA6390_WLAON_SW_COLD_RESET, 0}, + {0, QCA6390_WLAON_RFA_MEM_SLP_NRET_N_OVERRIDE, 0}, + {0, QCA6390_WLAON_GDSC_DELAY_SETTING, 0}, + {0, QCA6390_WLAON_GDSC_DELAY_SETTING2, 0}, + {0, QCA6390_WLAON_WL_PWR_STATUS_REG, 0}, + {0, QCA6390_WLAON_WL_AON_DBG_CFG_REG, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL1, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL6, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL7, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL3, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL4, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL8, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL2, 0}, + {0, QCA6390_WLAON_GLOBAL_COUNTER_CTRL9, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL1, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL2, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL3, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL4, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL5, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL6, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL7, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL8, 0}, + {0, QCA6390_WLAON_RTC_CLK_CAL_CTRL9, 0}, + {0, QCA6390_WLAON_WCSSAON_CONFIG_REG, 0}, + {0, QCA6390_WLAON_WLAN_OEM_DEBUG_REG, 0}, + {0, QCA6390_WLAON_WLAN_RAM_DUMP_REG, 0}, + {0, QCA6390_WLAON_QDSS_WCSS_REG, 0}, + {0, QCA6390_WLAON_QDSS_WCSS_ACK, 0}, + {0, QCA6390_WLAON_WL_CLK_CNTL_KDF_REG, 0}, + {0, QCA6390_WLAON_WL_CLK_CNTL_PMU_HFRC_REG, 0}, + {0, QCA6390_WLAON_QFPROM_PWR_CTRL_REG, 0}, + {0, QCA6390_WLAON_DLY_CONFIG, 0}, + {0, QCA6390_WLAON_WLAON_Q6_IRQ_REG, 0}, + {0, QCA6390_WLAON_PCIE_INTF_SW_CFG_REG, 0}, + {0, QCA6390_WLAON_PCIE_INTF_STICKY_SW_CFG_REG, 0}, + {0, QCA6390_WLAON_PCIE_INTF_PHY_SW_CFG_REG, 0}, + {0, QCA6390_WLAON_PCIE_INTF_PHY_NOCSR_SW_CFG_REG, 0}, + {0, QCA6390_WLAON_Q6_COOKIE_BIT, 0}, + {0, QCA6390_WLAON_WARM_SW_ENTRY, 0}, + {0, QCA6390_WLAON_RESET_DBG_SW_ENTRY, 0}, + {0, QCA6390_WLAON_WL_PMUNOC_CFG_REG, 0}, + {0, QCA6390_WLAON_RESET_CAUSE_CFG_REG, 0}, + {0, QCA6390_WLAON_SOC_WCSSAON_WAKEUP_IRQ_7_EN_REG, 0}, + {0, QCA6390_WLAON_DEBUG, 0}, + {0, QCA6390_WLAON_SOC_PARAMETERS, 0}, + {0, QCA6390_WLAON_WLPM_SIGNAL, 0}, + {0, QCA6390_WLAON_SOC_RESET_CAUSE_REG, 0}, + {0, QCA6390_WLAON_WAKEUP_PCIE_SOC_REG, 0}, + {0, QCA6390_WLAON_PBL_STACK_CANARY, 0}, + {0, QCA6390_WLAON_MEM_TOT_NUM_GRP_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP0_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP1_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP2_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP3_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP0_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP1_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP2_REG, 0}, + {0, QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP3_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP0_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP1_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP2_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP3_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP0_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP1_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP2_REG, 0}, + {0, QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP3_REG, 0}, + {0, QCA6390_WLAON_MEM_CNT_SEL_REG, 0}, + {0, QCA6390_WLAON_MEM_NO_EXTBHS_REG, 0}, + {0, QCA6390_WLAON_MEM_DEBUG_REG, 0}, + {0, QCA6390_WLAON_MEM_DEBUG_BUS_REG, 0}, + {0, QCA6390_WLAON_MEM_REDUN_CFG_REG, 0}, + {0, QCA6390_WLAON_WL_AON_SPARE2, 0}, + {0, QCA6390_WLAON_VSEL_CFG_FOR_WL_RET_DISABLE_REG, 0}, + {0, QCA6390_WLAON_BTFM_WLAN_IPC_STATUS_REG, 0}, + {0, QCA6390_WLAON_MPM_COUNTER_CHICKEN_BITS, 0}, + {0, QCA6390_WLAON_WLPM_CHICKEN_BITS, 0}, + {0, QCA6390_WLAON_PCIE_PHY_PWR_REG, 0}, + {0, QCA6390_WLAON_WL_CLK_CNTL_PMU_LPO2M_REG, 0}, + {0, QCA6390_WLAON_WL_SS_ROOT_CLK_SWITCH_REG, 0}, + {0, QCA6390_WLAON_POWERCTRL_PMU_REG, 0}, + {0, QCA6390_WLAON_POWERCTRL_MEM_REG, 0}, + {0, QCA6390_WLAON_PCIE_PWR_CTRL_REG, 0}, + {0, QCA6390_WLAON_SOC_PWR_PROFILE_REG, 0}, + {0, QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_HI_REG, 0}, + {0, QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_LO_REG, 0}, + {0, QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_HI_REG, 0}, + {0, QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_LO_REG, 0}, + {0, QCA6390_WLAON_MEM_SVS_CFG_REG, 0}, + {0, QCA6390_WLAON_CMN_AON_MISC_REG, 0}, + {0, QCA6390_WLAON_INTR_STATUS, 0}, + {0, QCA6390_SYSPM_SYSPM_PWR_STATUS, 0}, + {0, QCA6390_SYSPM_DBG_BTFM_AON_REG, 0}, + {0, QCA6390_SYSPM_DBG_BUS_SEL_REG, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, + {0, QCA6390_SYSPM_WCSSAON_SR_STATUS, 0}, +}; + +#define WCSS_REG_SIZE ARRAY_SIZE(wcss_reg_access_seq) +#define PCIE_REG_SIZE ARRAY_SIZE(pcie_reg_access_seq) +#define WLAON_REG_SIZE ARRAY_SIZE(wlaon_reg_access_seq) + static int cnss_pci_check_link_status(struct cnss_pci_data *pci_priv) { u16 device_id; @@ -221,11 +395,12 @@ static int cnss_pci_reg_read(struct cnss_pci_data *pci_priv, u32 offset, u32 *val) { int ret; - unsigned long flags; - ret = cnss_pci_check_link_status(pci_priv); - if (ret) - return ret; + if (!in_interrupt() && !irqs_disabled()) { + ret = cnss_pci_check_link_status(pci_priv); + if (ret) + return ret; + } if (pci_priv->pci_dev->device == QCA6174_DEVICE_ID || offset < MAX_UNWINDOWED_ADDRESS) { @@ -233,12 +408,12 @@ static int cnss_pci_reg_read(struct cnss_pci_data *pci_priv, return 0; } - spin_lock_irqsave(&pci_reg_window_lock, flags); + spin_lock_bh(&pci_reg_window_lock); cnss_pci_select_window(pci_priv, offset); *val = readl_relaxed(pci_priv->bar + WINDOW_START + (offset & WINDOW_RANGE_MASK)); - spin_unlock_irqrestore(&pci_reg_window_lock, flags); + spin_unlock_bh(&pci_reg_window_lock); return 0; } @@ -247,11 +422,12 @@ static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset, u32 val) { int ret; - unsigned long flags; - ret = cnss_pci_check_link_status(pci_priv); - if (ret) - return ret; + if (!in_interrupt() && !irqs_disabled()) { + ret = cnss_pci_check_link_status(pci_priv); + if (ret) + return ret; + } if (pci_priv->pci_dev->device == QCA6174_DEVICE_ID || offset < MAX_UNWINDOWED_ADDRESS) { @@ -259,12 +435,12 @@ static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset, return 0; } - spin_lock_irqsave(&pci_reg_window_lock, flags); + spin_lock_bh(&pci_reg_window_lock); cnss_pci_select_window(pci_priv, offset); writel_relaxed(val, pci_priv->bar + WINDOW_START + (offset & WINDOW_RANGE_MASK)); - spin_unlock_irqrestore(&pci_reg_window_lock, flags); + spin_unlock_bh(&pci_reg_window_lock); return 0; } @@ -277,7 +453,8 @@ static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv) ret = cnss_pci_force_wake_request(dev); if (ret) { - cnss_pr_err("Failed to request force wake\n"); + if (ret != -EAGAIN) + cnss_pr_err("Failed to request force wake\n"); return ret; } @@ -301,12 +478,74 @@ static int cnss_pci_force_wake_put(struct cnss_pci_data *pci_priv) int ret; ret = cnss_pci_force_wake_release(dev); - if (ret) + if (ret && ret != -EAGAIN) cnss_pr_err("Failed to release force wake\n"); return ret; } +int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset, + u32 *val) +{ + int ret = 0; + + ret = cnss_pci_is_device_down(&pci_priv->pci_dev->dev); + if (ret) + goto out; + + ret = cnss_pci_pm_runtime_get_sync(pci_priv); + if (ret < 0) + goto runtime_pm_put; + + cnss_pci_force_wake_get(pci_priv); + + ret = cnss_pci_reg_read(pci_priv, offset, val); + if (ret) { + cnss_pr_err("Failed to read register offset 0x%x, err = %d\n", + offset, ret); + goto force_wake_put; + } + +force_wake_put: + cnss_pci_force_wake_put(pci_priv); +runtime_pm_put: + cnss_pci_pm_runtime_mark_last_busy(pci_priv); + cnss_pci_pm_runtime_put_autosuspend(pci_priv); +out: + return ret; +} + +int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset, + u32 val) +{ + int ret = 0; + + ret = cnss_pci_is_device_down(&pci_priv->pci_dev->dev); + if (ret) + goto out; + + ret = cnss_pci_pm_runtime_get_sync(pci_priv); + if (ret < 0) + goto runtime_pm_put; + + cnss_pci_force_wake_get(pci_priv); + + ret = cnss_pci_reg_write(pci_priv, offset, val); + if (ret) { + cnss_pr_err("Failed to write 0x%x to register offset 0x%x, err = %d\n", + val, offset, ret); + goto force_wake_put; + } + +force_wake_put: + cnss_pci_force_wake_put(pci_priv); +runtime_pm_put: + cnss_pci_pm_runtime_mark_last_busy(pci_priv); + cnss_pci_pm_runtime_put_autosuspend(pci_priv); +out: + return ret; +} + static int cnss_set_pci_config_space(struct cnss_pci_data *pci_priv, bool save) { struct pci_dev *pci_dev = pci_priv->pci_dev; @@ -478,8 +717,10 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) } ret = cnss_set_pci_link(pci_priv, PCI_LINK_UP); - if (ret) + if (ret) { + ret = -EAGAIN; goto out; + } pci_priv->pci_link_state = PCI_LINK_UP; @@ -545,27 +786,45 @@ int cnss_pci_link_down(struct device *dev) } EXPORT_SYMBOL(cnss_pci_link_down); -int cnss_pci_is_device_down(struct device *dev) +int cnss_pcie_is_device_down(struct cnss_pci_data *pci_priv) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); - struct cnss_pci_data *pci_priv; + struct cnss_plat_data *plat_priv; - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL\n"); + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); return -ENODEV; } - pci_priv = plat_priv->bus_priv; - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL\n"); + plat_priv = pci_priv->plat_priv; + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); return -ENODEV; } return test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) | pci_priv->pci_link_down_ind; } + +int cnss_pci_is_device_down(struct device *dev) +{ + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); + + return cnss_pcie_is_device_down(pci_priv); +} EXPORT_SYMBOL(cnss_pci_is_device_down); +void cnss_pci_lock_reg_window(struct device *dev, unsigned long *flags) +{ + spin_lock_bh(&pci_reg_window_lock); +} +EXPORT_SYMBOL(cnss_pci_lock_reg_window); + +void cnss_pci_unlock_reg_window(struct device *dev, unsigned long *flags) +{ + spin_unlock_bh(&pci_reg_window_lock); +} +EXPORT_SYMBOL(cnss_pci_unlock_reg_window); + static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state) { switch (mhi_state) { @@ -575,6 +834,8 @@ static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state) return "DEINIT"; case CNSS_MHI_POWER_ON: return "POWER_ON"; + case CNSS_MHI_POWERING_OFF: + return "POWERING_OFF"; case CNSS_MHI_POWER_OFF: return "POWER_OFF"; case CNSS_MHI_FORCE_POWER_OFF: @@ -635,6 +896,8 @@ static int cnss_pci_check_mhi_state_bit(struct cnss_pci_data *pci_priv, cnss_pr_err("Cannot set MHI state %s(%d) in current MHI state (0x%lx)\n", cnss_mhi_state_to_str(mhi_state), mhi_state, pci_priv->mhi_state); + if (mhi_state != CNSS_MHI_TRIGGER_RDDM) + CNSS_ASSERT(0); return -EINVAL; } @@ -652,9 +915,13 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv, case CNSS_MHI_POWER_ON: set_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state); break; + case CNSS_MHI_POWERING_OFF: + set_bit(CNSS_MHI_POWERING_OFF, &pci_priv->mhi_state); + break; case CNSS_MHI_POWER_OFF: case CNSS_MHI_FORCE_POWER_OFF: clear_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state); + clear_bit(CNSS_MHI_POWERING_OFF, &pci_priv->mhi_state); clear_bit(CNSS_MHI_TRIGGER_RDDM, &pci_priv->mhi_state); clear_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state); break; @@ -705,6 +972,11 @@ static int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, break; case CNSS_MHI_POWER_ON: ret = mhi_sync_power_up(pci_priv->mhi_ctrl); + /* -ETIMEDOUT means MHI power up has succeeded but timed out + * for firmware mission mode event, so handle it properly. + */ + if (ret == -ETIMEDOUT) + ret = 0; break; case CNSS_MHI_POWER_OFF: mhi_power_down(pci_priv->mhi_ctrl, true); @@ -765,6 +1037,7 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) if (MHI_TIMEOUT_OVERWRITE_MS) pci_priv->mhi_ctrl->timeout_ms = MHI_TIMEOUT_OVERWRITE_MS; + pci_priv->mhi_ctrl->m2_timeout_ms = MHI_M2_TIMEOUT_MS; ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_INIT); if (ret) @@ -788,6 +1061,7 @@ static void cnss_pci_power_off_mhi(struct cnss_pci_data *pci_priv) return; cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_RESUME); + cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_POWERING_OFF); if (!pci_priv->pci_link_down_ind) cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_OFF); @@ -817,11 +1091,6 @@ static int cnss_pci_get_device_timestamp(struct cnss_pci_data *pci_priv, return -EINVAL; } - cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5, - QCA6390_TIME_SYNC_CLEAR); - cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5, - QCA6390_TIME_SYNC_ENABLE); - cnss_pci_reg_read(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL3, &low); cnss_pci_reg_read(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL4, &high); @@ -832,9 +1101,22 @@ static int cnss_pci_get_device_timestamp(struct cnss_pci_data *pci_priv, return 0; } +static void cnss_pci_enable_time_sync_counter(struct cnss_pci_data *pci_priv) +{ + cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5, + QCA6390_TIME_SYNC_ENABLE); +} + +static void cnss_pci_clear_time_sync_counter(struct cnss_pci_data *pci_priv) +{ + cnss_pci_reg_write(pci_priv, QCA6390_WLAON_GLOBAL_COUNTER_CTRL5, + QCA6390_TIME_SYNC_CLEAR); +} + static int cnss_pci_update_timestamp(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + unsigned long flags = 0; u64 host_time_us, device_time_us, offset; u32 low, high; int ret; @@ -847,8 +1129,13 @@ static int cnss_pci_update_timestamp(struct cnss_pci_data *pci_priv) if (ret) return ret; + spin_lock_irqsave(&time_sync_lock, flags); + cnss_pci_clear_time_sync_counter(pci_priv); + cnss_pci_enable_time_sync_counter(pci_priv); host_time_us = cnss_get_host_timestamp(plat_priv); ret = cnss_pci_get_device_timestamp(pci_priv, &device_time_us); + cnss_pci_clear_time_sync_counter(pci_priv); + spin_unlock_irqrestore(&time_sync_lock, flags); if (ret) goto force_wake_put; @@ -937,6 +1224,73 @@ static void cnss_pci_stop_time_sync_update(struct cnss_pci_data *pci_priv) cancel_delayed_work_sync(&pci_priv->time_sync_work); } +static int cnss_pci_pm_qos_notify(struct notifier_block *nb, + unsigned long curr_val, void *cpus) +{ + struct cnss_pci_data *pci_priv = + container_of(nb, struct cnss_pci_data, pm_qos_nb); + unsigned long flags; + + spin_lock_irqsave(&pm_qos_lock, flags); + + if (!pci_priv->runtime_pm_prevented && + curr_val != PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE) { + cnss_pci_pm_runtime_get_noresume(pci_priv); + pci_priv->runtime_pm_prevented = true; + } else if (pci_priv->runtime_pm_prevented && + curr_val == PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE) { + cnss_pci_pm_runtime_put_noidle(pci_priv); + pci_priv->runtime_pm_prevented = false; + } + + spin_unlock_irqrestore(&pm_qos_lock, flags); + + return NOTIFY_DONE; +} + +static int cnss_pci_pm_qos_add_notifier(struct cnss_pci_data *pci_priv) +{ + int ret; + + if (pci_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + pci_priv->pm_qos_nb.notifier_call = cnss_pci_pm_qos_notify; + ret = pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, + &pci_priv->pm_qos_nb); + if (ret) + cnss_pr_err("Failed to add qos notifier, err = %d\n", + ret); + + return ret; +} + +static int cnss_pci_pm_qos_remove_notifier(struct cnss_pci_data *pci_priv) +{ + int ret; + unsigned long flags; + + if (pci_priv->device_id == QCA6174_DEVICE_ID) + return 0; + + ret = pm_qos_remove_notifier(PM_QOS_CPU_DMA_LATENCY, + &pci_priv->pm_qos_nb); + if (ret) + cnss_pr_dbg("Failed to remove qos notifier, err = %d\n", + ret); + + spin_lock_irqsave(&pm_qos_lock, flags); + + if (pci_priv->runtime_pm_prevented) { + cnss_pci_pm_runtime_put_noidle(pci_priv); + pci_priv->runtime_pm_prevented = false; + } + + spin_unlock_irqrestore(&pm_qos_lock, flags); + + return ret; +} + int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -998,6 +1352,7 @@ int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv) } cnss_pci_start_time_sync_update(pci_priv); + cnss_pci_pm_qos_add_notifier(pci_priv); return 0; @@ -1015,8 +1370,6 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) plat_priv = pci_priv->plat_priv; - cnss_pci_stop_time_sync_update(pci_priv); - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { @@ -1029,13 +1382,15 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) return -EINVAL; } + cnss_pci_pm_qos_remove_notifier(pci_priv); + cnss_pci_stop_time_sync_update(pci_priv); + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { pci_priv->driver_ops->shutdown(pci_priv->pci_dev); } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { pci_priv->driver_ops->remove(pci_priv->pci_dev); clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); - clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); } else if (test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) { ret = pci_priv->driver_ops->idle_shutdown(pci_priv->pci_dev); @@ -1044,9 +1399,11 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) &plat_priv->driver_state); return ret; } - clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); } + plat_priv->get_info_cb_ctx = NULL; + plat_priv->get_info_cb = NULL; + return 0; } @@ -1086,6 +1443,74 @@ int cnss_pci_update_status(struct cnss_pci_data *pci_priv, return 0; } +static void cnss_pci_misc_reg_dump(struct cnss_pci_data *pci_priv, + struct cnss_misc_reg *misc_reg, + u32 misc_reg_size, + char *reg_name) +{ + int i; + + if (!misc_reg) + return; + + if (in_interrupt() || irqs_disabled()) + return; + + if (cnss_pci_check_link_status(pci_priv)) + return; + + cnss_pci_force_wake_get(pci_priv); + + cnss_pr_dbg("start to dump %s registers\n", reg_name); + + for (i = 0; i < misc_reg_size; i++) { + if (misc_reg[i].wr) { + if (misc_reg[i].offset == + QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG && + i >= 1) + misc_reg[i].val = + QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG_MSK | + misc_reg[i - 1].val; + if (cnss_pci_reg_write(pci_priv, + misc_reg[i].offset, + misc_reg[i].val)) + goto force_wake_put; + cnss_pr_vdbg("Write 0x%X to 0x%X\n", + misc_reg[i].val, + misc_reg[i].offset); + + } else { + if (cnss_pci_reg_read(pci_priv, + misc_reg[i].offset, + &misc_reg[i].val)) + goto force_wake_put; + cnss_pr_vdbg("Read 0x%X from 0x%X\n", + misc_reg[i].val, + misc_reg[i].offset); + } + } + +force_wake_put: + cnss_pci_force_wake_put(pci_priv); +} + +static void cnss_pci_dump_misc_reg(struct cnss_pci_data *pci_priv) +{ + if (in_interrupt() || irqs_disabled()) + return; + + if (cnss_pci_check_link_status(pci_priv)) + return; + + mhi_debug_reg_dump(pci_priv->mhi_ctrl); + cnss_pci_misc_reg_dump(pci_priv, pci_priv->wcss_reg, + pci_priv->wcss_reg_size, "wcss"); + cnss_pci_misc_reg_dump(pci_priv, pci_priv->pcie_reg, + pci_priv->pcie_reg_size, "pcie"); + cnss_pci_misc_reg_dump(pci_priv, pci_priv->wlaon_reg, + pci_priv->wlaon_reg_size, "wlaon"); +} + static void cnss_pci_dump_shadow_reg(struct cnss_pci_data *pci_priv) { int i, j = 0, array_size = SHADOW_REG_COUNT + SHADOW_REG_INTER_COUNT; @@ -1231,13 +1656,14 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) int ret = 0; struct cnss_plat_data *plat_priv = pci_priv->plat_priv; unsigned int timeout; + int retry = 0; - if (plat_priv->ramdump_info_v2.dump_data_valid || - test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + if (plat_priv->ramdump_info_v2.dump_data_valid) { cnss_pci_clear_dump_info(pci_priv); cnss_pci_deinit_mhi(pci_priv); } +retry: ret = cnss_power_on_device(plat_priv); if (ret) { cnss_pr_err("Failed to power on device, err = %d\n", ret); @@ -1247,6 +1673,18 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) ret = cnss_resume_pci_link(pci_priv); if (ret) { cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + if (test_bit(IGNORE_PCI_LINK_FAILURE, + &plat_priv->ctrl_params.quirks)) { + cnss_pr_dbg("Ignore PCI link resume failure\n"); + ret = 0; + goto out; + } + if (ret == -EAGAIN && retry++ < POWER_ON_RETRY_MAX_TIMES) { + cnss_power_off_device(plat_priv); + cnss_pr_dbg("Retry to resume PCI link #%d\n", retry); + msleep(POWER_ON_RETRY_DELAY_MS * retry); + goto retry; + } goto power_off; } @@ -1275,6 +1713,8 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) if (ret) goto stop_mhi; } else if (timeout) { + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) + timeout = timeout << 1; mod_timer(&plat_priv->fw_boot_timer, jiffies + msecs_to_jiffies(timeout << 1)); } @@ -1316,20 +1756,28 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) cnss_pci_collect_dump(pci_priv); } + if (!cnss_is_device_powered_on(plat_priv)) { + cnss_pr_dbg("Device is already powered off, ignore\n"); + goto skip_power_off; + } + cnss_pci_power_off_mhi(pci_priv); ret = cnss_suspend_pci_link(pci_priv); if (ret) cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); - if (!plat_priv->ramdump_info_v2.dump_data_valid && - !test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) + if (!plat_priv->ramdump_info_v2.dump_data_valid) cnss_pci_deinit_mhi(pci_priv); cnss_power_off_device(plat_priv); +skip_power_off: pci_priv->remap_window = 0; clear_bit(CNSS_FW_READY, &plat_priv->driver_state); clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) + clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); clear_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state); @@ -1531,17 +1979,18 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) if (!test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) goto register_driver; - cal_info = kzalloc(sizeof(*cal_info), GFP_KERNEL); - if (!cal_info) - return -ENOMEM; - cnss_pr_dbg("Start to wait for calibration to complete\n"); timeout = cnss_get_boot_timeout(&pci_priv->pci_dev->dev); ret = wait_for_completion_timeout(&plat_priv->cal_complete, - msecs_to_jiffies(timeout)); + msecs_to_jiffies(timeout) << 2); if (!ret) { cnss_pr_err("Timeout waiting for calibration to complete\n"); + + cal_info = kzalloc(sizeof(*cal_info), GFP_KERNEL); + if (!cal_info) + return -ENOMEM; + cal_info->cal_status = CNSS_CAL_TIMEOUT; cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE, @@ -1619,9 +2068,16 @@ int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv) static bool cnss_pci_is_drv_supported(struct cnss_pci_data *pci_priv) { struct pci_dev *root_port = pci_find_pcie_root_port(pci_priv->pci_dev); - struct device_node *root_of_node = root_port->dev.of_node; + struct device_node *root_of_node; bool drv_supported = false; + if (!root_port) { + cnss_pr_err("PCIe DRV is not supported as root port is null\n"); + return drv_supported; + } + + root_of_node = root_port->dev.of_node; + if (root_of_node->parent) drv_supported = of_property_read_bool(root_of_node->parent, "qcom,drv-supported"); @@ -1684,6 +2140,8 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify) break; case MSM_PCIE_EVENT_DRV_DISCONNECT: cnss_pr_dbg("DRV subsystem is disconnected\n"); + if (cnss_pci_get_auto_suspended(pci_priv)) + cnss_pci_pm_request_resume(pci_priv); cnss_pci_set_drv_connected(pci_priv, 0); break; default: @@ -1723,15 +2181,129 @@ static void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) msm_pcie_deregister_event(&pci_priv->msm_pci_event); } +static int cnss_pci_suspend_driver(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct cnss_wlan_driver *driver_ops = pci_priv->driver_ops; + int ret = 0; + + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + + if (driver_ops && driver_ops->suspend) { + ret = driver_ops->suspend(pci_dev, state); + if (ret) { + cnss_pr_err("Failed to suspend host driver, err = %d\n", + ret); + ret = -EAGAIN; + } + } + + return ret; +} + +static int cnss_pci_resume_driver(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct cnss_wlan_driver *driver_ops = pci_priv->driver_ops; + int ret = 0; + + if (driver_ops && driver_ops->resume) { + ret = driver_ops->resume(pci_dev); + if (ret) + cnss_pr_err("Failed to resume host driver, err = %d\n", + ret); + } + + return ret; +} + +int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + int ret = 0; + + if (pci_priv->pci_link_state == PCI_LINK_DOWN) + goto out; + + if (cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND)) { + ret = -EAGAIN; + goto out; + } + + if (pci_priv->drv_connected_last) + goto skip_disable_pci; + + pci_clear_master(pci_dev); + cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); + pci_disable_device(pci_dev); + + ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (ret) + cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); + +skip_disable_pci: + if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { + ret = -EAGAIN; + goto resume_mhi; + } + pci_priv->pci_link_state = PCI_LINK_DOWN; + + return 0; + +resume_mhi: + if (!pci_is_enabled(pci_dev)) + if (pci_enable_device(pci_dev)) + cnss_pr_err("Failed to enable PCI device\n"); + if (pci_priv->saved_state) + cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); + pci_set_master(pci_dev); + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); +out: + return ret; +} + +int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + int ret = 0; + + if (pci_priv->pci_link_state == PCI_LINK_UP) + goto out; + + if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) { + cnss_fatal_err("Failed to resume PCI link from suspend\n"); + cnss_pci_link_down(&pci_dev->dev); + ret = -EAGAIN; + goto out; + } + + if (pci_priv->drv_connected_last) + goto skip_enable_pci; + + ret = pci_enable_device(pci_dev); + if (ret) { + cnss_pr_err("Failed to enable PCI device, err = %d\n", + ret); + goto out; + } + + if (pci_priv->saved_state) + cnss_set_pci_config_space(pci_priv, + RESTORE_PCI_CONFIG_SPACE); + pci_set_master(pci_dev); + +skip_enable_pci: + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + pci_priv->pci_link_state = PCI_LINK_UP; +out: + return ret; +} + static int cnss_pci_suspend(struct device *dev) { int ret = 0; - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); struct cnss_plat_data *plat_priv; - struct cnss_wlan_driver *driver_ops; - - pm_message_t state = { .event = PM_EVENT_SUSPEND }; if (!pci_priv) goto out; @@ -1743,62 +2315,34 @@ static int cnss_pci_suspend(struct device *dev) if (!cnss_is_device_powered_on(plat_priv)) goto out; - set_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); - - if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks)) + if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks)) { pci_priv->drv_connected_last = cnss_pci_get_drv_connected(pci_priv); - - driver_ops = pci_priv->driver_ops; - if (driver_ops && driver_ops->suspend) { - ret = driver_ops->suspend(pci_dev, state); - if (ret) { - cnss_pr_err("Failed to suspend host driver, err = %d\n", - ret); + if (!pci_priv->drv_connected_last) { + cnss_pr_dbg("Firmware does not support non-DRV suspend, reject\n"); ret = -EAGAIN; - goto clear_flag; + goto out; } } - if (pci_priv->pci_link_state == PCI_LINK_UP && !pci_priv->disable_pc) { - if (cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND)) { - ret = -EAGAIN; - goto resume_driver; - } + set_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); - if (pci_priv->drv_connected_last) - goto skip_disable_pci; + ret = cnss_pci_suspend_driver(pci_priv); + if (ret) + goto clear_flag; - pci_clear_master(pci_dev); - cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); - pci_disable_device(pci_dev); - - ret = pci_set_power_state(pci_dev, PCI_D3hot); + if (!pci_priv->disable_pc) { + ret = cnss_pci_suspend_bus(pci_priv); if (ret) - cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); - -skip_disable_pci: - if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { - ret = -EAGAIN; - goto resume_mhi; - } - pci_priv->pci_link_state = PCI_LINK_DOWN; + goto resume_driver; } cnss_pci_set_monitor_wake_intr(pci_priv, false); return 0; -resume_mhi: - if (pci_enable_device(pci_dev)) - cnss_pr_err("Failed to enable PCI device\n"); - if (pci_priv->saved_state) - cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); - pci_set_master(pci_dev); - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); resume_driver: - if (driver_ops && driver_ops->resume) - driver_ops->resume(pci_dev); + cnss_pci_resume_driver(pci_priv); clear_flag: pci_priv->drv_connected_last = 0; clear_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); @@ -1812,7 +2356,6 @@ static int cnss_pci_resume(struct device *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; - struct cnss_wlan_driver *driver_ops; if (!pci_priv) goto out; @@ -1827,40 +2370,13 @@ static int cnss_pci_resume(struct device *dev) if (!cnss_is_device_powered_on(pci_priv->plat_priv)) goto out; - if (pci_priv->pci_link_state == PCI_LINK_DOWN && - !pci_priv->disable_pc) { - if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) { - cnss_fatal_err("Failed to resume PCI link from suspend\n"); - cnss_pci_link_down(dev); - ret = -EAGAIN; + if (!pci_priv->disable_pc) { + ret = cnss_pci_resume_bus(pci_priv); + if (ret) goto out; - } - pci_priv->pci_link_state = PCI_LINK_UP; - - if (pci_priv->drv_connected_last) - goto skip_enable_pci; - - ret = pci_enable_device(pci_dev); - if (ret) - cnss_pr_err("Failed to enable PCI device, err = %d\n", - ret); - - if (pci_priv->saved_state) - cnss_set_pci_config_space(pci_priv, - RESTORE_PCI_CONFIG_SPACE); - pci_set_master(pci_dev); - -skip_enable_pci: - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); } - driver_ops = pci_priv->driver_ops; - if (driver_ops && driver_ops->resume) { - ret = driver_ops->resume(pci_dev); - if (ret) - cnss_pr_err("Failed to resume host driver, err = %d\n", - ret); - } + ret = cnss_pci_resume_driver(pci_priv); pci_priv->drv_connected_last = 0; clear_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); @@ -1929,11 +2445,16 @@ static int cnss_pci_runtime_suspend(struct device *dev) return -EAGAIN; } - cnss_pr_vdbg("Runtime suspend start\n"); - - if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks)) + if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks)) { pci_priv->drv_connected_last = cnss_pci_get_drv_connected(pci_priv); + if (!pci_priv->drv_connected_last) { + cnss_pr_dbg("Firmware does not support non-DRV suspend, reject\n"); + return -EAGAIN; + } + } + + cnss_pr_vdbg("Runtime suspend start\n"); driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->runtime_ops && @@ -2032,46 +2553,74 @@ void cnss_pci_pm_runtime_show_usage_count(struct cnss_pci_data *pci_priv) int cnss_pci_pm_request_resume(struct cnss_pci_data *pci_priv) { - struct pci_dev *pci_dev; + struct device *dev; + enum rpm_status status; if (!pci_priv) return -ENODEV; - pci_dev = pci_priv->pci_dev; - if (!pci_dev) - return -ENODEV; + dev = &pci_priv->pci_dev->dev; - return pm_request_resume(&pci_dev->dev); + status = dev->power.runtime_status; + if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) + cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", + (void *)_RET_IP_); + + return pm_request_resume(dev); } int cnss_pci_pm_runtime_resume(struct cnss_pci_data *pci_priv) { - struct pci_dev *pci_dev; + struct device *dev; + enum rpm_status status; if (!pci_priv) return -ENODEV; - pci_dev = pci_priv->pci_dev; - if (!pci_dev) - return -ENODEV; + dev = &pci_priv->pci_dev->dev; - return pm_runtime_resume(&pci_dev->dev); + status = dev->power.runtime_status; + if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) + cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", + (void *)_RET_IP_); + + return pm_runtime_resume(dev); } int cnss_pci_pm_runtime_get(struct cnss_pci_data *pci_priv) { + struct device *dev; + enum rpm_status status; + if (!pci_priv) return -ENODEV; - return pm_runtime_get(&pci_priv->pci_dev->dev); + dev = &pci_priv->pci_dev->dev; + + status = dev->power.runtime_status; + if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) + cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", + (void *)_RET_IP_); + + return pm_runtime_get(dev); } int cnss_pci_pm_runtime_get_sync(struct cnss_pci_data *pci_priv) { + struct device *dev; + enum rpm_status status; + if (!pci_priv) return -ENODEV; - return pm_runtime_get_sync(&pci_priv->pci_dev->dev); + dev = &pci_priv->pci_dev->dev; + + status = dev->power.runtime_status; + if (status == RPM_SUSPENDING || status == RPM_SUSPENDED) + cnss_pr_vdbg("Runtime PM resume is requested by %ps\n", + (void *)_RET_IP_); + + return pm_runtime_get_sync(dev); } void cnss_pci_pm_runtime_get_noresume(struct cnss_pci_data *pci_priv) @@ -2079,22 +2628,40 @@ void cnss_pci_pm_runtime_get_noresume(struct cnss_pci_data *pci_priv) if (!pci_priv) return; - return pm_runtime_get_noresume(&pci_priv->pci_dev->dev); + pm_runtime_get_noresume(&pci_priv->pci_dev->dev); } int cnss_pci_pm_runtime_put_autosuspend(struct cnss_pci_data *pci_priv) { + struct device *dev; + if (!pci_priv) return -ENODEV; + dev = &pci_priv->pci_dev->dev; + + if (atomic_read(&dev->power.usage_count) == 0) { + cnss_pr_dbg("Ignore excessive runtime PM put operation\n"); + return -EINVAL; + } + return pm_runtime_put_autosuspend(&pci_priv->pci_dev->dev); } void cnss_pci_pm_runtime_put_noidle(struct cnss_pci_data *pci_priv) { + struct device *dev; + if (!pci_priv) return; + dev = &pci_priv->pci_dev->dev; + + if (atomic_read(&dev->power.usage_count) == 0) { + cnss_pr_dbg("Ignore excessive runtime PM put operation\n"); + return; + } + pm_runtime_put_noidle(&pci_priv->pci_dev->dev); } @@ -2120,45 +2687,21 @@ int cnss_auto_suspend(struct device *dev) if (!plat_priv) return -ENODEV; - if (pci_priv->pci_link_state == PCI_LINK_UP) { - if (cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND)) { - ret = -EAGAIN; - goto out; - } - - if (pci_priv->drv_connected_last) - goto skip_disable_pci; - - pci_clear_master(pci_dev); - cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); - pci_disable_device(pci_dev); - - ret = pci_set_power_state(pci_dev, PCI_D3hot); - if (ret) - cnss_pr_err("Failed to set D3Hot, err = %d\n", ret); - -skip_disable_pci: - if (cnss_set_pci_link(pci_priv, PCI_LINK_DOWN)) { - ret = -EAGAIN; - goto resume_mhi; - } - - pci_priv->pci_link_state = PCI_LINK_DOWN; + mutex_lock(&pci_priv->bus_lock); + ret = cnss_pci_suspend_bus(pci_priv); + if (ret) { + mutex_unlock(&pci_priv->bus_lock); + return ret; } cnss_pci_set_auto_suspended(pci_priv, 1); + mutex_unlock(&pci_priv->bus_lock); + cnss_pci_set_monitor_wake_intr(pci_priv, true); cnss_request_bus_bandwidth(dev, CNSS_BUS_WIDTH_NONE); return 0; - -resume_mhi: - if (pci_enable_device(pci_dev)) - cnss_pr_err("Failed to enable PCI device!\n"); - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); -out: - return ret; } EXPORT_SYMBOL(cnss_auto_suspend); @@ -2176,37 +2719,20 @@ int cnss_auto_resume(struct device *dev) if (!plat_priv) return -ENODEV; - if (pci_priv->pci_link_state == PCI_LINK_DOWN) { - if (cnss_set_pci_link(pci_priv, PCI_LINK_UP)) { - cnss_fatal_err("Failed to resume PCI link from suspend\n"); - cnss_pci_link_down(dev); - ret = -EAGAIN; - goto out; - } - pci_priv->pci_link_state = PCI_LINK_UP; - - if (pci_priv->drv_connected_last) - goto skip_enable_pci; - - ret = pci_enable_device(pci_dev); - if (ret) - cnss_pr_err("Failed to enable PCI device, err = %d\n", - ret); - - cnss_set_pci_config_space(pci_priv, RESTORE_PCI_CONFIG_SPACE); - pci_set_master(pci_dev); - -skip_enable_pci: - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); + mutex_lock(&pci_priv->bus_lock); + ret = cnss_pci_resume_bus(pci_priv); + if (ret) { + mutex_unlock(&pci_priv->bus_lock); + return ret; } cnss_pci_set_auto_suspended(pci_priv, 0); + mutex_unlock(&pci_priv->bus_lock); cnss_request_bus_bandwidth(dev, plat_priv->bus_bw_info->current_bw_vote); -out: - return ret; + return 0; } EXPORT_SYMBOL(cnss_auto_resume); @@ -2293,18 +2819,60 @@ int cnss_pci_force_wake_release(struct device *dev) } EXPORT_SYMBOL(cnss_pci_force_wake_release); +int cnss_pci_qmi_send_get(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) + return -ENODEV; + + mutex_lock(&pci_priv->bus_lock); + if (!cnss_pci_get_auto_suspended(pci_priv)) + goto out; + + cnss_pr_vdbg("Starting to handle get info prepare\n"); + + ret = cnss_pci_resume_bus(pci_priv); + +out: + mutex_unlock(&pci_priv->bus_lock); + return ret; +} + +int cnss_pci_qmi_send_put(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) + return -ENODEV; + + mutex_lock(&pci_priv->bus_lock); + if (!cnss_pci_get_auto_suspended(pci_priv)) + goto out; + + cnss_pr_vdbg("Starting to handle get info done\n"); + + ret = cnss_pci_suspend_bus(pci_priv); + +out: + mutex_unlock(&pci_priv->bus_lock); + return ret; +} + int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + struct device *dev = &pci_priv->pci_dev->dev; int i; for (i = 0; i < plat_priv->fw_mem_seg_len; i++) { if (!fw_mem[i].va && fw_mem[i].size) { fw_mem[i].va = - dma_alloc_coherent(&pci_priv->pci_dev->dev, - fw_mem[i].size, - &fw_mem[i].pa, GFP_KERNEL); + dma_alloc_attrs(dev, fw_mem[i].size, + &fw_mem[i].pa, GFP_KERNEL, + fw_mem[i].attrs); + if (!fw_mem[i].va) { cnss_pr_err("Failed to allocate memory for FW, size: 0x%zx, type: %u\n", fw_mem[i].size, fw_mem[i].type); @@ -2317,6 +2885,31 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) return 0; } +static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + struct device *dev = &pci_priv->pci_dev->dev; + int i; + + for (i = 0; i < plat_priv->fw_mem_seg_len; i++) { + if (fw_mem[i].va && fw_mem[i].size) { + cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n", + fw_mem[i].va, &fw_mem[i].pa, + fw_mem[i].size, fw_mem[i].type); + dma_free_attrs(dev, fw_mem[i].size, + fw_mem[i].va, fw_mem[i].pa, + fw_mem[i].attrs); + fw_mem[i].va = NULL; + fw_mem[i].pa = 0; + fw_mem[i].size = 0; + fw_mem[i].type = 0; + } + } + + plat_priv->fw_mem_seg_len = 0; +} + int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -2374,30 +2967,6 @@ void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv) plat_priv->qdss_mem_seg_len = 0; } -static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv) -{ - struct cnss_plat_data *plat_priv = pci_priv->plat_priv; - struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; - int i; - - for (i = 0; i < plat_priv->fw_mem_seg_len; i++) { - if (fw_mem[i].va && fw_mem[i].size) { - cnss_pr_dbg("Freeing memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx, type: %u\n", - fw_mem[i].va, &fw_mem[i].pa, - fw_mem[i].size, fw_mem[i].type); - dma_free_coherent(&pci_priv->pci_dev->dev, - fw_mem[i].size, fw_mem[i].va, - fw_mem[i].pa); - fw_mem[i].va = NULL; - fw_mem[i].pa = 0; - fw_mem[i].size = 0; - fw_mem[i].type = 0; - } - } - - plat_priv->fw_mem_seg_len = 0; -} - int cnss_pci_load_m3(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -2451,44 +3020,24 @@ static void cnss_pci_free_m3_mem(struct cnss_pci_data *pci_priv) m3_mem->size = 0; } -int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) -{ - int ret; - struct cnss_plat_data *plat_priv; - - if (!pci_priv) - return -ENODEV; - - plat_priv = pci_priv->plat_priv; - if (!plat_priv) - return -ENODEV; - - cnss_auto_resume(&pci_priv->pci_dev->dev); - cnss_pci_dump_shadow_reg(pci_priv); - - ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_TRIGGER_RDDM); - if (ret) { - cnss_fatal_err("Failed to trigger RDDM, err = %d\n", ret); - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_DEFAULT); - return ret; - } - - if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); - } - - return 0; -} - void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) { + struct cnss_plat_data *plat_priv; + if (!pci_priv) return; cnss_fatal_err("Timeout waiting for FW ready indication\n"); + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return; + + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { + cnss_pr_dbg("Ignore FW ready timeout for calibration mode\n"); + return; + } + cnss_schedule_recovery(&pci_priv->pci_dev->dev, CNSS_REASON_TIMEOUT); } @@ -2499,7 +3048,7 @@ static int cnss_pci_smmu_fault_handler(struct iommu_domain *domain, { struct cnss_pci_data *pci_priv = handler_token; - cnss_pr_err("SMMU fault happened with IOVA 0x%lx\n", iova); + cnss_fatal_err("SMMU fault happened with IOVA 0x%lx\n", iova); if (!pci_priv) { cnss_pr_err("pci_priv is NULL\n"); @@ -2587,9 +3136,14 @@ int cnss_smmu_map(struct device *dev, phys_addr_t paddr, uint32_t *iova_addr, size_t size) { struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); + struct cnss_plat_data *plat_priv; unsigned long iova; size_t len; int ret = 0; + int flag = IOMMU_READ | IOMMU_WRITE; + struct pci_dev *root_port; + struct device_node *root_of_node; + bool dma_coherent = false; if (!pci_priv) return -ENODEV; @@ -2600,6 +3154,8 @@ int cnss_smmu_map(struct device *dev, return -EINVAL; } + plat_priv = pci_priv->plat_priv; + len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE); iova = roundup(pci_priv->smmu_iova_ipa_start, PAGE_SIZE); @@ -2612,9 +3168,27 @@ int cnss_smmu_map(struct device *dev, return -ENOMEM; } + if (!test_bit(DISABLE_IO_COHERENCY, + &plat_priv->ctrl_params.quirks)) { + root_port = pci_find_pcie_root_port(pci_priv->pci_dev); + if (!root_port) { + cnss_pr_err("Root port is null, so dma_coherent is disabled\n"); + } else { + root_of_node = root_port->dev.of_node; + if (root_of_node && root_of_node->parent) { + dma_coherent = + of_property_read_bool(root_of_node->parent, + "dma-coherent"); + cnss_pr_dbg("dma-coherent is %s\n", + dma_coherent ? "enabled" : "disabled"); + if (dma_coherent) + flag |= IOMMU_CACHE; + } + } + } + ret = iommu_map(pci_priv->iommu_domain, iova, - rounddown(paddr, PAGE_SIZE), len, - IOMMU_READ | IOMMU_WRITE); + rounddown(paddr, PAGE_SIZE), len, flag); if (ret) { cnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret); return ret; @@ -2981,6 +3555,84 @@ static void cnss_pci_dump_registers(struct cnss_pci_data *pci_priv) cnss_pci_dump_ce_reg(pci_priv, CNSS_CE_10); } +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) +{ + int ret; + struct cnss_plat_data *plat_priv; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + if (!test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state) || + test_bit(CNSS_MHI_POWERING_OFF, &pci_priv->mhi_state)) + return -EINVAL; + + cnss_auto_resume(&pci_priv->pci_dev->dev); + cnss_pci_dump_misc_reg(pci_priv); + cnss_pci_dump_shadow_reg(pci_priv); + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_TRIGGER_RDDM); + if (ret) { + if (!test_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state) || + test_bit(CNSS_MHI_POWERING_OFF, &pci_priv->mhi_state)) { + cnss_pr_dbg("MHI is not powered on, ignore RDDM failure\n"); + return 0; + } + cnss_fatal_err("Failed to trigger RDDM, err = %d\n", ret); + cnss_pci_dump_registers(pci_priv); + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_DEFAULT); + return ret; + } + + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); + } + + return 0; +} + +static void cnss_pci_add_dump_seg(struct cnss_pci_data *pci_priv, + struct cnss_dump_seg *dump_seg, + enum cnss_fw_dump_type type, int seg_no, + void *va, dma_addr_t dma, size_t size) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct device *dev = &pci_priv->pci_dev->dev; + phys_addr_t pa; + + dump_seg->address = dma; + dump_seg->v_address = va; + dump_seg->size = size; + dump_seg->type = type; + + cnss_pr_dbg("Seg: %x, va: %pK, dma: %pa, size: 0x%zx\n", + seg_no, va, &dma, size); + + if (cnss_va_to_pa(dev, size, va, dma, &pa, DMA_ATTR_FORCE_CONTIGUOUS)) + return; + + cnss_minidump_add_region(plat_priv, type, seg_no, va, pa, size); +} + +static void cnss_pci_remove_dump_seg(struct cnss_pci_data *pci_priv, + struct cnss_dump_seg *dump_seg, + enum cnss_fw_dump_type type, int seg_no, + void *va, dma_addr_t dma, size_t size) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct device *dev = &pci_priv->pci_dev->dev; + phys_addr_t pa; + + cnss_va_to_pa(dev, size, va, dma, &pa, DMA_ATTR_FORCE_CONTIGUOUS); + cnss_minidump_remove_region(plat_priv, type, seg_no, va, pa, size); +} + void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -2990,7 +3642,7 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) plat_priv->ramdump_info_v2.dump_data_vaddr; struct image_info *fw_image, *rddm_image; struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; - int ret, i; + int ret, i, j; if (test_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state)) { cnss_pr_dbg("RAM dump is already collected, skip\n"); @@ -3000,6 +3652,7 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) if (cnss_pci_check_link_status(pci_priv)) return; + cnss_pci_dump_misc_reg(pci_priv); cnss_pci_dump_qdss_reg(pci_priv); ret = mhi_download_rddm_img(pci_priv->mhi_ctrl, in_panic); @@ -3018,13 +3671,10 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) fw_image->entries); for (i = 0; i < fw_image->entries; i++) { - dump_seg->address = fw_image->mhi_buf[i].dma_addr; - dump_seg->v_address = fw_image->mhi_buf[i].buf; - dump_seg->size = fw_image->mhi_buf[i].len; - dump_seg->type = CNSS_FW_IMAGE; - cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n", - i, dump_seg->address, - dump_seg->v_address, dump_seg->size); + cnss_pci_add_dump_seg(pci_priv, dump_seg, CNSS_FW_IMAGE, i, + fw_image->mhi_buf[i].buf, + fw_image->mhi_buf[i].dma_addr, + fw_image->mhi_buf[i].len); dump_seg++; } @@ -3034,31 +3684,28 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) rddm_image->entries); for (i = 0; i < rddm_image->entries; i++) { - dump_seg->address = rddm_image->mhi_buf[i].dma_addr; - dump_seg->v_address = rddm_image->mhi_buf[i].buf; - dump_seg->size = rddm_image->mhi_buf[i].len; - dump_seg->type = CNSS_FW_RDDM; - cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n", - i, dump_seg->address, - dump_seg->v_address, dump_seg->size); + cnss_pci_add_dump_seg(pci_priv, dump_seg, CNSS_FW_RDDM, i, + rddm_image->mhi_buf[i].buf, + rddm_image->mhi_buf[i].dma_addr, + rddm_image->mhi_buf[i].len); dump_seg++; } dump_data->nentries += rddm_image->entries; + mhi_dump_sfr(pci_priv->mhi_ctrl); + cnss_pr_dbg("Collect remote heap dump segment\n"); - for (i = 0; i < plat_priv->fw_mem_seg_len; i++) { + for (i = 0, j = 0; i < plat_priv->fw_mem_seg_len; i++) { if (fw_mem[i].type == CNSS_MEM_TYPE_DDR) { - dump_seg->address = fw_mem[i].pa; - dump_seg->v_address = fw_mem[i].va; - dump_seg->size = fw_mem[i].size; - dump_seg->type = CNSS_FW_REMOTE_HEAP; - cnss_pr_dbg("seg-%d: address 0x%lx, v_address %pK, size 0x%lx\n", - i, dump_seg->address, dump_seg->v_address, - dump_seg->size); + cnss_pci_add_dump_seg(pci_priv, dump_seg, + CNSS_FW_REMOTE_HEAP, j, + fw_mem[i].va, fw_mem[i].pa, + fw_mem[i].size); dump_seg++; dump_data->nentries++; + j++; } } @@ -3072,6 +3719,41 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_dump_seg *dump_seg = + plat_priv->ramdump_info_v2.dump_data_vaddr; + struct image_info *fw_image, *rddm_image; + struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + int i, j; + + fw_image = pci_priv->mhi_ctrl->fbc_image; + rddm_image = pci_priv->mhi_ctrl->rddm_image; + + for (i = 0; i < fw_image->entries; i++) { + cnss_pci_remove_dump_seg(pci_priv, dump_seg, CNSS_FW_IMAGE, i, + fw_image->mhi_buf[i].buf, + fw_image->mhi_buf[i].dma_addr, + fw_image->mhi_buf[i].len); + dump_seg++; + } + + for (i = 0; i < rddm_image->entries; i++) { + cnss_pci_remove_dump_seg(pci_priv, dump_seg, CNSS_FW_RDDM, i, + rddm_image->mhi_buf[i].buf, + rddm_image->mhi_buf[i].dma_addr, + rddm_image->mhi_buf[i].len); + dump_seg++; + } + + for (i = 0, j = 0; i < plat_priv->fw_mem_seg_len; i++) { + if (fw_mem[i].type == CNSS_MEM_TYPE_DDR) { + cnss_pci_remove_dump_seg(pci_priv, dump_seg, + CNSS_FW_REMOTE_HEAP, j, + fw_mem[i].va, fw_mem[i].pa, + fw_mem[i].size); + dump_seg++; + j++; + } + } plat_priv->ramdump_info_v2.dump_data.nentries = 0; plat_priv->ramdump_info_v2.dump_data_valid = false; @@ -3103,6 +3785,8 @@ static char *cnss_mhi_notify_status_to_str(enum MHI_CB status) return "SYS_ERROR"; case MHI_CB_FATAL_ERROR: return "FATAL_ERROR"; + case MHI_CB_EE_MISSION_MODE: + return "MISSION_MODE"; default: return "UNKNOWN"; } @@ -3153,6 +3837,7 @@ static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, void *priv, switch (reason) { case MHI_CB_IDLE: + case MHI_CB_EE_MISSION_MODE: return; case MHI_CB_FATAL_ERROR: set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); @@ -3211,7 +3896,7 @@ static int cnss_pci_get_mhi_msi(struct cnss_pci_data *pci_priv) return 0; } -static void cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv) +static int cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; struct mhi_controller *mhi_ctrl = pci_priv->mhi_ctrl; @@ -3227,14 +3912,25 @@ static void cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv) plat_priv->device_version.major_version, plat_priv->device_version.minor_version); - if (pci_priv->device_id == QCA6390_DEVICE_ID && - plat_priv->device_version.major_version >= FW_V2_NUMBER) { + switch (pci_priv->device_id) { + case QCA6390_DEVICE_ID: + if (plat_priv->device_version.major_version < FW_V2_NUMBER) { + cnss_pr_dbg("Device ID:version (0x%lx:%d) is not supported\n", + pci_priv->device_id, + plat_priv->device_version.major_version); + return -EINVAL; + } scnprintf(plat_priv->firmware_name, sizeof(plat_priv->firmware_name), FW_V2_FILE_NAME); mhi_ctrl->fw_image = plat_priv->firmware_name; + break; + default: + break; } cnss_pr_dbg("Firmware name is %s\n", mhi_ctrl->fw_image); + + return 0; } static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) @@ -3268,8 +3964,8 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) ret = cnss_pci_get_mhi_msi(pci_priv); if (ret) { - cnss_pr_err("Failed to get MSI for MHI\n"); - return ret; + cnss_pr_err("Failed to get MSI for MHI, err = %d\n", ret); + goto free_mhi_ctrl; } if (pci_priv->smmu_s1_enable) { @@ -3299,12 +3995,25 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) ret = of_register_mhi_controller(mhi_ctrl); if (ret) { cnss_pr_err("Failed to register to MHI bus, err = %d\n", ret); - return ret; + goto destroy_ipc; } - cnss_pci_update_fw_name(pci_priv); + ret = cnss_pci_update_fw_name(pci_priv); + if (ret) + goto unreg_mhi; return 0; + +unreg_mhi: + mhi_unregister_mhi_controller(mhi_ctrl); +destroy_ipc: + if (mhi_ctrl->log_buf) + ipc_log_context_destroy(mhi_ctrl->log_buf); + kfree(mhi_ctrl->irq); +free_mhi_ctrl: + mhi_free_controller(mhi_ctrl); + + return ret; } static void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv) @@ -3312,8 +4021,35 @@ static void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv) struct mhi_controller *mhi_ctrl = pci_priv->mhi_ctrl; mhi_unregister_mhi_controller(mhi_ctrl); - ipc_log_context_destroy(mhi_ctrl->log_buf); + if (mhi_ctrl->log_buf) + ipc_log_context_destroy(mhi_ctrl->log_buf); kfree(mhi_ctrl->irq); + mhi_free_controller(mhi_ctrl); +} + +static void cnss_pci_config_regs(struct cnss_pci_data *pci_priv) +{ + switch (pci_priv->device_id) { + case QCA6390_DEVICE_ID: + pci_priv->wcss_reg = wcss_reg_access_seq; + pci_priv->wcss_reg_size = WCSS_REG_SIZE; + pci_priv->pcie_reg = pcie_reg_access_seq; + pci_priv->pcie_reg_size = PCIE_REG_SIZE; + pci_priv->wlaon_reg = wlaon_reg_access_seq; + pci_priv->wlaon_reg_size = WLAON_REG_SIZE; + + /* Configure WDOG register with specific value so that we can + * know if HW is in the process of WDOG reset recovery or not + * when reading the registers. + */ + cnss_pci_reg_write + (pci_priv, + QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG, + QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG_VAL); + break; + default: + return; + } } static int cnss_pci_probe(struct pci_dev *pci_dev, @@ -3322,12 +4058,12 @@ 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; cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n", id->vendor, pci_dev->device); - pci_priv = devm_kzalloc(&pci_dev->dev, sizeof(*pci_priv), - GFP_KERNEL); + pci_priv = devm_kzalloc(dev, sizeof(*pci_priv), GFP_KERNEL); if (!pci_priv) { ret = -ENOMEM; goto out; @@ -3344,6 +4080,14 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, plat_priv->bus_priv = pci_priv; snprintf(plat_priv->firmware_name, sizeof(plat_priv->firmware_name), DEFAULT_FW_FILE_NAME); + mutex_init(&pci_priv->bus_lock); + + ret = of_reserved_mem_device_init(dev); + if (ret) + cnss_pr_err("Failed to init reserved mem device, err = %d\n", + ret); + if (dev->cma_area) + cnss_pr_dbg("CMA area is %s\n", cma_get_name(dev->cma_area)); ret = cnss_register_subsys(plat_priv); if (ret) @@ -3397,6 +4141,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, goto disable_bus; } cnss_pci_get_link_status(pci_priv); + + cnss_pci_config_regs(pci_priv); + if (EMULATION_HW) break; ret = cnss_suspend_pci_link(pci_priv); @@ -3457,9 +4204,14 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) cnss_pci_disable_bus(pci_priv); cnss_dereg_pci_event(pci_priv); cnss_pci_deinit_smmu(pci_priv); - cnss_unregister_ramdump(plat_priv); - cnss_unregister_subsys(plat_priv); - plat_priv->bus_priv = NULL; + if (plat_priv) { + cnss_unregister_ramdump(plat_priv); + cnss_unregister_subsys(plat_priv); + plat_priv->bus_priv = NULL; + } else { + cnss_pr_err("Plat_priv is null, Unable to unregister ramdump,subsys\n"); + } + } static const struct pci_device_id cnss_pci_id_table[] = { @@ -3515,7 +4267,16 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv) goto out; } + if (!plat_priv->bus_priv) { + cnss_pr_err("Failed to probe PCI driver\n"); + ret = -ENODEV; + goto unreg_pci; + } + return 0; + +unreg_pci: + pci_unregister_driver(&cnss_pci_driver); out: return ret; } diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 769ba6651bf2..31a91b4ce42e 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -16,6 +16,7 @@ enum cnss_mhi_state { CNSS_MHI_INIT, CNSS_MHI_DEINIT, CNSS_MHI_POWER_ON, + CNSS_MHI_POWERING_OFF, CNSS_MHI_POWER_OFF, CNSS_MHI_FORCE_POWER_OFF, CNSS_MHI_SUSPEND, @@ -53,6 +54,12 @@ struct cnss_pci_debug_reg { u32 val; }; +struct cnss_misc_reg { + u8 wr; + u32 offset; + u32 val; +}; + struct cnss_pci_data { struct pci_dev *pci_dev; struct cnss_plat_data *plat_priv; @@ -65,6 +72,8 @@ struct cnss_pci_data { struct pci_saved_state *saved_state; struct pci_saved_state *default_state; struct msm_pcie_register_event msm_pci_event; + struct notifier_block pm_qos_nb; + u8 runtime_pm_prevented; atomic_t auto_suspended; atomic_t drv_connected; u8 drv_connected_last; @@ -86,7 +95,14 @@ struct cnss_pci_data { struct timer_list dev_rddm_timer; struct delayed_work time_sync_work; u8 disable_pc; + struct mutex bus_lock; /* mutex for suspend and resume bus */ struct cnss_pci_debug_reg *debug_reg; + struct cnss_misc_reg *wcss_reg; + u32 wcss_reg_size; + struct cnss_misc_reg *pcie_reg; + u32 pcie_reg_size; + struct cnss_misc_reg *wlaon_reg; + u32 wlaon_reg_size; }; static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data) @@ -161,6 +177,8 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic); void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv); int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv); +int cnss_pci_qmi_send_get(struct cnss_pci_data *pci_priv); +int cnss_pci_qmi_send_put(struct cnss_pci_data *pci_priv); void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv); int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv); int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv); @@ -183,5 +201,12 @@ void cnss_pci_pm_runtime_put_noidle(struct cnss_pci_data *pci_priv); void cnss_pci_pm_runtime_mark_last_busy(struct cnss_pci_data *pci_priv); int cnss_pci_update_status(struct cnss_pci_data *pci_priv, enum cnss_driver_status status); +int cnss_pcie_is_device_down(struct cnss_pci_data *pci_priv); +int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv); +int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv); +int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset, + u32 *val); +int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset, + u32 val); #endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index 89e12659d9ad..5086b7e9b0c1 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -92,6 +92,8 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) req->qdss_trace_save_enable = 1; req->qdss_trace_free_enable_valid = 1; req->qdss_trace_free_enable = 1; + req->respond_get_info_enable_valid = 1; + req->respond_get_info_enable = 1; ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, wlfw_ind_register_resp_msg_v01_ei, resp); @@ -399,6 +401,9 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) resp->fw_version_info.fw_build_timestamp, QMI_WLFW_MAX_TIMESTAMP_LEN + 1); } + if (resp->fw_build_id_valid) + strlcpy(plat_priv->fw_build_id, resp->fw_build_id, + QMI_WLFW_MAX_BUILD_ID_LEN + 1); if (resp->voltage_mv_valid) { plat_priv->cpr_info.voltage = resp->voltage_mv; cnss_pr_dbg("Voltage for CPR: %dmV\n", @@ -413,12 +418,13 @@ int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) if (resp->otp_version_valid) plat_priv->otp_version = resp->otp_version; - cnss_pr_dbg("Target capability: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s, otp_version: 0x%x\n", + cnss_pr_dbg("Target capability: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s, fw_build_id: %s, otp_version: 0x%x\n", plat_priv->chip_info.chip_id, plat_priv->chip_info.chip_family, plat_priv->board_info.board_id, plat_priv->soc_info.soc_id, plat_priv->fw_version_info.fw_version, plat_priv->fw_version_info.fw_build_timestamp, + plat_priv->fw_build_id, plat_priv->otp_version); kfree(req); @@ -432,6 +438,56 @@ out: return ret; } +static int cnss_get_bdf_file_name(struct cnss_plat_data *plat_priv, + u32 bdf_type, char *filename, + u32 filename_len) +{ + int ret = 0; + + switch (bdf_type) { + case CNSS_BDF_ELF: + if (plat_priv->board_info.board_id == 0xFF) + snprintf(filename, filename_len, ELF_BDF_FILE_NAME); + else if (plat_priv->board_info.board_id < 0xFF) + snprintf(filename, filename_len, + ELF_BDF_FILE_NAME_PREFIX "%02x", + plat_priv->board_info.board_id); + else + snprintf(filename, filename_len, + BDF_FILE_NAME_PREFIX "%02x.e%02x", + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); + break; + case CNSS_BDF_BIN: + if (plat_priv->board_info.board_id == 0xFF) + snprintf(filename, filename_len, BIN_BDF_FILE_NAME); + else if (plat_priv->board_info.board_id < 0xFF) + snprintf(filename, filename_len, + BIN_BDF_FILE_NAME_PREFIX "%02x", + plat_priv->board_info.board_id); + else + snprintf(filename, filename_len, + BDF_FILE_NAME_PREFIX "%02x.b%02x", + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); + break; + case CNSS_BDF_REGDB: + snprintf(filename, filename_len, REGDB_FILE_NAME); + break; + case CNSS_BDF_DUMMY: + cnss_pr_dbg("CNSS_BDF_DUMMY is set, sending dummy BDF\n"); + snprintf(filename, filename_len, DUMMY_BDF_FILE_NAME); + ret = MAX_BDF_FILE_NAME; + break; + default: + cnss_pr_err("Invalid BDF type: %d\n", + plat_priv->ctrl_params.bdf_type); + ret = -EINVAL; + break; + } + return ret; +} + int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, u32 bdf_type) { @@ -439,7 +495,7 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, struct wlfw_bdf_download_resp_msg_v01 *resp; struct qmi_txn txn; char filename[MAX_BDF_FILE_NAME]; - const struct firmware *fw_entry; + const struct firmware *fw_entry = NULL; const u8 *temp; unsigned int remaining; int ret = 0; @@ -457,46 +513,13 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, return -ENOMEM; } - switch (bdf_type) { - case CNSS_BDF_ELF: - if (plat_priv->board_info.board_id == 0xFF) - snprintf(filename, sizeof(filename), ELF_BDF_FILE_NAME); - else if (plat_priv->board_info.board_id < 0xFF) - snprintf(filename, sizeof(filename), - ELF_BDF_FILE_NAME_PREFIX "%02x", - plat_priv->board_info.board_id); - else - snprintf(filename, sizeof(filename), - BDF_FILE_NAME_PREFIX "%02x.e%02x", - plat_priv->board_info.board_id >> 8 & 0xFF, - plat_priv->board_info.board_id & 0xFF); - break; - case CNSS_BDF_BIN: - if (plat_priv->board_info.board_id == 0xFF) - snprintf(filename, sizeof(filename), BIN_BDF_FILE_NAME); - else if (plat_priv->board_info.board_id < 0xFF) - snprintf(filename, sizeof(filename), - BIN_BDF_FILE_NAME_PREFIX "%02x", - plat_priv->board_info.board_id); - else - snprintf(filename, sizeof(filename), - BDF_FILE_NAME_PREFIX "%02x.b%02x", - plat_priv->board_info.board_id >> 8 & 0xFF, - plat_priv->board_info.board_id & 0xFF); - break; - case CNSS_BDF_REGDB: - snprintf(filename, sizeof(filename), REGDB_FILE_NAME); - break; - case CNSS_BDF_DUMMY: - cnss_pr_dbg("CNSS_BDF_DUMMY is set, sending dummy BDF\n"); - snprintf(filename, sizeof(filename), DUMMY_BDF_FILE_NAME); + ret = cnss_get_bdf_file_name(plat_priv, bdf_type, + filename, sizeof(filename)); + if (ret > 0) { temp = DUMMY_BDF_FILE_NAME; remaining = MAX_BDF_FILE_NAME; goto bypass_bdf; - default: - cnss_pr_err("Invalid BDF type: %d\n", - plat_priv->ctrl_params.bdf_type); - ret = -EINVAL; + } else if (ret < 0) { goto err_req_fw; } @@ -522,7 +545,7 @@ bypass_bdf: req->data_valid = 1; req->end_valid = 1; req->bdf_type_valid = 1; - req->bdf_type = plat_priv->ctrl_params.bdf_type; + req->bdf_type = bdf_type; if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; @@ -572,12 +595,15 @@ bypass_bdf: req->seg_id++; } + if (bdf_type != CNSS_BDF_DUMMY) + release_firmware(fw_entry); + kfree(req); kfree(resp); return 0; err_send: - if (plat_priv->ctrl_params.bdf_type != CNSS_BDF_DUMMY) + if (bdf_type != CNSS_BDF_DUMMY) release_firmware(fw_entry); err_req_fw: if (bdf_type != CNSS_BDF_REGDB) @@ -758,12 +784,12 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, u32 i; int ret = 0; - cnss_pr_dbg("Sending WLAN config message, state: 0x%lx\n", - plat_priv->driver_state); - if (!plat_priv) return -ENODEV; + cnss_pr_dbg("Sending WLAN config message, state: 0x%lx\n", + plat_priv->driver_state); + req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; @@ -1441,6 +1467,77 @@ out: return ret; } +int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type, + void *cmd, int cmd_len) +{ + struct wlfw_get_info_req_msg_v01 *req; + struct wlfw_get_info_resp_msg_v01 *resp; + struct qmi_txn txn; + int ret = 0; + + cnss_pr_vdbg("Sending get info message, type: %d, cmd length: %d, state: 0x%lx\n", + type, cmd_len, plat_priv->driver_state); + + if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01) + return -EINVAL; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + + req->type = type; + req->data_len = cmd_len; + memcpy(req->data, cmd, req->data_len); + + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, + wlfw_get_info_resp_msg_v01_ei, resp); + if (ret < 0) { + cnss_pr_err("Failed to initialize txn for get info request, err: %d\n", + ret); + goto out; + } + + ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn, + QMI_WLFW_GET_INFO_REQ_V01, + WLFW_GET_INFO_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_get_info_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + cnss_pr_err("Failed to send get info request, err: %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF); + if (ret < 0) { + cnss_pr_err("Failed to wait for response of get info request, err: %d\n", + ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + cnss_pr_err("Get info request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = -resp->resp.result; + goto out; + } + + kfree(req); + kfree(resp); + return 0; + +out: + kfree(req); + kfree(resp); + return ret; +} + unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv) { cnss_pr_dbg("QMI timeout is %u ms\n", QMI_WLFW_TIMEOUT_MS); @@ -1470,6 +1567,9 @@ static void cnss_wlfw_request_mem_ind_cb(struct qmi_handle *qmi_wlfw, ind_msg->mem_seg[i].size, ind_msg->mem_seg[i].type); plat_priv->fw_mem[i].type = ind_msg->mem_seg[i].type; plat_priv->fw_mem[i].size = ind_msg->mem_seg[i].size; + if (plat_priv->fw_mem[i].type == CNSS_MEM_TYPE_DDR) + plat_priv->fw_mem[i].attrs |= + DMA_ATTR_FORCE_CONTIGUOUS; } cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_REQUEST_MEM, @@ -1700,6 +1800,31 @@ static void cnss_wlfw_qdss_trace_free_ind_cb(struct qmi_handle *qmi_wlfw, 0, NULL); } +static void cnss_wlfw_respond_get_info_ind_cb(struct qmi_handle *qmi_wlfw, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *data) +{ + struct cnss_plat_data *plat_priv = + container_of(qmi_wlfw, struct cnss_plat_data, qmi_wlfw); + const struct wlfw_respond_get_info_ind_msg_v01 *ind_msg = data; + + cnss_pr_vdbg("Received QMI WLFW respond get info indication\n"); + + if (!txn) { + cnss_pr_err("Spurious indication\n"); + return; + } + + cnss_pr_vdbg("Extract message with event length: %d, type: %d, is last: %d, seq no: %d\n", + ind_msg->data_len, ind_msg->type, + ind_msg->is_last, ind_msg->seq_no); + + if (plat_priv->get_info_cb_ctx && plat_priv->get_info_cb) + plat_priv->get_info_cb(plat_priv->get_info_cb_ctx, + (void *)ind_msg->data, + ind_msg->data_len); +} static struct qmi_msg_handler qmi_wlfw_msg_handlers[] = { { .type = QMI_INDICATION, @@ -1768,6 +1893,14 @@ static struct qmi_msg_handler qmi_wlfw_msg_handlers[] = { sizeof(struct wlfw_qdss_trace_free_ind_msg_v01), .fn = cnss_wlfw_qdss_trace_free_ind_cb }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_RESPOND_GET_INFO_IND_V01, + .ei = wlfw_respond_get_info_ind_msg_v01_ei, + .decoded_size = + sizeof(struct wlfw_respond_get_info_ind_msg_v01), + .fn = cnss_wlfw_respond_get_info_ind_cb + }, {} }; diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h index a06466097b20..fc2a2c677c1e 100644 --- a/drivers/net/wireless/cnss2/qmi.h +++ b/drivers/net/wireless/cnss2/qmi.h @@ -59,6 +59,8 @@ int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, int cnss_wlfw_antenna_switch_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_antenna_grant_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_dynamic_feature_mask_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type, + void *cmd, int cmd_len); int cnss_register_coex_service(struct cnss_plat_data *plat_priv); void cnss_unregister_coex_service(struct cnss_plat_data *plat_priv); int coex_antenna_switch_to_wlan_send_sync_msg(struct cnss_plat_data *plat_priv); diff --git a/drivers/net/wireless/cnss2/reg.h b/drivers/net/wireless/cnss2/reg.h new file mode 100644 index 000000000000..4052de4b78c5 --- /dev/null +++ b/drivers/net/wireless/cnss2/reg.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */ + +#ifndef _CNSS_REG_H +#define _CNSS_REG_H + +#define QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET 0x310C + +#define QCA6390_CE_SRC_RING_REG_BASE 0xA00000 +#define QCA6390_CE_DST_RING_REG_BASE 0xA01000 +#define QCA6390_CE_COMMON_REG_BASE 0xA18000 + +#define QCA6390_CE_SRC_RING_BASE_LSB_OFFSET 0x0 +#define QCA6390_CE_SRC_RING_BASE_MSB_OFFSET 0x4 +#define QCA6390_CE_SRC_RING_ID_OFFSET 0x8 +#define QCA6390_CE_SRC_RING_MISC_OFFSET 0x10 +#define QCA6390_CE_SRC_CTRL_OFFSET 0x58 +#define QCA6390_CE_SRC_R0_CE_CH_SRC_IS_OFFSET 0x5C +#define QCA6390_CE_SRC_RING_HP_OFFSET 0x400 +#define QCA6390_CE_SRC_RING_TP_OFFSET 0x404 + +#define QCA6390_CE_DEST_RING_BASE_LSB_OFFSET 0x0 +#define QCA6390_CE_DEST_RING_BASE_MSB_OFFSET 0x4 +#define QCA6390_CE_DEST_RING_ID_OFFSET 0x8 +#define QCA6390_CE_DEST_RING_MISC_OFFSET 0x10 +#define QCA6390_CE_DEST_CTRL_OFFSET 0xB0 +#define QCA6390_CE_CH_DST_IS_OFFSET 0xB4 +#define QCA6390_CE_CH_DEST_CTRL2_OFFSET 0xB8 +#define QCA6390_CE_DEST_RING_HP_OFFSET 0x400 +#define QCA6390_CE_DEST_RING_TP_OFFSET 0x404 + +#define QCA6390_CE_STATUS_RING_BASE_LSB_OFFSET 0x58 +#define QCA6390_CE_STATUS_RING_BASE_MSB_OFFSET 0x5C +#define QCA6390_CE_STATUS_RING_ID_OFFSET 0x60 +#define QCA6390_CE_STATUS_RING_MISC_OFFSET 0x68 +#define QCA6390_CE_STATUS_RING_HP_OFFSET 0x408 +#define QCA6390_CE_STATUS_RING_TP_OFFSET 0x40C + +#define QCA6390_CE_COMMON_GXI_ERR_INTS 0x14 +#define QCA6390_CE_COMMON_GXI_ERR_STATS 0x18 +#define QCA6390_CE_COMMON_GXI_WDOG_STATUS 0x2C +#define QCA6390_CE_COMMON_TARGET_IE_0 0x48 +#define QCA6390_CE_COMMON_TARGET_IE_1 0x4C + +#define QCA6390_CE_REG_INTERVAL 0x2000 + +#define SHADOW_REG_COUNT 36 +#define QCA6390_PCIE_SHADOW_REG_VALUE_0 0x8FC +#define QCA6390_PCIE_SHADOW_REG_VALUE_34 0x984 +#define QCA6390_PCIE_SHADOW_REG_VALUE_35 0x988 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL3 0x1F80118 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL4 0x1F8011C +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL5 0x1F80120 + +#define SHADOW_REG_INTER_COUNT 43 +#define QCA6390_PCIE_SHADOW_REG_INTER_0 0x1E05000 +#define QCA6390_PCIE_SHADOW_REG_HUNG 0x1E050A8 + +#define QDSS_APB_DEC_CSR_BASE 0x1C01000 + +#define QDSS_APB_DEC_CSR_ETRIRQCTRL_OFFSET 0x6C +#define QDSS_APB_DEC_CSR_PRESERVEETF_OFFSET 0x70 +#define QDSS_APB_DEC_CSR_PRESERVEETR0_OFFSET 0x74 +#define QDSS_APB_DEC_CSR_PRESERVEETR1_OFFSET 0x78 + +#define MAX_UNWINDOWED_ADDRESS 0x80000 +#define WINDOW_ENABLE_BIT 0x40000000 +#define WINDOW_SHIFT 19 +#define WINDOW_VALUE_MASK 0x3F +#define WINDOW_START MAX_UNWINDOWED_ADDRESS +#define WINDOW_RANGE_MASK 0x7FFFF + +#define QCA6390_TIME_SYNC_ENABLE 0x80000000 +#define QCA6390_TIME_SYNC_CLEAR 0x0 + +#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 +#define QCA6390_PCIE_PCIE_WCSS_STATUS_FOR_DEBUG_LOW_PCIE_LOCAL_REG 0x01E03140 +#define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG 0x1E04054 +#define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_STATUS_SOC_PCIE_REG 0x1E04058 +#define QCA6390_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x1E05090 +#define QCA6390_PCIE_PCIE_PARF_LTSSM 0x01E081B0 +#define QCA6390_PCIE_PCIE_PARF_PM_STTS 0x01E08024 +#define QCA6390_PCIE_PCIE_PARF_PM_STTS_1 0x01E08028 +#define QCA6390_PCIE_PCIE_PARF_INT_STATUS 0x01E08220 +#define QCA6390_PCIE_PCIE_INT_ALL_STATUS 0x01E08224 +#define QCA6390_PCIE_PCIE_INT_ALL_MASK 0x01E0822C +#define QCA6390_PCIE_PCIE_PARF_BDF_TO_SID_CFG 0x01E0AC00 +#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_4 0x01E08530 +#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_3 0x01E0852c +#define QCA6390_PCIE_PCIE_PARF_MHI_CLOCK_RESET_CTRL 0x01E08174 +#define QCA6390_PCIE_PCIE_PARF_MHI_BASE_ADDR_LOWER 0x01E08178 +#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS 0x01E084D0 +#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_CFG 0x01E084d4 +#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2 0x01E0ec88 +#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1SUB 0x01E0ec08 +#define QCA6390_PCIE_PCIE_CORE_CONFIG 0x01E08640 +#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L2 0x01E0EC04 +#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1 0x01E0EC0C +#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1 0x01E0EC84 +#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_HIGH 0x01E030C8 +#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_LOW 0x01E030CC +#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_HIGH 0x01E0313C +#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_LOW 0x01E03140 + +#define QCA6390_GCC_DEBUG_CLK_CTL 0x001E4025C + +#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_PLL_MODE 0x00D00200 +#define QCA6390_WCSS_WFSS_PMM_WFSS_PMM_R0_PMM_CTRL 0x00B60164 +#define QCA6390_WCSS_PMM_TOP_PMU_CX_CSR 0x00B70080 +#define QCA6390_WCSS_PMM_TOP_AON_INT_RAW_STAT 0x00B700E0 +#define QCA6390_WCSS_PMM_TOP_AON_INT_EN 0x00B700D0 +#define QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_STS 0x00B70020 +#define QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_CTL 0x00B7001C +#define QCA6390_WCSS_PMM_TOP_TESTBUS_STS 0x00B70028 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG 0x00DB0008 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG_MSK 0x20 +#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL 0x00D02000 +#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE 0x00D02004 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_STS 0x00DB000C +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_CTL 0x00DB0030 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_0 0x00DB0400 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_9 0x00DB0424 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS0 0x00D90380 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS1 0x00D90384 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS2 0x00D90388 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS3 0x00D9038C +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS4 0x00D90390 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS5 0x00D90394 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS6 0x00D90398 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE0 0x00D90100 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE1 0x00D90104 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE2 0x00D90108 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE3 0x00D9010C +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE4 0x00D90110 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE5 0x00D90114 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE6 0x00D90118 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING0 0x00D90500 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING1 0x00D90504 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING2 0x00D90508 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING3 0x00D9050C +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING4 0x00D90510 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING5 0x00D90514 +#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING6 0x00D90518 +#define QCA6390_WCSS_CC_WCSS_UMAC_NOC_CBCR 0x00C3029C +#define QCA6390_WCSS_CC_WCSS_UMAC_AHB_CBCR 0x00C302BC +#define QCA6390_WCSS_CC_WCSS_UMAC_GDSCR 0x00C30298 +#define QCA6390_WCSS_CC_WCSS_WLAN1_GDSCR 0x00C300C4 +#define QCA6390_WCSS_CC_WCSS_WLAN2_GDSCR 0x00C30138 +#define QCA6390_WCSS_PMM_TOP_PMM_INT_CLR 0x00B70168 +#define QCA6390_WCSS_PMM_TOP_AON_INT_STICKY_EN 0x00B700D8 + +#define QCA6390_TLMM_GPIO_IN_OUT57 0x01839004 +#define QCA6390_TLMM_GPIO_INTR_CFG57 0x01839008 +#define QCA6390_TLMM_GPIO_INTR_STATUS57 0x0183900C +#define QCA6390_TLMM_GPIO_IN_OUT59 0x0183b004 +#define QCA6390_TLMM_GPIO_INTR_CFG59 0x0183b008 +#define QCA6390_TLMM_GPIO_INTR_STATUS59 0x0183b00C + +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_STATUS_REG2 0x00B6017C +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_STATUS_REG2 0x00B60190 +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN2_CFG_REG1 0x00B6018C +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN1_CFG_REG1 0x00B60178 +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_APS_STATUS_REG1 0x00B600B0 +#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_APS_STATUS_REG1 0x00B60044 + +#define QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_HI_REG 0x01F806C4 +#define QCA6390_WLAON_SOC_POWER_CTRL 0x01F80000 +#define QCA6390_WLAON_PCIE_PWR_CTRL_REG 0x01F806BC +#define QCA6390_WLAON_WCSSAON_PCIE_SR_STATUS_LO_REG 0x1F806C8 +#define QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_HI_REG 0x1F806CC +#define QCA6390_WLAON_WCSS_TCSR_PMM_SR_STATUS_LO_REG 0x1F806D0 +#define QCA6390_WLAON_SOC_PWR_WDG_BARK_THRSHD 0x1F80004 +#define QCA6390_WLAON_SOC_PWR_WDG_BITE_THRSHD 0x1F80008 +#define QCA6390_WLAON_SW_COLD_RESET 0x1F8000C +#define QCA6390_WLAON_RFA_MEM_SLP_NRET_N_OVERRIDE 0x1F8001C +#define QCA6390_WLAON_GDSC_DELAY_SETTING 0x1F80024 +#define QCA6390_WLAON_GDSC_DELAY_SETTING2 0x1F80028 +#define QCA6390_WLAON_WL_PWR_STATUS_REG 0x1F8002C +#define QCA6390_WLAON_WL_AON_DBG_CFG_REG 0x1F80030 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL1 0x1F80100 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL6 0x1F80108 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL7 0x1F8010C +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL3 0x1F80118 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL4 0x1F8011C +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL5 0x1F80120 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL8 0x1F801F0 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL2 0x1F801F4 +#define QCA6390_WLAON_GLOBAL_COUNTER_CTRL9 0x1F801F8 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL1 0x1F80200 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL2 0x1F80204 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL3 0x1F80208 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL4 0x1F8020C +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL5 0x1F80210 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL6 0x1F80214 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL7 0x1F80218 +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL8 0x1F8021C +#define QCA6390_WLAON_RTC_CLK_CAL_CTRL9 0x1F80220 +#define QCA6390_WLAON_WCSSAON_CONFIG_REG 0x1F80300 +#define QCA6390_WLAON_WLAN_OEM_DEBUG_REG 0x1F80304 +#define QCA6390_WLAON_WLAN_RAM_DUMP_REG 0x1F80308 +#define QCA6390_WLAON_QDSS_WCSS_REG 0x1F8030C +#define QCA6390_WLAON_QDSS_WCSS_ACK 0x1F80310 +#define QCA6390_WLAON_WL_CLK_CNTL_KDF_REG 0x1F80314 +#define QCA6390_WLAON_WL_CLK_CNTL_PMU_HFRC_REG 0x1F80318 +#define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C +#define QCA6390_WLAON_DLY_CONFIG 0x1F80400 +#define QCA6390_WLAON_WLAON_Q6_IRQ_REG 0x1F80404 +#define QCA6390_WLAON_PCIE_INTF_SW_CFG_REG 0x1F80408 +#define QCA6390_WLAON_PCIE_INTF_STICKY_SW_CFG_REG 0x1F8040C +#define QCA6390_WLAON_PCIE_INTF_PHY_SW_CFG_REG 0x1F80410 +#define QCA6390_WLAON_PCIE_INTF_PHY_NOCSR_SW_CFG_REG 0x1F80414 +#define QCA6390_WLAON_Q6_COOKIE_BIT 0x1F80500 +#define QCA6390_WLAON_WARM_SW_ENTRY 0x1F80504 +#define QCA6390_WLAON_RESET_DBG_SW_ENTRY 0x1F80508 +#define QCA6390_WLAON_WL_PMUNOC_CFG_REG 0x1F8050C +#define QCA6390_WLAON_RESET_CAUSE_CFG_REG 0x1F80510 +#define QCA6390_WLAON_SOC_WCSSAON_WAKEUP_IRQ_7_EN_REG 0x1F80514 +#define QCA6390_WLAON_DEBUG 0x1F80600 +#define QCA6390_WLAON_SOC_PARAMETERS 0x1F80604 +#define QCA6390_WLAON_WLPM_SIGNAL 0x1F80608 +#define QCA6390_WLAON_SOC_RESET_CAUSE_REG 0x1F8060C +#define QCA6390_WLAON_WAKEUP_PCIE_SOC_REG 0x1F80610 +#define QCA6390_WLAON_PBL_STACK_CANARY 0x1F80614 +#define QCA6390_WLAON_MEM_TOT_NUM_GRP_REG 0x1F80618 +#define QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP0_REG 0x1F8061C +#define QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP1_REG 0x1F80620 +#define QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP2_REG 0x1F80624 +#define QCA6390_WLAON_MEM_TOT_BANKS_IN_GRP3_REG 0x1F80628 +#define QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP0_REG 0x1F8062C +#define QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP1_REG 0x1F80630 +#define QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP2_REG 0x1F80634 +#define QCA6390_WLAON_MEM_TOT_SIZE_IN_GRP3_REG 0x1F80638 +#define QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP0_REG 0x1F8063C +#define QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP1_REG 0x1F80640 +#define QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP2_REG 0x1F80644 +#define QCA6390_WLAON_MEM_SLP_NRET_OVERRIDE_GRP3_REG 0x1F80648 +#define QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP0_REG 0x1F8064C +#define QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP1_REG 0x1F80650 +#define QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP2_REG 0x1F80654 +#define QCA6390_WLAON_MEM_SLP_RET_OVERRIDE_GRP3_REG 0x1F80658 +#define QCA6390_WLAON_MEM_CNT_SEL_REG 0x1F8065C +#define QCA6390_WLAON_MEM_NO_EXTBHS_REG 0x1F80660 +#define QCA6390_WLAON_MEM_DEBUG_REG 0x1F80664 +#define QCA6390_WLAON_MEM_DEBUG_BUS_REG 0x1F80668 +#define QCA6390_WLAON_MEM_REDUN_CFG_REG 0x1F8066C +#define QCA6390_WLAON_WL_AON_SPARE2 0x1F80670 +#define QCA6390_WLAON_VSEL_CFG_FOR_WL_RET_DISABLE_REG 0x1F80680 +#define QCA6390_WLAON_BTFM_WLAN_IPC_STATUS_REG 0x1F80690 +#define QCA6390_WLAON_MPM_COUNTER_CHICKEN_BITS 0x1F806A0 +#define QCA6390_WLAON_WLPM_CHICKEN_BITS 0x1F806A4 +#define QCA6390_WLAON_PCIE_PHY_PWR_REG 0x1F806A8 +#define QCA6390_WLAON_WL_CLK_CNTL_PMU_LPO2M_REG 0x1F806AC +#define QCA6390_WLAON_WL_SS_ROOT_CLK_SWITCH_REG 0x1F806B0 +#define QCA6390_WLAON_POWERCTRL_PMU_REG 0x1F806B4 +#define QCA6390_WLAON_POWERCTRL_MEM_REG 0x1F806B8 +#define QCA6390_WLAON_SOC_PWR_PROFILE_REG 0x1F806C0 +#define QCA6390_WLAON_MEM_SVS_CFG_REG 0x1F806D4 +#define QCA6390_WLAON_CMN_AON_MISC_REG 0x1F806D8 +#define QCA6390_WLAON_INTR_STATUS 0x1F80700 + +#define QCA6390_SYSPM_SYSPM_PWR_STATUS 0x1F82000 +#define QCA6390_SYSPM_DBG_BTFM_AON_REG 0x1F82004 +#define QCA6390_SYSPM_DBG_BUS_SEL_REG 0x1F82008 +#define QCA6390_SYSPM_WCSSAON_SR_STATUS 0x1F8200C + +#endif diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c index 03a418ed5b74..682a20b88950 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c @@ -703,6 +703,24 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_ind_register_req_msg_v01, qdss_trace_free_enable), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1F, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + respond_get_info_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1F, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + respond_get_info_enable), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -3608,3 +3626,143 @@ struct qmi_elem_info wlfw_wfc_call_status_resp_msg_v01_ei[] = { }, }; +struct qmi_elem_info wlfw_get_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wlfw_get_info_req_msg_v01, + type), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_get_info_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_get_info_req_msg_v01, + data), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info wlfw_get_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_get_info_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + type_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + type), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + is_last_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + is_last), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + seq_no_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_respond_get_info_ind_msg_v01, + seq_no), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h index dacdfdb2a471..0e18d8000c3f 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -13,6 +13,7 @@ #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 #define QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01 0x0044 +#define QMI_WLFW_GET_INFO_REQ_V01 0x004A #define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A #define QMI_WLFW_CAL_DONE_IND_V01 0x003E #define QMI_WLFW_WFC_CALL_STATUS_RESP_V01 0x0049 @@ -23,6 +24,7 @@ #define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038 #define QMI_WLFW_ANTENNA_GRANT_RESP_V01 0x0048 #define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026 +#define QMI_WLFW_RESPOND_GET_INFO_IND_V01 0x004B #define QMI_WLFW_M3_INFO_RESP_V01 0x003C #define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029 #define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027 @@ -49,6 +51,7 @@ #define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031 #define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022 #define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLFW_GET_INFO_RESP_V01 0x004A #define QMI_WLFW_QDSS_TRACE_MODE_RESP_V01 0x0045 #define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C #define QMI_WLFW_FW_READY_IND_V01 0x0021 @@ -272,9 +275,11 @@ struct wlfw_ind_register_req_msg_v01 { u8 qdss_trace_save_enable; u8 qdss_trace_free_enable_valid; u8 qdss_trace_free_enable; + u8 respond_get_info_enable_valid; + u8 respond_get_info_enable; }; -#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 66 +#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 70 extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { @@ -949,4 +954,34 @@ struct wlfw_wfc_call_status_resp_msg_v01 { #define WLFW_WFC_CALL_STATUS_RESP_MSG_V01_MAX_MSG_LEN 7 extern struct qmi_elem_info wlfw_wfc_call_status_resp_msg_v01_ei[]; +struct wlfw_get_info_req_msg_v01 { + u8 type; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; +}; + +#define WLFW_GET_INFO_REQ_MSG_V01_MAX_MSG_LEN 6153 +extern struct qmi_elem_info wlfw_get_info_req_msg_v01_ei[]; + +struct wlfw_get_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_GET_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct qmi_elem_info wlfw_get_info_resp_msg_v01_ei[]; + +struct wlfw_respond_get_info_ind_msg_v01 { + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 type_valid; + u8 type; + u8 is_last_valid; + u8 is_last; + u8 seq_no_valid; + u32 seq_no; +}; + +#define WLFW_RESPOND_GET_INFO_IND_MSG_V01_MAX_MSG_LEN 6164 +extern struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[]; + #endif diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 892096b85993..ee6cbb7894b7 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -166,6 +166,11 @@ extern int cnss_self_recovery(struct device *dev, enum cnss_recovery_reason reason); extern int cnss_force_fw_assert(struct device *dev); extern int cnss_force_collect_rddm(struct device *dev); +extern int cnss_qmi_send_get(struct device *dev); +extern int cnss_qmi_send_put(struct device *dev); +extern int cnss_qmi_send(struct device *dev, int type, void *cmd, + int cmd_len, void *cb_ctx, + int (*cb)(void *ctx, void *event, int event_len)); extern void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size); extern int cnss_get_fw_files_for_target(struct device *dev, struct cnss_fw_files *pfw_files, @@ -185,6 +190,9 @@ extern void cnss_request_pm_qos(struct device *dev, u32 qos_val); extern void cnss_remove_pm_qos(struct device *dev); extern void cnss_lock_pm_sem(struct device *dev); extern void cnss_release_pm_sem(struct device *dev); +extern void cnss_pci_lock_reg_window(struct device *dev, unsigned long *flags); +extern void cnss_pci_unlock_reg_window(struct device *dev, + unsigned long *flags); extern int cnss_wlan_pm_control(struct device *dev, bool vote); extern int cnss_auto_suspend(struct device *dev); extern int cnss_auto_resume(struct device *dev);