diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath12k/qmi.c')
-rw-r--r-- | drivers/net/wireless/ath/ath12k/qmi.c | 504 |
1 files changed, 414 insertions, 90 deletions
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index b93ce9f87f61..348dbc81bad8 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/elf.h> @@ -2016,26 +2016,56 @@ static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { }, }; -static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, - struct qmi_wlanfw_host_cap_req_msg_v01 *req) +static void ath12k_host_cap_hw_link_id_init(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab, *partner_ab; + int i, j, hw_id_base; + + for (i = 0; i < ag->num_devices; i++) { + hw_id_base = 0; + ab = ag->ab[i]; + + for (j = 0; j < ag->num_devices; j++) { + partner_ab = ag->ab[j]; + + if (partner_ab->wsi_info.index >= ab->wsi_info.index) + continue; + + hw_id_base += partner_ab->qmi.num_radios; + } + + ab->wsi_info.hw_link_id_base = hw_id_base; + } + + ag->hw_link_id_init_done = true; +} + +static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, + struct qmi_wlanfw_host_cap_req_msg_v01 *req) { struct wlfw_host_mlo_chip_info_s_v01 *info; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_base *partner_ab; u8 hw_link_id = 0; - int i; + int i, j, ret; - if (!(ab->mlo_capable_flags & ATH12K_INTRA_DEVICE_MLO_SUPPORT)) { + if (!ag->mlo_capable) { ath12k_dbg(ab, ATH12K_DBG_QMI, - "intra device MLO is disabled hence skip QMI MLO cap"); - return; + "MLO is disabled hence skip QMI MLO cap"); + return 0; } if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { - ab->mlo_capable_flags = 0; - + ag->mlo_capable = false; ath12k_dbg(ab, ATH12K_DBG_QMI, "skip QMI MLO cap due to invalid num_radio %d\n", ab->qmi.num_radios); - return; + return 0; + } + + if (ab->device_id == ATH12K_INVALID_DEVICE_ID) { + ath12k_err(ab, "failed to send MLO cap due to invalid device id\n"); + return -EINVAL; } req->mlo_capable_valid = 1; @@ -2043,30 +2073,88 @@ static void ath12k_host_cap_parse_mlo(struct ath12k_base *ab, req->mlo_chip_id_valid = 1; req->mlo_chip_id = ab->device_id; req->mlo_group_id_valid = 1; - req->mlo_group_id = 0; + req->mlo_group_id = ag->id; req->max_mlo_peer_valid = 1; /* Max peer number generally won't change for the same device * but needs to be synced with host driver. */ req->max_mlo_peer = ab->hw_params->max_mlo_peer; req->mlo_num_chips_valid = 1; - req->mlo_num_chips = 1; + req->mlo_num_chips = ag->num_devices; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo capability advertisement device_id %d group_id %d num_devices %d", + req->mlo_chip_id, req->mlo_group_id, req->mlo_num_chips); - info = &req->mlo_chip_info[0]; - info->chip_id = ab->device_id; - info->num_local_links = ab->qmi.num_radios; + mutex_lock(&ag->mutex); - for (i = 0; i < info->num_local_links; i++) { - info->hw_link_id[i] = hw_link_id; - info->valid_mlo_link_id[i] = 1; + if (!ag->hw_link_id_init_done) + ath12k_host_cap_hw_link_id_init(ag); - hw_link_id++; + for (i = 0; i < ag->num_devices; i++) { + info = &req->mlo_chip_info[i]; + partner_ab = ag->ab[i]; + + if (partner_ab->device_id == ATH12K_INVALID_DEVICE_ID) { + ath12k_err(ab, "failed to send MLO cap due to invalid partner device id\n"); + ret = -EINVAL; + goto device_cleanup; + } + + info->chip_id = partner_ab->device_id; + info->num_local_links = partner_ab->qmi.num_radios; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo device id %d num_link %d\n", + info->chip_id, info->num_local_links); + + for (j = 0; j < info->num_local_links; j++) { + info->hw_link_id[j] = partner_ab->wsi_info.hw_link_id_base + j; + info->valid_mlo_link_id[j] = 1; + + ath12k_dbg(ab, ATH12K_DBG_QMI, "mlo hw_link_id %d\n", + info->hw_link_id[j]); + + hw_link_id++; + } } + if (hw_link_id <= 0) + ag->mlo_capable = false; + req->mlo_chip_info_valid = 1; + + mutex_unlock(&ag->mutex); + + return 0; + +device_cleanup: + for (i = i - 1; i >= 0; i--) { + info = &req->mlo_chip_info[i]; + + memset(info, 0, sizeof(*info)); + } + + req->mlo_num_chips = 0; + req->mlo_num_chips_valid = 0; + + req->max_mlo_peer = 0; + req->max_mlo_peer_valid = 0; + req->mlo_group_id = 0; + req->mlo_group_id_valid = 0; + req->mlo_chip_id = 0; + req->mlo_chip_id_valid = 0; + req->mlo_capable = 0; + req->mlo_capable_valid = 0; + + ag->mlo_capable = false; + + mutex_unlock(&ag->mutex); + + return ret; } -static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_host_cap_send(struct ath12k_base *ab) { struct qmi_wlanfw_host_cap_req_msg_v01 req = {}; struct qmi_wlanfw_host_cap_resp_msg_v01 resp = {}; @@ -2111,7 +2199,9 @@ static int ath12k_qmi_host_cap_send(struct ath12k_base *ab) req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; } - ath12k_host_cap_parse_mlo(ab, &req); + ret = ath12k_host_cap_parse_mlo(ab, &req); + if (ret < 0) + goto out; ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); @@ -2174,13 +2264,6 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) goto out; } - if (resp.single_chip_mlo_support_valid) { - if (resp.single_chip_mlo_support) - ab->mlo_capable_flags |= ATH12K_INTRA_DEVICE_MLO_SUPPORT; - else - ab->mlo_capable_flags &= ~ATH12K_INTRA_DEVICE_MLO_SUPPORT; - } - if (!resp.num_phy_valid) { ret = -ENODATA; goto out; @@ -2189,10 +2272,9 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = resp.num_phy; ath12k_dbg(ab, ATH12K_DBG_QMI, - "phy capability resp valid %d num_phy %d valid %d board_id %d valid %d single_chip_mlo_support %d\n", + "phy capability resp valid %d num_phy %d valid %d board_id %d\n", resp.num_phy_valid, resp.num_phy, - resp.board_id_valid, resp.board_id, - resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support); + resp.board_id_valid, resp.board_id); return; @@ -2275,7 +2357,9 @@ resp_out: return ret; } -static int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) { struct qmi_wlanfw_respond_mem_req_msg_v01 *req; struct qmi_wlanfw_respond_mem_resp_msg_v01 resp = {}; @@ -2350,30 +2434,125 @@ out: return ret; } +static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, + struct target_mem_chunk *chunk, + int idx) +{ + struct ath12k_hw_group *ag = ab->ag; + struct target_mem_chunk *mlo_chunk; + + lockdep_assert_held(&ag->mutex); + + if (!ag->mlo_mem.init_done || ag->num_started) + return; + + if (idx >= ARRAY_SIZE(ag->mlo_mem.chunk)) { + ath12k_warn(ab, "invalid index for MLO memory chunk free: %d\n", idx); + return; + } + + mlo_chunk = &ag->mlo_mem.chunk[idx]; + if (mlo_chunk->v.addr) { + dma_free_coherent(ab->dev, + mlo_chunk->size, + mlo_chunk->v.addr, + mlo_chunk->paddr); + mlo_chunk->v.addr = NULL; + } + + mlo_chunk->paddr = 0; + mlo_chunk->size = 0; + chunk->v.addr = NULL; + chunk->paddr = 0; + chunk->size = 0; +} + static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) { - int i; + struct ath12k_hw_group *ag = ab->ag; + int i, mlo_idx; - for (i = 0; i < ab->qmi.mem_seg_count; i++) { + for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { if (!ab->qmi.target_mem[i].v.addr) continue; - dma_free_coherent(ab->dev, - ab->qmi.target_mem[i].prev_size, - ab->qmi.target_mem[i].v.addr, - ab->qmi.target_mem[i].paddr); - ab->qmi.target_mem[i].v.addr = NULL; + if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) { + ath12k_qmi_free_mlo_mem_chunk(ab, + &ab->qmi.target_mem[i], + mlo_idx++); + } else { + dma_free_coherent(ab->dev, + ab->qmi.target_mem[i].prev_size, + ab->qmi.target_mem[i].v.addr, + ab->qmi.target_mem[i].paddr); + ab->qmi.target_mem[i].v.addr = NULL; + } + } + + if (!ag->num_started && ag->mlo_mem.init_done) { + ag->mlo_mem.init_done = false; + ag->mlo_mem.mlo_mem_size = 0; + } +} + +static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, + struct target_mem_chunk *chunk) +{ + /* Firmware reloads in recovery/resume. + * In such cases, no need to allocate memory for FW again. + */ + if (chunk->v.addr) { + if (chunk->prev_type == chunk->type && + chunk->prev_size == chunk->size) + goto this_chunk_done; + + /* cannot reuse the existing chunk */ + dma_free_coherent(ab->dev, chunk->prev_size, + chunk->v.addr, chunk->paddr); + chunk->v.addr = NULL; + } + + chunk->v.addr = dma_alloc_coherent(ab->dev, + chunk->size, + &chunk->paddr, + GFP_KERNEL | __GFP_NOWARN); + if (!chunk->v.addr) { + if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { + ab->qmi.target_mem_delayed = true; + ath12k_warn(ab, + "qmi dma allocation failed (%d B type %u), will try later with small size\n", + chunk->size, + chunk->type); + ath12k_qmi_free_target_mem_chunk(ab); + return -EAGAIN; + } + ath12k_warn(ab, "memory allocation failure for %u size: %d\n", + chunk->type, chunk->size); + return -ENOMEM; } + chunk->prev_type = chunk->type; + chunk->prev_size = chunk->size; +this_chunk_done: + return 0; } static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) { - int i; - struct target_mem_chunk *chunk; + struct target_mem_chunk *chunk, *mlo_chunk; + struct ath12k_hw_group *ag = ab->ag; + int i, mlo_idx, ret; + int mlo_size = 0; + + mutex_lock(&ag->mutex); + + if (!ag->mlo_mem.init_done) { + memset(ag->mlo_mem.chunk, 0, sizeof(ag->mlo_mem.chunk)); + ag->mlo_mem.init_done = true; + } ab->qmi.target_mem_delayed = false; - for (i = 0; i < ab->qmi.mem_seg_count; i++) { + for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { chunk = &ab->qmi.target_mem[i]; /* Allocate memory for the region and the functionality supported @@ -2385,42 +2564,41 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) case M3_DUMP_REGION_TYPE: case PAGEABLE_MEM_REGION_TYPE: case CALDB_MEM_REGION_TYPE: - /* Firmware reloads in recovery/resume. - * In such cases, no need to allocate memory for FW again. - */ - if (chunk->v.addr) { - if (chunk->prev_type == chunk->type && - chunk->prev_size == chunk->size) - goto this_chunk_done; - - /* cannot reuse the existing chunk */ - dma_free_coherent(ab->dev, chunk->prev_size, - chunk->v.addr, chunk->paddr); - chunk->v.addr = NULL; + ret = ath12k_qmi_alloc_chunk(ab, chunk); + if (ret) + goto err; + break; + case MLO_GLOBAL_MEM_REGION_TYPE: + mlo_size += chunk->size; + if (ag->mlo_mem.mlo_mem_size && + mlo_size > ag->mlo_mem.mlo_mem_size) { + ath12k_err(ab, "QMI MLO memory allocation failure, requested size %d is more than allocated size %d", + mlo_size, ag->mlo_mem.mlo_mem_size); + ret = -EINVAL; + goto err; } - chunk->v.addr = dma_alloc_coherent(ab->dev, - chunk->size, - &chunk->paddr, - GFP_KERNEL | __GFP_NOWARN); - if (!chunk->v.addr) { - if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { - ab->qmi.target_mem_delayed = true; - ath12k_warn(ab, - "qmi dma allocation failed (%d B type %u), will try later with small size\n", - chunk->size, - chunk->type); - ath12k_qmi_free_target_mem_chunk(ab); - return 0; + mlo_chunk = &ag->mlo_mem.chunk[mlo_idx]; + if (mlo_chunk->paddr) { + if (chunk->size != mlo_chunk->size) { + ath12k_err(ab, "QMI MLO chunk memory allocation failure for index %d, requested size %d is more than allocated size %d", + mlo_idx, chunk->size, mlo_chunk->size); + ret = -EINVAL; + goto err; } - ath12k_warn(ab, "memory allocation failure for %u size: %d\n", - chunk->type, chunk->size); - return -ENOMEM; + } else { + mlo_chunk->size = chunk->size; + mlo_chunk->type = chunk->type; + ret = ath12k_qmi_alloc_chunk(ab, mlo_chunk); + if (ret) + goto err; + memset(mlo_chunk->v.addr, 0, mlo_chunk->size); } - chunk->prev_type = chunk->type; - chunk->prev_size = chunk->size; -this_chunk_done: + chunk->paddr = mlo_chunk->paddr; + chunk->v.addr = mlo_chunk->v.addr; + mlo_idx++; + break; default: ath12k_warn(ab, "memory type %u not supported\n", @@ -2430,10 +2608,39 @@ this_chunk_done: break; } } + + if (!ag->mlo_mem.mlo_mem_size) { + ag->mlo_mem.mlo_mem_size = mlo_size; + } else if (ag->mlo_mem.mlo_mem_size != mlo_size) { + ath12k_err(ab, "QMI MLO memory size error, expected size is %d but requested size is %d", + ag->mlo_mem.mlo_mem_size, mlo_size); + ret = -EINVAL; + goto err; + } + + mutex_unlock(&ag->mutex); + return 0; + +err: + ath12k_qmi_free_target_mem_chunk(ab); + + mutex_unlock(&ag->mutex); + + /* The firmware will attempt to request memory in smaller chunks + * on the next try. However, the current caller should be notified + * that this instance of request parsing was successful. + * Therefore, return 0 only. + */ + if (ret == -EAGAIN) + ret = 0; + + return ret; } -static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_request_target_cap(struct ath12k_base *ab) { struct qmi_wlanfw_cap_req_msg_v01 req = {}; struct qmi_wlanfw_cap_resp_msg_v01 resp = {}; @@ -2527,6 +2734,15 @@ static int ath12k_qmi_request_target_cap(struct ath12k_base *ab) if (r) ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); + r = ath12k_acpi_start(ab); + if (r) + /* ACPI is optional so continue in case of an error */ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r); + + r = ath12k_acpi_check_bdf_variant_name(ab); + if (r) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "ACPI bdf variant name not set.\n"); + out: return ret; } @@ -2619,8 +2835,10 @@ out: return ret; } -static int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, - enum ath12k_qmi_bdf_type type) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_load_bdf_qmi(struct ath12k_base *ab, + enum ath12k_qmi_bdf_type type) { struct device *dev = ab->dev; char filename[ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE]; @@ -2791,7 +3009,9 @@ out: return ret; } -static int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req = {}; @@ -3023,6 +3243,8 @@ void ath12k_qmi_firmware_stop(struct ath12k_base *ab) { int ret; + clear_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags); + ret = ath12k_qmi_wlanfw_mode_send(ab, ATH12K_FIRMWARE_MODE_OFF); if (ret < 0) { ath12k_warn(ab, "qmi failed to send wlan mode off\n"); @@ -3079,9 +3301,69 @@ ath12k_qmi_driver_event_post(struct ath12k_qmi *qmi, return 0; } -static int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) +void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab) { - struct ath12k_base *ab = qmi->ab; + struct ath12k_qmi *qmi = &ab->qmi; + + spin_lock(&qmi->event_lock); + + if (ath12k_qmi_get_event_block(qmi)) + ath12k_qmi_set_event_block(qmi, false); + + spin_unlock(&qmi->event_lock); + + ath12k_dbg(ab, ATH12K_DBG_QMI, "trigger host cap for device id %d\n", + ab->device_id); + + ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_HOST_CAP, NULL); +} + +static bool ath12k_qmi_hw_group_host_cap_ready(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + + if (!(ab && ab->qmi.num_radios != U8_MAX)) + return false; + } + + return true; +} + +static struct ath12k_base *ath12k_qmi_hw_group_find_blocked(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + spin_lock(&ab->qmi.event_lock); + + if (ath12k_qmi_get_event_block(&ab->qmi)) { + spin_unlock(&ab->qmi.event_lock); + return ab; + } + + spin_unlock(&ab->qmi.event_lock); + } + + return NULL; +} + +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) +{ + struct ath12k_base *ab = qmi->ab, *block_ab; + struct ath12k_hw_group *ag = ab->ag; int ret; ath12k_qmi_phy_cap_send(ab); @@ -3092,16 +3374,30 @@ static int ath12k_qmi_event_server_arrive(struct ath12k_qmi *qmi) return ret; } - ret = ath12k_qmi_host_cap_send(ab); - if (ret < 0) { - ath12k_warn(ab, "qmi failed to send host cap QMI:%d\n", ret); - return ret; + spin_lock(&qmi->event_lock); + + ath12k_qmi_set_event_block(qmi, true); + + spin_unlock(&qmi->event_lock); + + mutex_lock(&ag->mutex); + + if (ath12k_qmi_hw_group_host_cap_ready(ag)) { + ath12k_core_hw_group_set_mlo_capable(ag); + + block_ab = ath12k_qmi_hw_group_find_blocked(ag); + if (block_ab) + ath12k_qmi_trigger_host_cap(block_ab); } + mutex_unlock(&ag->mutex); + return ret; } -static int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; int ret; @@ -3115,7 +3411,9 @@ static int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) return ret; } -static int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) +/* clang stack usage explodes if this is inlined */ +static noinline_for_stack +int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; int ret; @@ -3280,6 +3578,21 @@ static const struct qmi_ops ath12k_qmi_ops = { .del_server = ath12k_qmi_ops_del_server, }; +static int ath12k_qmi_event_host_cap(struct ath12k_qmi *qmi) +{ + struct ath12k_base *ab = qmi->ab; + int ret; + + ret = ath12k_qmi_host_cap_send(ab); + if (ret < 0) { + ath12k_warn(ab, "failed to send qmi host cap for device id %d: %d\n", + ab->device_id, ret); + return ret; + } + + return ret; +} + static void ath12k_qmi_driver_event_work(struct work_struct *work) { struct ath12k_qmi *qmi = container_of(work, struct ath12k_qmi, @@ -3306,7 +3619,6 @@ static void ath12k_qmi_driver_event_work(struct work_struct *work) break; case ATH12K_QMI_EVENT_SERVER_EXIT: set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); - set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); break; case ATH12K_QMI_EVENT_REQUEST_MEM: ret = ath12k_qmi_event_mem_request(qmi); @@ -3320,20 +3632,28 @@ static void ath12k_qmi_driver_event_work(struct work_struct *work) break; case ATH12K_QMI_EVENT_FW_READY: clear_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags); - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { + if (test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags)) { if (ab->is_reset) ath12k_hal_dump_srng_stats(ab); + + set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); queue_work(ab->workqueue, &ab->restart_work); break; } clear_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); - clear_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); - ath12k_core_qmi_firmware_ready(ab); - set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + ret = ath12k_core_qmi_firmware_ready(ab); + if (!ret) + set_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, + &ab->dev_flags); break; + case ATH12K_QMI_EVENT_HOST_CAP: + ret = ath12k_qmi_event_host_cap(qmi); + if (ret < 0) + set_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags); + break; default: ath12k_warn(ab, "invalid event type: %d", event->type); break; @@ -3386,11 +3706,15 @@ int ath12k_qmi_init_service(struct ath12k_base *ab) void ath12k_qmi_deinit_service(struct ath12k_base *ab) { + if (!ab->qmi.ab) + return; + qmi_handle_release(&ab->qmi.handle); cancel_work_sync(&ab->qmi.event_work); destroy_workqueue(ab->qmi.event_wq); ath12k_qmi_m3_free(ab); ath12k_qmi_free_target_mem_chunk(ab); + ab->qmi.ab = NULL; } void ath12k_qmi_free_resource(struct ath12k_base *ab) |