| /* |
| * Copyright (c) 2018 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: init_cmd_api.c |
| * |
| * WMI Init command prepare & send APIs |
| */ |
| #include <qdf_status.h> |
| #include <qdf_types.h> |
| #include <wlan_objmgr_psoc_obj.h> |
| #include <wlan_objmgr_pdev_obj.h> |
| #include <target_if.h> |
| #include <service_ready_util.h> |
| #include <wlan_tgt_def_config.h> |
| #include <wlan_reg_ucfg_api.h> |
| #include <init_cmd_api.h> |
| #include <wlan_defs.h> |
| #include <target_if_scan.h> |
| #include <target_if_reg.h> |
| |
| /** |
| * init_deinit_alloc_host_mem_chunk() - allocates chunk of memory requested |
| * by FW. |
| * @psoc: PSOC object |
| * @tgt_hdl: Target PSOC info |
| * @req_id: request id |
| * @idx: chunk id |
| * @num_units: Number of units |
| * @unit_len: Unit length |
| * @num_unit_info: Num unit info |
| * |
| * API to allocate host memory chunk requested by FW |
| * |
| * Return: num_units on successful allocation |
| * 0 on failure |
| */ |
| static uint32_t init_deinit_alloc_host_mem_chunk(struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl, |
| u_int32_t req_id, u_int32_t idx, u_int32_t num_units, |
| u_int32_t unit_len, u_int32_t num_unit_info) |
| { |
| qdf_dma_addr_t paddr; |
| uint32_t ichunk = 0; |
| struct tgt_info *info; |
| qdf_device_t qdf_dev; |
| |
| info = (&tgt_hdl->info); |
| |
| if (!num_units || !unit_len) |
| return 0; |
| |
| qdf_dev = wlan_psoc_get_qdf_dev(psoc); |
| if (!qdf_dev) |
| return 0; |
| |
| /* |
| * We have skip smaller chunks memory allocation for TXBF_CV buffer |
| * as Firmware is expecting continuous memory |
| */ |
| if (!((num_unit_info & HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED) && |
| (req_id == TXBF_CV_POOL0 || req_id == TXBF_CV_POOL1 || |
| req_id == TXBF_CV_POOL2))) { |
| ichunk = ((num_units * unit_len) >> |
| HOST_MEM_CHUNK_MAX_SIZE_POWER2); |
| if (ichunk) |
| num_units = num_units / (ichunk + 1); |
| } |
| |
| info->mem_chunks[idx].vaddr = NULL; |
| /* reduce the requested allocation by half until allocation succeeds */ |
| while (!info->mem_chunks[idx].vaddr && num_units) { |
| info->mem_chunks[idx].vaddr = qdf_mem_alloc_consistent(qdf_dev, |
| qdf_dev->dev, num_units * unit_len, &paddr); |
| if (info->mem_chunks[idx].vaddr == NULL) { |
| if (num_unit_info & |
| HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED) { |
| num_units = 0; |
| target_if_err("mem chink alloc failed for %d", |
| idx); |
| break; |
| } |
| /* reduce length by half */ |
| num_units = (num_units >> 1); |
| } else { |
| info->mem_chunks[idx].paddr = paddr; |
| info->mem_chunks[idx].len = num_units*unit_len; |
| info->mem_chunks[idx].req_id = req_id; |
| } |
| } |
| target_if_debug("req_id %d idx %d num_units %d unit_len %d", |
| req_id, idx, num_units, unit_len); |
| |
| return num_units; |
| } |
| |
| /* Host mem size units, it is used for round-off */ |
| #define HOST_MEM_SIZE_UNIT 4 |
| |
| /** |
| * init_deinit_alloc_host_mem() - allocates amount of memory requested by FW. |
| * @psoc: PSOC object |
| * @tgt_hdl: Target PSOC info |
| * @req_id: request id |
| * @num_units: Number of units |
| * @unit_len: Unit length |
| * @num_unit_info: Num unit info |
| * |
| * API to allocate host memory requested by FW |
| * |
| * Return: QDF_STATUS_SUCCESS on successful allocation |
| * QDF_STATUS_E_FAILURE on failure |
| */ |
| static QDF_STATUS init_deinit_alloc_host_mem(struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl, u_int32_t req_id, |
| u_int32_t num_units, u_int32_t unit_len, |
| u_int32_t num_unit_info) |
| { |
| struct tgt_info *info; |
| uint32_t remaining_units; |
| uint32_t allocated_units = 0; |
| uint32_t idx; |
| |
| info = (&tgt_hdl->info); |
| /* adjust the length to nearest multiple of unit size */ |
| unit_len = (unit_len + (HOST_MEM_SIZE_UNIT - 1)) & |
| (~(HOST_MEM_SIZE_UNIT - 1)); |
| idx = info->num_mem_chunks; |
| remaining_units = num_units; |
| |
| while (remaining_units) { |
| if (idx == MAX_MEM_CHUNKS) { |
| target_if_err( |
| "REACHED MAX CHUNK LIMIT for mem units %d", |
| num_units); |
| target_if_err( |
| "unit len %d requested by FW, only allocated %d", |
| unit_len, (num_units - remaining_units)); |
| info->num_mem_chunks = idx; |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if ((tgt_hdl->tif_ops) && |
| (tgt_hdl->tif_ops->mem_mgr_alloc_chunk)) |
| allocated_units = tgt_hdl->tif_ops->mem_mgr_alloc_chunk( |
| psoc, tgt_hdl, req_id, idx, |
| remaining_units, |
| unit_len, num_unit_info); |
| else |
| allocated_units = init_deinit_alloc_host_mem_chunk( |
| psoc, tgt_hdl, req_id, idx, |
| remaining_units, |
| unit_len, num_unit_info); |
| if (allocated_units == 0) { |
| target_if_err("FAILED TO ALLOC mem unit len %d", |
| unit_len); |
| target_if_err("units requested %d units allocated %d", |
| num_units, (num_units - remaining_units)); |
| info->num_mem_chunks = idx; |
| return QDF_STATUS_E_NOMEM; |
| } |
| remaining_units -= allocated_units; |
| ++idx; |
| } |
| info->num_mem_chunks = idx; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * init_deinit_alloc_num_units() - allocates num units requested by FW. |
| * @psoc: PSOC object |
| * @tgt_hdl: Target PSOC info |
| * @mem_reqs: pointer to mem req |
| * @num_units: Number |
| * @i: FW priority |
| * @idx: Index |
| * |
| * API to allocate num units of host memory requested by FW |
| * |
| * Return: QDF_STATUS_SUCCESS on successful allocation |
| * QDF_STATUS_E_FAILURE on failure |
| */ |
| static QDF_STATUS init_deinit_alloc_num_units(struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl, |
| host_mem_req *mem_reqs, uint16_t fw_prio, |
| uint16_t idx) |
| { |
| struct tgt_info *info; |
| uint32_t num_units; |
| QDF_STATUS status; |
| |
| if (!tgt_hdl) { |
| target_if_err("target_psoc_info is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| info = (&tgt_hdl->info); |
| |
| if (((fw_prio == FW_MEM_HIGH_PRIORITY) && |
| (mem_reqs[idx].num_unit_info & |
| HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED)) || |
| ((fw_prio == FW_MEM_LOW_PRIORITY) && |
| (!(mem_reqs[idx].num_unit_info & |
| HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED)))) { |
| /* First allocate the memory that requires contiguous memory */ |
| num_units = mem_reqs[idx].num_units; |
| if (mem_reqs[idx].num_unit_info) { |
| if (mem_reqs[idx].num_unit_info & |
| NUM_UNITS_IS_NUM_PEERS) { |
| /* |
| * number of units allocated is equal to number |
| * of peers, 1 extra for self peer on target. |
| * this needs to be fixed, host and target can |
| * get out of sync |
| */ |
| num_units = info->wlan_res_cfg.num_peers + 1; |
| } |
| if (mem_reqs[idx].num_unit_info & |
| NUM_UNITS_IS_NUM_ACTIVE_PEERS) { |
| /* |
| * Requesting allocation of memory using |
| * num_active_peers in qcache. if qcache is |
| * disabled in host, then it should allocate |
| * memory for num_peers instead of |
| * num_active_peers. |
| */ |
| if (info->wlan_res_cfg.num_active_peers) |
| num_units = |
| info->wlan_res_cfg.num_active_peers + 1; |
| else |
| num_units = |
| info->wlan_res_cfg.num_peers + 1; |
| } |
| } |
| |
| target_if_debug("idx %d req %d num_units %d num_unit_info %d unit size %d actual units %d", |
| idx, mem_reqs[idx].req_id, |
| mem_reqs[idx].num_units, |
| mem_reqs[idx].num_unit_info, |
| mem_reqs[idx].unit_size, num_units); |
| |
| status = init_deinit_alloc_host_mem(psoc, tgt_hdl, |
| mem_reqs[idx].req_id, num_units, |
| mem_reqs[idx].unit_size, |
| mem_reqs[idx].num_unit_info); |
| if (status == QDF_STATUS_E_FAILURE) { |
| target_if_err( |
| "psoc:(%pK) num_mem_chunk exceeds supp number", |
| psoc); |
| return QDF_STATUS_E_FAILURE; |
| } else if (status == QDF_STATUS_E_NOMEM) { |
| target_if_err("soc:(%pK) mem alloc failure", psoc); |
| return QDF_STATUS_E_NOMEM; |
| } |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS init_deinit_free_num_units(struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl) |
| { |
| struct tgt_info *info; |
| qdf_device_t qdf_dev; |
| uint32_t idx; |
| QDF_STATUS status; |
| |
| if (!tgt_hdl) { |
| target_if_err("target_psoc_info is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if ((tgt_hdl->tif_ops) && |
| (tgt_hdl->tif_ops->mem_mgr_free_chunks)) { |
| status = tgt_hdl->tif_ops->mem_mgr_free_chunks(psoc, tgt_hdl); |
| } else { |
| qdf_dev = wlan_psoc_get_qdf_dev(psoc); |
| if (!qdf_dev) { |
| target_if_err("qdf_dev is null"); |
| QDF_BUG(0); |
| return QDF_STATUS_E_INVAL; |
| } |
| info = (&tgt_hdl->info); |
| for (idx = 0; idx < info->num_mem_chunks; idx++) { |
| qdf_mem_free_consistent( |
| qdf_dev, qdf_dev->dev, |
| info->mem_chunks[idx].len, |
| info->mem_chunks[idx].vaddr, |
| info->mem_chunks[idx].paddr, |
| qdf_get_dma_mem_context( |
| (&(info->mem_chunks[idx])), memctx)); |
| |
| info->mem_chunks[idx].vaddr = NULL; |
| info->mem_chunks[idx].paddr = 0; |
| info->mem_chunks[idx].len = 0; |
| } |
| info->num_mem_chunks = 0; |
| status = QDF_STATUS_SUCCESS; |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS init_deinit_handle_host_mem_req( |
| struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl, uint8_t *event) |
| { |
| uint8_t num_mem_reqs; |
| host_mem_req *mem_reqs; |
| uint32_t i; |
| uint32_t idx; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct common_wmi_handle *wmi_handle; |
| struct tgt_info *info; |
| |
| if (!tgt_hdl) { |
| target_if_err("target_psoc_info is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wmi_handle = target_psoc_get_wmi_hdl(tgt_hdl); |
| info = (&tgt_hdl->info); |
| |
| mem_reqs = wmi_extract_host_mem_req_from_service_ready( |
| wmi_handle, event, &num_mem_reqs); |
| if (!num_mem_reqs) |
| return QDF_STATUS_SUCCESS; |
| |
| for (i = 0; i < FW_PRIORITY_MAX; i++) { |
| for (idx = 0; idx < num_mem_reqs; idx++) { |
| status = init_deinit_alloc_num_units(psoc, tgt_hdl, |
| mem_reqs, i, idx); |
| if (status != QDF_STATUS_SUCCESS) |
| return status; |
| } |
| } |
| |
| return status; |
| } |
| |
| void init_deinit_derive_band_to_mac_param( |
| struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl, |
| struct wmi_host_pdev_band_to_mac *band_to_mac) |
| { |
| uint8_t i; |
| struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; |
| struct wlan_psoc_host_hal_reg_capabilities_ext *reg_cap; |
| struct tgt_info *info; |
| |
| if (!tgt_hdl) { |
| target_if_err("target_psoc_info is null "); |
| return; |
| } |
| |
| info = (&tgt_hdl->info); |
| |
| reg_cap = ucfg_reg_get_hal_reg_cap(psoc); |
| if (!reg_cap) { |
| target_if_err("reg cap is NULL"); |
| return; |
| } |
| |
| for (i = 0; i < target_psoc_get_num_radios(tgt_hdl); i++) { |
| mac_phy_cap = &info->mac_phy_cap[i]; |
| if (mac_phy_cap->supported_bands == |
| (WMI_HOST_WLAN_5G_CAPABILITY | |
| WMI_HOST_WLAN_2G_CAPABILITY)) { |
| /*Supports both 5G and 2G. Use freq from both radios*/ |
| target_if_debug("Supports both 2G and 5G"); |
| band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; |
| band_to_mac[i].start_freq = |
| reg_cap[i].low_2ghz_chan; |
| band_to_mac[i].end_freq = |
| reg_cap[i].high_5ghz_chan; |
| |
| } else if (mac_phy_cap->supported_bands == |
| WMI_HOST_WLAN_2G_CAPABILITY) { |
| band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; |
| band_to_mac[i].start_freq = |
| reg_cap[i].low_2ghz_chan; |
| band_to_mac[i].end_freq = |
| reg_cap[i].high_2ghz_chan; |
| |
| reg_cap[mac_phy_cap->phy_id].low_5ghz_chan = 0; |
| reg_cap[mac_phy_cap->phy_id].high_5ghz_chan = 0; |
| |
| target_if_debug("2G radio - pdev_id = %d start_freq = %d end_freq= %d", |
| band_to_mac[i].pdev_id, |
| band_to_mac[i].start_freq, |
| band_to_mac[i].end_freq); |
| |
| } else if (mac_phy_cap->supported_bands == |
| WMI_HOST_WLAN_5G_CAPABILITY) { |
| band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; |
| band_to_mac[i].start_freq = |
| reg_cap[i].low_5ghz_chan; |
| band_to_mac[i].end_freq = |
| reg_cap[i].high_5ghz_chan; |
| |
| reg_cap[mac_phy_cap->phy_id].low_2ghz_chan = 0; |
| reg_cap[mac_phy_cap->phy_id].high_2ghz_chan = 0; |
| |
| target_if_debug("5G radio -pdev_id = %d start_freq = %d end_freq =%d\n", |
| band_to_mac[i].pdev_id, |
| band_to_mac[i].start_freq, |
| band_to_mac[i].end_freq); |
| } |
| } |
| } |
| |
| void init_deinit_prepare_send_init_cmd( |
| struct wlan_objmgr_psoc *psoc, |
| struct target_psoc_info *tgt_hdl) |
| { |
| struct wmi_init_cmd_param init_param = {0}; |
| struct tgt_info *info; |
| struct common_wmi_handle *wmi_handle; |
| QDF_STATUS ret_val; |
| |
| if (!tgt_hdl) { |
| target_if_err("target_psoc_info is null"); |
| return; |
| } |
| |
| wmi_handle = target_psoc_get_wmi_hdl(tgt_hdl); |
| info = (&tgt_hdl->info); |
| |
| init_param.res_cfg = &info->wlan_res_cfg; |
| init_param.num_mem_chunks = info->num_mem_chunks; |
| init_param.mem_chunks = info->mem_chunks; |
| |
| if (init_deinit_is_service_ext_msg(psoc, tgt_hdl) == |
| QDF_STATUS_SUCCESS) { |
| init_param.hw_mode_id = info->preferred_hw_mode; |
| /* Temp change, until FW submits support for handling this TLV |
| * For single mode, skip sending hw_mode |
| */ |
| if (info->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE) |
| init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; |
| |
| init_param.num_band_to_mac = target_psoc_get_num_radios( |
| tgt_hdl); |
| |
| init_deinit_derive_band_to_mac_param(psoc, tgt_hdl, |
| init_param.band_to_mac); |
| } else { |
| ret_val = tgt_if_regulatory_modify_freq_range(psoc); |
| if (QDF_IS_STATUS_ERROR(ret_val)) { |
| target_if_err("Modify freq range is failed"); |
| return; |
| } |
| } |
| |
| ret_val = target_if_alloc_pdevs(psoc, tgt_hdl); |
| if (ret_val != QDF_STATUS_SUCCESS) |
| return; |
| |
| ret_val = target_if_update_pdev_tgt_info(psoc, tgt_hdl); |
| if (ret_val != QDF_STATUS_SUCCESS) |
| return; |
| |
| target_if_info("FW version 0x%x ", info->target_caps.fw_version); |
| if (init_deinit_is_service_ext_msg(psoc, tgt_hdl) == QDF_STATUS_SUCCESS) |
| target_if_info("0x%x\n", |
| info->service_ext_param.fw_build_vers_ext); |
| else |
| target_if_info("0x%x\n", info->target_caps.fw_version_1); |
| |
| wmi_unified_init_cmd_send(wmi_handle, &init_param); |
| |
| /* Set Max scans allowed */ |
| target_if_scan_set_max_active_scans(psoc, |
| WLAN_MAX_ACTIVE_SCANS_ALLOWED); |
| } |