1 /* 2 * Copyright (c) 2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /** 20 * DOC: init_cmd_api.c 21 * 22 * WMI Init command prepare & send APIs 23 */ 24 #include <qdf_status.h> 25 #include <qdf_types.h> 26 #include <wlan_objmgr_psoc_obj.h> 27 #include <wlan_objmgr_pdev_obj.h> 28 #include <target_if.h> 29 #include <service_ready_util.h> 30 #include <wlan_tgt_def_config.h> 31 #include <wlan_reg_ucfg_api.h> 32 #include <init_cmd_api.h> 33 #include <wlan_defs.h> 34 #include <target_if_scan.h> 35 36 /** 37 * init_deinit_alloc_host_mem_chunk() - allocates chunk of memory requested 38 * by FW. 39 * @psoc: PSOC object 40 * @tgt_hdl: Target PSOC info 41 * @req_id: request id 42 * @idx: chunk id 43 * @num_units: Number of units 44 * @unit_len: Unit length 45 * @num_unit_info: Num unit info 46 * 47 * API to allocate host memory chunk requested by FW 48 * 49 * Return: num_units on successful allocation 50 * 0 on failure 51 */ 52 static uint32_t init_deinit_alloc_host_mem_chunk(struct wlan_objmgr_psoc *psoc, 53 struct target_psoc_info *tgt_hdl, 54 u_int32_t req_id, u_int32_t idx, u_int32_t num_units, 55 u_int32_t unit_len, u_int32_t num_unit_info) 56 { 57 qdf_dma_addr_t paddr; 58 uint32_t ichunk = 0; 59 struct tgt_info *info; 60 qdf_device_t qdf_dev; 61 62 info = (&tgt_hdl->info); 63 64 if (!num_units || !unit_len) 65 return 0; 66 67 qdf_dev = wlan_psoc_get_qdf_dev(psoc); 68 /* 69 * We have skip smaller chunks memory allocation for TXBF_CV buffer 70 * as Firmware is expecting continuous memory 71 */ 72 if (!((num_unit_info & HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED) && 73 (req_id == TXBF_CV_POOL0 || req_id == TXBF_CV_POOL1 || 74 req_id == TXBF_CV_POOL2))) { 75 ichunk = ((num_units * unit_len) >> 76 HOST_MEM_CHUNK_MAX_SIZE_POWER2); 77 if (ichunk) 78 num_units = num_units / (ichunk + 1); 79 } 80 81 info->mem_chunks[idx].vaddr = NULL; 82 /* reduce the requested allocation by half until allocation succeeds */ 83 while (!info->mem_chunks[idx].vaddr && num_units) { 84 info->mem_chunks[idx].vaddr = qdf_mem_alloc_consistent(qdf_dev, 85 qdf_dev->dev, num_units * unit_len, &paddr); 86 if (info->mem_chunks[idx].vaddr == NULL) { 87 if (num_unit_info & 88 HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED) { 89 num_units = 0; 90 target_if_err("mem chink alloc failed for %d", 91 idx); 92 break; 93 } 94 /* reduce length by half */ 95 num_units = (num_units >> 1); 96 } else { 97 info->mem_chunks[idx].paddr = paddr; 98 info->mem_chunks[idx].len = num_units*unit_len; 99 info->mem_chunks[idx].req_id = req_id; 100 } 101 } 102 target_if_info( 103 "req_id %d idx %d num_units %d unit_len %d", 104 req_id, idx, num_units, unit_len); 105 106 return num_units; 107 } 108 109 /* Host mem size units, it is used for round-off */ 110 #define HOST_MEM_SIZE_UNIT 4 111 112 /** 113 * init_deinit_alloc_host_mem() - allocates amount of memory requested by FW. 114 * @psoc: PSOC object 115 * @tgt_hdl: Target PSOC info 116 * @req_id: request id 117 * @num_units: Number of units 118 * @unit_len: Unit length 119 * @num_unit_info: Num unit info 120 * 121 * API to allocate host memory requested by FW 122 * 123 * Return: QDF_STATUS_SUCCESS on successful allocation 124 * QDF_STATUS_E_FAILURE on failure 125 */ 126 static QDF_STATUS init_deinit_alloc_host_mem(struct wlan_objmgr_psoc *psoc, 127 struct target_psoc_info *tgt_hdl, u_int32_t req_id, 128 u_int32_t num_units, u_int32_t unit_len, 129 u_int32_t num_unit_info) 130 { 131 struct tgt_info *info; 132 uint32_t remaining_units; 133 uint32_t allocated_units = 0; 134 uint32_t idx; 135 136 info = (&tgt_hdl->info); 137 /* adjust the length to nearest multiple of unit size */ 138 unit_len = (unit_len + (HOST_MEM_SIZE_UNIT - 1)) & 139 (~(HOST_MEM_SIZE_UNIT - 1)); 140 idx = info->num_mem_chunks; 141 remaining_units = num_units; 142 143 while (remaining_units) { 144 if (idx == MAX_MEM_CHUNKS) { 145 target_if_err( 146 "REACHED MAX CHUNK LIMIT for mem units %d", 147 num_units); 148 target_if_err( 149 "unit len %d requested by FW, only allocated %d", 150 unit_len, (num_units - remaining_units)); 151 info->num_mem_chunks = idx; 152 return QDF_STATUS_E_FAILURE; 153 } 154 155 if ((tgt_hdl->tif_ops) && 156 (tgt_hdl->tif_ops->mem_mgr_alloc_chunk)) 157 allocated_units = tgt_hdl->tif_ops->mem_mgr_alloc_chunk( 158 psoc, tgt_hdl, req_id, idx, 159 remaining_units, 160 unit_len, num_unit_info); 161 else 162 allocated_units = init_deinit_alloc_host_mem_chunk( 163 psoc, tgt_hdl, req_id, idx, 164 remaining_units, 165 unit_len, num_unit_info); 166 if (allocated_units == 0) { 167 target_if_err("FAILED TO ALLOC mem unit len %d", 168 unit_len); 169 target_if_err("units requested %d units allocated %d", 170 num_units, (num_units - remaining_units)); 171 info->num_mem_chunks = idx; 172 return QDF_STATUS_E_NOMEM; 173 } 174 remaining_units -= allocated_units; 175 ++idx; 176 } 177 info->num_mem_chunks = idx; 178 179 return QDF_STATUS_SUCCESS; 180 } 181 182 /** 183 * init_deinit_alloc_num_units() - allocates num units requested by FW. 184 * @psoc: PSOC object 185 * @tgt_hdl: Target PSOC info 186 * @mem_reqs: pointer to mem req 187 * @num_units: Number 188 * @i: FW priority 189 * @idx: Index 190 * 191 * API to allocate num units of host memory requested by FW 192 * 193 * Return: QDF_STATUS_SUCCESS on successful allocation 194 * QDF_STATUS_E_FAILURE on failure 195 */ 196 static QDF_STATUS init_deinit_alloc_num_units(struct wlan_objmgr_psoc *psoc, 197 struct target_psoc_info *tgt_hdl, 198 host_mem_req *mem_reqs, uint16_t fw_prio, 199 uint16_t idx) 200 { 201 struct tgt_info *info; 202 uint32_t num_units; 203 QDF_STATUS status; 204 205 if (!tgt_hdl) { 206 target_if_err("target_psoc_info is null"); 207 return QDF_STATUS_E_INVAL; 208 } 209 210 info = (&tgt_hdl->info); 211 212 if (((fw_prio == FW_MEM_HIGH_PRIORITY) && 213 (mem_reqs[idx].num_unit_info & 214 HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED)) || 215 ((fw_prio == FW_MEM_LOW_PRIORITY) && 216 (!(mem_reqs[idx].num_unit_info & 217 HOST_CONTIGUOUS_MEM_CHUNK_REQUIRED)))) { 218 /* First allocate the memory that requires contiguous memory */ 219 num_units = mem_reqs[idx].num_units; 220 if (mem_reqs[idx].num_unit_info) { 221 if (mem_reqs[idx].num_unit_info & 222 NUM_UNITS_IS_NUM_PEERS) { 223 /* 224 * number of units allocated is equal to number 225 * of peers, 1 extra for self peer on target. 226 * this needs to be fixed, host and target can 227 * get out of sync 228 */ 229 num_units = info->wlan_res_cfg.num_peers + 1; 230 } 231 if (mem_reqs[idx].num_unit_info & 232 NUM_UNITS_IS_NUM_ACTIVE_PEERS) { 233 /* 234 * Requesting allocation of memory using 235 * num_active_peers in qcache. if qcache is 236 * disabled in host, then it should allocate 237 * memory for num_peers instead of 238 * num_active_peers. 239 */ 240 if (info->wlan_res_cfg.num_active_peers) 241 num_units = 242 info->wlan_res_cfg.num_active_peers + 1; 243 else 244 num_units = 245 info->wlan_res_cfg.num_peers + 1; 246 } 247 } 248 249 target_if_info("idx %d req %d num_units %d num_unit_info %d unit size %d actual units %d", 250 idx, mem_reqs[idx].req_id, 251 mem_reqs[idx].num_units, 252 mem_reqs[idx].num_unit_info, 253 mem_reqs[idx].unit_size, num_units); 254 255 status = init_deinit_alloc_host_mem(psoc, tgt_hdl, 256 mem_reqs[idx].req_id, num_units, 257 mem_reqs[idx].unit_size, 258 mem_reqs[idx].num_unit_info); 259 if (status == QDF_STATUS_E_FAILURE) { 260 target_if_err( 261 "psoc:(%pK) num_mem_chunk exceeds supp number", 262 psoc); 263 return QDF_STATUS_E_FAILURE; 264 } else if (status == QDF_STATUS_E_NOMEM) { 265 target_if_err("soc:(%pK) mem alloc failure", psoc); 266 return QDF_STATUS_E_NOMEM; 267 } 268 } 269 270 return QDF_STATUS_SUCCESS; 271 } 272 273 QDF_STATUS init_deinit_free_num_units(struct wlan_objmgr_psoc *psoc, 274 struct target_psoc_info *tgt_hdl) 275 { 276 struct tgt_info *info; 277 qdf_device_t qdf_dev; 278 uint32_t idx; 279 QDF_STATUS status; 280 281 if (!tgt_hdl) { 282 target_if_err("target_psoc_info is null"); 283 return QDF_STATUS_E_INVAL; 284 } 285 286 if ((tgt_hdl->tif_ops) && 287 (tgt_hdl->tif_ops->mem_mgr_free_chunks)) { 288 status = tgt_hdl->tif_ops->mem_mgr_free_chunks(psoc, tgt_hdl); 289 } else { 290 qdf_dev = wlan_psoc_get_qdf_dev(psoc); 291 info = (&tgt_hdl->info); 292 for (idx = 0; idx < info->num_mem_chunks; idx++) { 293 qdf_mem_free_consistent( 294 qdf_dev, qdf_dev->dev, 295 info->mem_chunks[idx].len, 296 info->mem_chunks[idx].vaddr, 297 info->mem_chunks[idx].paddr, 298 qdf_get_dma_mem_context( 299 (&(info->mem_chunks[idx])), memctx)); 300 301 info->mem_chunks[idx].vaddr = NULL; 302 info->mem_chunks[idx].paddr = 0; 303 info->mem_chunks[idx].len = 0; 304 } 305 info->num_mem_chunks = 0; 306 status = QDF_STATUS_SUCCESS; 307 } 308 309 return status; 310 } 311 312 QDF_STATUS init_deinit_handle_host_mem_req( 313 struct wlan_objmgr_psoc *psoc, 314 struct target_psoc_info *tgt_hdl, uint8_t *event) 315 { 316 uint8_t num_mem_reqs; 317 host_mem_req *mem_reqs; 318 uint32_t i; 319 uint32_t idx; 320 QDF_STATUS status = QDF_STATUS_SUCCESS; 321 void *wmi_handle; 322 struct tgt_info *info; 323 324 if (!tgt_hdl) { 325 target_if_err("target_psoc_info is null"); 326 return QDF_STATUS_E_INVAL; 327 } 328 329 wmi_handle = target_psoc_get_wmi_hdl(tgt_hdl); 330 info = (&tgt_hdl->info); 331 332 mem_reqs = wmi_extract_host_mem_req_from_service_ready(wmi_handle, 333 event, &num_mem_reqs); 334 if (!num_mem_reqs) 335 return QDF_STATUS_SUCCESS; 336 337 for (i = 0; i < FW_PRIORITY_MAX; i++) { 338 for (idx = 0; idx < num_mem_reqs; idx++) { 339 status = init_deinit_alloc_num_units(psoc, tgt_hdl, 340 mem_reqs, i, idx); 341 if (status != QDF_STATUS_SUCCESS) 342 return status; 343 } 344 } 345 346 return status; 347 } 348 349 void init_deinit_derive_band_to_mac_param( 350 struct wlan_objmgr_psoc *psoc, 351 struct target_psoc_info *tgt_hdl, 352 struct wmi_host_pdev_band_to_mac *band_to_mac) 353 { 354 uint8_t i; 355 struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; 356 struct wlan_psoc_host_hal_reg_capabilities_ext *reg_cap; 357 struct tgt_info *info; 358 359 if (!tgt_hdl) { 360 target_if_err("target_psoc_info is null "); 361 return; 362 } 363 364 info = (&tgt_hdl->info); 365 366 reg_cap = ucfg_reg_get_hal_reg_cap(psoc); 367 if (!reg_cap) { 368 target_if_err("reg cap is NULL"); 369 return; 370 } 371 372 for (i = 0; i < target_psoc_get_num_radios(tgt_hdl); i++) { 373 mac_phy_cap = &info->mac_phy_cap[i]; 374 if (mac_phy_cap->supported_bands == 375 (WMI_HOST_WLAN_5G_CAPABILITY | 376 WMI_HOST_WLAN_2G_CAPABILITY)) { 377 /*Supports both 5G and 2G. Use freq from both radios*/ 378 target_if_info("Supports both 2G and 5G"); 379 band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; 380 band_to_mac[i].start_freq = 381 reg_cap[i].low_2ghz_chan; 382 band_to_mac[i].end_freq = 383 reg_cap[i].high_5ghz_chan; 384 385 } else if (mac_phy_cap->supported_bands == 386 WMI_HOST_WLAN_2G_CAPABILITY) { 387 band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; 388 band_to_mac[i].start_freq = 389 reg_cap[i].low_2ghz_chan; 390 band_to_mac[i].end_freq = 391 reg_cap[i].high_2ghz_chan; 392 393 reg_cap[mac_phy_cap->phy_id].low_5ghz_chan = 0; 394 reg_cap[mac_phy_cap->phy_id].high_5ghz_chan = 0; 395 396 target_if_info( 397 "2G radio - pdev_id = %d start_freq = %d end_freq= %d", 398 band_to_mac[i].pdev_id, 399 band_to_mac[i].start_freq, 400 band_to_mac[i].end_freq); 401 402 } else if (mac_phy_cap->supported_bands == 403 WMI_HOST_WLAN_5G_CAPABILITY) { 404 band_to_mac[i].pdev_id = mac_phy_cap->pdev_id; 405 band_to_mac[i].start_freq = 406 reg_cap[i].low_5ghz_chan; 407 band_to_mac[i].end_freq = 408 reg_cap[i].high_5ghz_chan; 409 410 reg_cap[mac_phy_cap->phy_id].low_2ghz_chan = 0; 411 reg_cap[mac_phy_cap->phy_id].high_2ghz_chan = 0; 412 413 target_if_info( 414 "5G radio -pdev_id = %d start_freq = %d end_freq =%d\n", 415 band_to_mac[i].pdev_id, 416 band_to_mac[i].start_freq, 417 band_to_mac[i].end_freq); 418 } 419 } 420 } 421 422 void init_deinit_prepare_send_init_cmd( 423 struct wlan_objmgr_psoc *psoc, 424 struct target_psoc_info *tgt_hdl) 425 { 426 struct wmi_init_cmd_param init_param = {0}; 427 struct tgt_info *info; 428 void *wmi_handle; 429 QDF_STATUS ret_val; 430 431 if (!tgt_hdl) { 432 target_if_err("target_psoc_info is null"); 433 return; 434 } 435 436 wmi_handle = target_psoc_get_wmi_hdl(tgt_hdl); 437 info = (&tgt_hdl->info); 438 439 init_param.res_cfg = &info->wlan_res_cfg; 440 init_param.num_mem_chunks = info->num_mem_chunks; 441 init_param.mem_chunks = info->mem_chunks; 442 443 if (init_deinit_is_service_ext_msg(psoc, tgt_hdl) == 444 QDF_STATUS_SUCCESS) { 445 init_param.hw_mode_id = info->preferred_hw_mode; 446 /* Temp change, until FW submits support for handling this TLV 447 * For single mode, skip sending hw_mode 448 */ 449 if (info->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE) 450 init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; 451 452 init_param.num_band_to_mac = target_psoc_get_num_radios( 453 tgt_hdl); 454 455 init_deinit_derive_band_to_mac_param(psoc, tgt_hdl, 456 init_param.band_to_mac); 457 } 458 459 ret_val = target_if_alloc_pdevs(psoc, tgt_hdl); 460 if (ret_val != QDF_STATUS_SUCCESS) 461 return; 462 463 ret_val = target_if_update_pdev_tgt_info(psoc, tgt_hdl); 464 if (ret_val != QDF_STATUS_SUCCESS) 465 return; 466 467 target_if_info("FW version 0x%x ", info->target_caps.fw_version); 468 if (init_deinit_is_service_ext_msg(psoc, tgt_hdl) == 469 QDF_STATUS_SUCCESS) 470 target_if_info("0x%x\n", 471 info->service_ext_param.fw_build_vers_ext); 472 else 473 target_if_info("0x%x\n", info->target_caps.fw_version_1); 474 475 wmi_unified_init_cmd_send(wmi_handle, &init_param); 476 477 /* Set Max scans allowed */ 478 target_if_scan_set_max_active_scans(psoc, 479 WLAN_MAX_ACTIVE_SCANS_ALLOWED); 480 } 481