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