diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/qmi.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath11k/qmi.c | 143 |
1 files changed, 87 insertions, 56 deletions
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 41fad03a3025..ff6a97e328b8 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1,17 +1,19 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/elf.h> +#include <linux/export.h> #include "qmi.h" #include "core.h" #include "debug.h" #include "hif.h" #include <linux/of.h> -#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> #include <linux/ioport.h> #include <linux/firmware.h> #include <linux/of_irq.h> @@ -1704,7 +1706,9 @@ static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { }, }; -static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath11k_qmi_host_cap_send(struct ath11k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req; struct qmi_wlanfw_host_cap_resp_msg_v01 resp; @@ -1955,13 +1959,15 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) int i; for (i = 0; i < ab->qmi.mem_seg_count; i++) { - if ((ab->hw_params.fixed_mem_region || - test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && - ab->qmi.target_mem[i].iaddr) - iounmap(ab->qmi.target_mem[i].iaddr); + if (!ab->qmi.target_mem[i].anyaddr) + continue; - if (!ab->qmi.target_mem[i].vaddr) + if (ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + iounmap(ab->qmi.target_mem[i].iaddr); + ab->qmi.target_mem[i].iaddr = NULL; continue; + } dma_free_coherent(ab->dev, ab->qmi.target_mem[i].prev_size, @@ -1989,6 +1995,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) chunk->prev_size == chunk->size) continue; + if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) { + ath11k_dbg(ab, ATH11K_DBG_QMI, + "size/type mismatch (current %d %u) (prev %d %u), try later with small size\n", + chunk->size, chunk->type, + chunk->prev_size, chunk->prev_type); + ab->qmi.target_mem_delayed = true; + return 0; + } + /* cannot reuse the existing chunk */ dma_free_coherent(ab->dev, chunk->prev_size, chunk->vaddr, chunk->paddr); @@ -2025,23 +2040,14 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) { struct device *dev = ab->dev; - struct device_node *hremote_node = NULL; - struct resource res; + struct resource res = {}; u32 host_ddr_sz; int i, idx, ret; for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { switch (ab->qmi.target_mem[i].type) { case HOST_DDR_REGION_TYPE: - hremote_node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!hremote_node) { - ath11k_dbg(ab, ATH11K_DBG_QMI, - "fail to get hremote_node\n"); - return -ENODEV; - } - - ret = of_address_to_resource(hremote_node, 0, &res); - of_node_put(hremote_node); + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); if (ret) { ath11k_dbg(ab, ATH11K_DBG_QMI, "fail to get reg from hremote\n"); @@ -2068,7 +2074,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) break; case BDF_MEM_REGION_TYPE: ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -2080,7 +2086,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } if (ath11k_core_coldboot_cal_support(ab)) { - if (hremote_node) { + if (resource_size(&res)) { ab->qmi.target_mem[idx].paddr = res.start + host_ddr_sz; ab->qmi.target_mem[idx].iaddr = @@ -2091,10 +2097,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } else { ab->qmi.target_mem[idx].paddr = ATH11K_QMI_CALDB_ADDRESS; + ab->qmi.target_mem[idx].iaddr = NULL; } } else { ab->qmi.target_mem[idx].paddr = 0; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; } ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; @@ -2180,6 +2187,9 @@ static int ath11k_qmi_request_device_info(struct ath11k_base *ab) ab->mem = bar_addr_va; ab->mem_len = resp.bar_size; + if (!ab->hw_params.ce_remap) + ab->mem_ce = ab->mem; + return 0; out: return ret; @@ -2293,7 +2303,7 @@ static int ath11k_qmi_load_file_target_mem(struct ath11k_base *ab, struct qmi_txn txn; const u8 *temp = data; void __iomem *bdf_addr = NULL; - int ret; + int ret = 0; u32 remaining = len; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -2502,38 +2512,56 @@ out: static int ath11k_qmi_m3_load(struct ath11k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; - const struct firmware *fw; + const struct firmware *fw = NULL; + const void *m3_data; char path[100]; + size_t m3_len; int ret; - fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE); - if (IS_ERR(fw)) { - ret = PTR_ERR(fw); - ath11k_core_create_firmware_path(ab, ATH11K_M3_FILE, - path, sizeof(path)); - ath11k_err(ab, "failed to load %s: %d\n", path, ret); - return ret; - } + if (m3_mem->vaddr) + /* m3 firmware buffer is already available in the DMA buffer */ + return 0; + + if (ab->fw.m3_data && ab->fw.m3_len > 0) { + /* firmware-N.bin had a m3 firmware file so use that */ + m3_data = ab->fw.m3_data; + m3_len = ab->fw.m3_len; + } else { + /* No m3 file in firmware-N.bin so try to request old + * separate m3.bin. + */ + fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE); + if (IS_ERR(fw)) { + ret = PTR_ERR(fw); + ath11k_core_create_firmware_path(ab, ATH11K_M3_FILE, + path, sizeof(path)); + ath11k_err(ab, "failed to load %s: %d\n", path, ret); + return ret; + } - if (m3_mem->vaddr || m3_mem->size) - goto skip_m3_alloc; + m3_data = fw->data; + m3_len = fw->size; + } m3_mem->vaddr = dma_alloc_coherent(ab->dev, - fw->size, &m3_mem->paddr, + m3_len, &m3_mem->paddr, GFP_KERNEL); if (!m3_mem->vaddr) { ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n", - fw->size); - release_firmware(fw); - return -ENOMEM; + m3_len); + ret = -ENOMEM; + goto out; } -skip_m3_alloc: - memcpy(m3_mem->vaddr, fw->data, fw->size); - m3_mem->size = fw->size; + memcpy(m3_mem->vaddr, m3_data, m3_len); + m3_mem->size = m3_len; + + ret = 0; + +out: release_firmware(fw); - return 0; + return ret; } static void ath11k_qmi_m3_free(struct ath11k_base *ab) @@ -2549,7 +2577,9 @@ static void ath11k_qmi_m3_free(struct ath11k_base *ab) m3_mem->size = 0; } -static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req; @@ -2841,7 +2871,7 @@ int ath11k_qmi_firmware_start(struct ath11k_base *ab, int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) { - int timeout; + long time_left; if (!ath11k_core_coldboot_cal_support(ab) || ab->hw_params.cbcal_restart_fw == 0) @@ -2849,17 +2879,17 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "wait for cold boot done\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + if (time_left <= 0) { ath11k_warn(ab, "Coldboot Calibration timed out\n"); return -ETIMEDOUT; } /* reset the firmware */ - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n"); return 0; @@ -2868,7 +2898,7 @@ EXPORT_SYMBOL(ath11k_qmi_fwreset_from_cold_boot); static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) { - int timeout; + long time_left; int ret; ret = ath11k_qmi_wlanfw_mode_send(ab, ATH11K_FIRMWARE_MODE_COLD_BOOT); @@ -2879,10 +2909,10 @@ static int ath11k_qmi_process_coldboot_calibration(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_QMI, "Coldboot calibration wait started\n"); - timeout = wait_event_timeout(ab->qmi.cold_boot_waitq, - (ab->qmi.cal_done == 1), - ATH11K_COLD_BOOT_FW_RESET_DELAY); - if (timeout <= 0) { + time_left = wait_event_timeout(ab->qmi.cold_boot_waitq, + (ab->qmi.cal_done == 1), + ATH11K_COLD_BOOT_FW_RESET_DELAY); + if (time_left <= 0) { ath11k_warn(ab, "coldboot calibration timed out\n"); return 0; } @@ -3147,7 +3177,7 @@ static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, sq->sq_node = service->node; sq->sq_port = service->port; - ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq, + ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)sq, sizeof(*sq), 0); if (ret) { ath11k_warn(ab, "failed to connect to qmi remote service: %d\n", ret); @@ -3231,7 +3261,8 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) case ATH11K_QMI_EVENT_FW_INIT_DONE: clear_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { - ath11k_hal_dump_srng_stats(ab); + if (ab->is_reset) + ath11k_hal_dump_srng_stats(ab); queue_work(ab->workqueue, &ab->restart_work); break; } |
