diff --git a/cnss2/bus.c b/cnss2/bus.c index dc8a39db0d..e6472cfeca 100644 --- a/cnss2/bus.c +++ b/cnss2/bus.c @@ -154,6 +154,22 @@ int cnss_bus_load_tme_patch(struct cnss_plat_data *plat_priv) } } +int cnss_bus_load_tme_opt_file(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_load_tme_opt_file(plat_priv->bus_priv, file); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) { if (!plat_priv) @@ -459,6 +475,37 @@ int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) } } +int cnss_bus_runtime_pm_get_sync(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_pm_runtime_get_sync(plat_priv->bus_priv, RTPM_ID_CNSS); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_runtime_pm_put(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + cnss_pci_pm_runtime_mark_last_busy(plat_priv->bus_priv); + cnss_pci_pm_runtime_put_autosuspend(plat_priv->bus_priv, RTPM_ID_CNSS); + break; + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + } +} + int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) { if (!plat_priv) diff --git a/cnss2/bus.h b/cnss2/bus.h index ea7e98e96c..eadea243d3 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -22,6 +22,10 @@ void cnss_bus_deinit(struct cnss_plat_data *plat_priv); void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv, char *prefix_name, char *name); int cnss_bus_load_tme_patch(struct cnss_plat_data *plat_priv); +int cnss_bus_load_tme_opt_file(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file); +int cnss_bus_runtime_pm_get_sync(struct cnss_plat_data *plat_priv); +void cnss_bus_runtime_pm_put(struct cnss_plat_data *plat_priv); int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); int cnss_bus_load_aux(struct cnss_plat_data *plat_priv); int cnss_bus_handle_dev_sol_irq(struct cnss_plat_data *plat_priv); diff --git a/cnss2/main.c b/cnss2/main.c index 3a0bec2402..d63004803d 100644 --- a/cnss2/main.c +++ b/cnss2/main.c @@ -4120,6 +4120,23 @@ static ssize_t recovery_show(struct device *dev, return curr_len; } +static ssize_t tme_opt_file_download_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 buf_size = PAGE_SIZE; + u32 curr_len = 0; + u32 buf_written = 0; + + buf_written = scnprintf(buf, buf_size, + "Usage: echo [file_type] > /sys/kernel/cnss/tme_opt_file_download\n" + "file_type = sec -- For OEM_FUSE file\n" + "file_type = rpr -- For RPR file\n" + "file_type = dpr -- For DPR file\n"); + + curr_len += buf_written; + return curr_len; +} + static ssize_t time_sync_period_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -4369,6 +4386,44 @@ static ssize_t qdss_conf_download_store(struct device *dev, return count; } +static ssize_t tme_opt_file_download_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cnss_plat_data *plat_priv = dev_get_drvdata(dev); + char cmd[5]; + + if (sscanf(buf, "%s", cmd) != 1) + return -EINVAL; + + if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_err("Firmware is not ready yet\n"); + return 0; + } + + if (plat_priv->device_id == PEACH_DEVICE_ID && + cnss_bus_runtime_pm_get_sync(plat_priv) < 0) + goto runtime_pm_put; + + if (strcmp(cmd, "sec") == 0) { + cnss_bus_load_tme_opt_file(plat_priv, WLFW_TME_LITE_OEM_FUSE_FILE_V01); + cnss_wlfw_tme_opt_file_dnld_send_sync(plat_priv, WLFW_TME_LITE_OEM_FUSE_FILE_V01); + } else if (strcmp(cmd, "rpr") == 0) { + cnss_bus_load_tme_opt_file(plat_priv, WLFW_TME_LITE_RPR_FILE_V01); + cnss_wlfw_tme_opt_file_dnld_send_sync(plat_priv, WLFW_TME_LITE_RPR_FILE_V01); + } else if (strcmp(cmd, "dpr") == 0) { + cnss_bus_load_tme_opt_file(plat_priv, WLFW_TME_LITE_DPR_FILE_V01); + cnss_wlfw_tme_opt_file_dnld_send_sync(plat_priv, WLFW_TME_LITE_DPR_FILE_V01); + } + + cnss_pr_dbg("Received tme_opt_file_download indication cmd: %s\n", cmd); + +runtime_pm_put: + if (plat_priv->device_id == PEACH_DEVICE_ID) + cnss_bus_runtime_pm_put(plat_priv); + return count; +} + static ssize_t hw_trace_override_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -4406,6 +4461,7 @@ static DEVICE_ATTR_WO(enable_hds); static DEVICE_ATTR_WO(qdss_trace_start); static DEVICE_ATTR_WO(qdss_trace_stop); static DEVICE_ATTR_WO(qdss_conf_download); +static DEVICE_ATTR_RW(tme_opt_file_download); static DEVICE_ATTR_WO(hw_trace_override); static DEVICE_ATTR_WO(charger_mode); static DEVICE_ATTR_RW(time_sync_period); @@ -4418,6 +4474,7 @@ static struct attribute *cnss_attrs[] = { &dev_attr_qdss_trace_start.attr, &dev_attr_qdss_trace_stop.attr, &dev_attr_qdss_conf_download.attr, + &dev_attr_tme_opt_file_download.attr, &dev_attr_hw_trace_override.attr, &dev_attr_charger_mode.attr, &dev_attr_time_sync_period.attr, diff --git a/cnss2/main.h b/cnss2/main.h index 39f9021aa5..a610e5c16c 100644 --- a/cnss2/main.h +++ b/cnss2/main.h @@ -81,6 +81,10 @@ #define CNSS_EVENT_SYNC_UNINTERRUPTIBLE (CNSS_EVENT_SYNC | \ CNSS_EVENT_UNINTERRUPTIBLE) #define CNSS_EVENT_SYNC_UNKILLABLE (CNSS_EVENT_SYNC | CNSS_EVENT_UNKILLABLE) +#define QMI_WLFW_MAX_TME_OPT_FILE_NUM 3 +#define TME_OEM_FUSE_FILE_NAME "peach_sec.dat" +#define TME_RPR_FILE_NAME "peach_rpr.bin" +#define TME_DPR_FILE_NAME "peach_dpr.bin" enum cnss_dt_type { CNSS_DTT_LEGACY = 0, @@ -552,6 +556,7 @@ struct cnss_plat_data { struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; struct cnss_fw_mem m3_mem; struct cnss_fw_mem tme_lite_mem; + struct cnss_fw_mem tme_opt_file_mem[QMI_WLFW_MAX_TME_OPT_FILE_NUM]; struct cnss_fw_mem *cal_mem; struct cnss_fw_mem aux_mem; u64 cal_time; diff --git a/cnss2/pci.c b/cnss2/pci.c index 9f4c6b7947..963f9fb674 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -4919,6 +4919,91 @@ static void cnss_pci_free_tme_lite_mem(struct cnss_pci_data *pci_priv) tme_lite_mem->size = 0; } +int cnss_pci_load_tme_opt_file(struct cnss_pci_data *pci_priv, + enum wlfw_tme_lite_file_type_v01 file) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *tme_lite_mem = NULL; + char filename[MAX_FIRMWARE_NAME_LEN]; + char *tme_opt_filename = NULL; + const struct firmware *fw_entry; + int ret = 0; + + switch (pci_priv->device_id) { + case PEACH_DEVICE_ID: + if (file == WLFW_TME_LITE_OEM_FUSE_FILE_V01) { + tme_opt_filename = TME_OEM_FUSE_FILE_NAME; + tme_lite_mem = &plat_priv->tme_opt_file_mem[0]; + } else if (file == WLFW_TME_LITE_RPR_FILE_V01) { + tme_opt_filename = TME_RPR_FILE_NAME; + tme_lite_mem = &plat_priv->tme_opt_file_mem[1]; + } else if (file == WLFW_TME_LITE_DPR_FILE_V01) { + tme_opt_filename = TME_DPR_FILE_NAME; + tme_lite_mem = &plat_priv->tme_opt_file_mem[2]; + } + break; + case QCA6174_DEVICE_ID: + case QCA6290_DEVICE_ID: + case QCA6390_DEVICE_ID: + case QCA6490_DEVICE_ID: + case KIWI_DEVICE_ID: + case MANGO_DEVICE_ID: + default: + cnss_pr_dbg("TME-L opt file: %s not supported for device ID: (0x%x)\n", + tme_opt_filename, pci_priv->device_id); + return 0; + } + + if (!tme_lite_mem->va && !tme_lite_mem->size) { + cnss_pci_add_fw_prefix_name(pci_priv, filename, + tme_opt_filename); + + ret = firmware_request_nowarn(&fw_entry, filename, + &pci_priv->pci_dev->dev); + if (ret) { + cnss_pr_err("Failed to load TME-L opt file: %s, ret: %d\n", + filename, ret); + return ret; + } + + tme_lite_mem->va = dma_alloc_coherent(&pci_priv->pci_dev->dev, + fw_entry->size, &tme_lite_mem->pa, + GFP_KERNEL); + if (!tme_lite_mem->va) { + cnss_pr_err("Failed to allocate memory for TME-L opt file %s,size: 0x%zx\n", + filename, fw_entry->size); + release_firmware(fw_entry); + return -ENOMEM; + } + + memcpy(tme_lite_mem->va, fw_entry->data, fw_entry->size); + tme_lite_mem->size = fw_entry->size; + release_firmware(fw_entry); + } + + return 0; +} + +static void cnss_pci_free_tme_opt_file_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *tme_opt_file_mem = plat_priv->tme_opt_file_mem; + int i = 0; + + for (i = 0; i < QMI_WLFW_MAX_TME_OPT_FILE_NUM; i++) { + if (tme_opt_file_mem[i].va && tme_opt_file_mem[i].size) { + cnss_pr_dbg("Free memory for TME opt file,va:0x%pK, pa:%pa, size:0x%zx\n", + tme_opt_file_mem[i].va, &tme_opt_file_mem[i].pa, + tme_opt_file_mem[i].size); + dma_free_coherent(&pci_priv->pci_dev->dev, tme_opt_file_mem[i].size, + tme_opt_file_mem[i].va, tme_opt_file_mem[i].pa); + } + tme_opt_file_mem[i].va = NULL; + tme_opt_file_mem[i].pa = 0; + tme_opt_file_mem[i].size = 0; + } +} + int cnss_pci_load_m3(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -7465,6 +7550,7 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) cnss_pci_unregister_driver_hdlr(pci_priv); cnss_pci_free_aux_mem(pci_priv); cnss_pci_free_tme_lite_mem(pci_priv); + cnss_pci_free_tme_opt_file_mem(pci_priv); cnss_pci_free_m3_mem(pci_priv); cnss_pci_free_fw_mem(pci_priv); cnss_pci_free_qdss_mem(pci_priv); diff --git a/cnss2/pci.h b/cnss2/pci.h index fa101b9922..6dcfd3e07d 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -260,6 +260,8 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv); int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv); void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_tme_patch(struct cnss_pci_data *pci_priv); +int cnss_pci_load_tme_opt_file(struct cnss_pci_data *pci_priv, + enum wlfw_tme_lite_file_type_v01 file); int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); void cnss_pci_free_blob_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_aux(struct cnss_pci_data *pci_priv); diff --git a/cnss2/qmi.c b/cnss2/qmi.c index 59203c0032..ab3e64642c 100644 --- a/cnss2/qmi.c +++ b/cnss2/qmi.c @@ -58,6 +58,10 @@ #define QMI_WLFW_MAC_READY_TIMEOUT_MS 50 #define QMI_WLFW_MAC_READY_MAX_RETRY 200 +// these error values are not defined in and fw is sending as error response +#define QMI_ERR_HARDWARE_RESTRICTED_V01 0x0053 +#define QMI_ERR_ENOMEM_V01 0x0002 + enum nm_modem_bit { SLEEP_CLOCK_SELECT_INTERNAL_BIT = BIT(1), HOST_CSTATE_BIT = BIT(2), @@ -982,6 +986,107 @@ out: return ret; } +int cnss_wlfw_tme_opt_file_dnld_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file) +{ + struct wlfw_tme_lite_info_req_msg_v01 *req; + struct wlfw_tme_lite_info_resp_msg_v01 *resp; + struct qmi_txn txn; + struct cnss_fw_mem *tme_opt_file_mem = NULL; + char *file_name = NULL; + int ret = 0; + + if (plat_priv->device_id != PEACH_DEVICE_ID) + return 0; + + cnss_pr_dbg("Sending TME opt file information message, state: 0x%lx\n", + plat_priv->driver_state); + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + + if (file == WLFW_TME_LITE_OEM_FUSE_FILE_V01) { + tme_opt_file_mem = &plat_priv->tme_opt_file_mem[0]; + file_name = TME_OEM_FUSE_FILE_NAME; + } else if (file == WLFW_TME_LITE_RPR_FILE_V01) { + tme_opt_file_mem = &plat_priv->tme_opt_file_mem[1]; + file_name = TME_RPR_FILE_NAME; + } else if (file == WLFW_TME_LITE_DPR_FILE_V01) { + tme_opt_file_mem = &plat_priv->tme_opt_file_mem[2]; + file_name = TME_DPR_FILE_NAME; + } + + if (!tme_opt_file_mem->pa || !tme_opt_file_mem->size) { + cnss_pr_err("Memory for TME opt file is not available\n"); + ret = -ENOMEM; + goto out; + } + + cnss_pr_dbg("TME opt file %s memory, va: 0x%pK, pa: %pa, size: 0x%zx\n", + file_name, tme_opt_file_mem->va, &tme_opt_file_mem->pa, tme_opt_file_mem->size); + + req->tme_file = file; + req->addr = tme_opt_file_mem->pa; + req->size = tme_opt_file_mem->size; + + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, + wlfw_tme_lite_info_resp_msg_v01_ei, resp); + if (ret < 0) { + cnss_pr_err("Failed to initialize txn for TME opt file information request, err: %d\n", + ret); + goto out; + } + + ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn, + QMI_WLFW_TME_LITE_INFO_REQ_V01, + WLFW_TME_LITE_INFO_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_tme_lite_info_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + cnss_pr_err("Failed to send TME opt file information 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 TME opt file information request, err: %d\n", + ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + ret = -resp->resp.result; + if (resp->resp.error == QMI_ERR_HARDWARE_RESTRICTED_V01) { + cnss_pr_err("TME Power On failed\n"); + goto out; + } else if (resp->resp.error == QMI_ERR_ENOMEM_V01) { + cnss_pr_err("malloc SRAM failed\n"); + goto out; + } + cnss_pr_err("TME opt file information request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + goto out; + } + + kfree(req); + kfree(resp); + return 0; + +out: + CNSS_QMI_ASSERT(); + kfree(req); + kfree(resp); + return ret; +} + int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv) { struct wlfw_m3_info_req_msg_v01 *req; diff --git a/cnss2/qmi.h b/cnss2/qmi.h index e978f58732..cc94907764 100644 --- a/cnss2/qmi.h +++ b/cnss2/qmi.h @@ -87,6 +87,8 @@ void cnss_dms_deinit(struct cnss_plat_data *plat_priv); int cnss_wlfw_qdss_dnld_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_qdss_data_send_sync(struct cnss_plat_data *plat_priv, char *file_name, u32 total_size); +int cnss_wlfw_tme_opt_file_dnld_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file); int wlfw_qdss_trace_start(struct cnss_plat_data *plat_priv); int wlfw_qdss_trace_stop(struct cnss_plat_data *plat_priv, unsigned long long option); int cnss_wlfw_cal_report_req_send_sync(struct cnss_plat_data *plat_priv, @@ -297,6 +299,12 @@ int cnss_wlfw_qdss_data_send_sync(struct cnss_plat_data *plat_priv, char *file_n return 0; } +int cnss_wlfw_tme_opt_file_dnld_send_sync(struct cnss_plat_data *plat_priv, + enum wlfw_tme_lite_file_type_v01 file) +{ + return 0; +} + static inline void cnss_dms_deinit(struct cnss_plat_data *plat_priv) {} int wlfw_qdss_trace_start(struct cnss_plat_data *plat_priv)