1 /* 2 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #ifdef IPA_OFFLOAD 18 19 #include <qdf_ipa_wdi3.h> 20 #include <qdf_types.h> 21 #include <qdf_lock.h> 22 #include <hal_hw_headers.h> 23 #include <hal_api.h> 24 #include <hif.h> 25 #include <htt.h> 26 #include <wdi_event.h> 27 #include <queue.h> 28 #include "dp_types.h" 29 #include "dp_htt.h" 30 #include "dp_tx.h" 31 #include "dp_ipa.h" 32 33 /* Hard coded config parameters until dp_ops_cfg.cfg_attach implemented */ 34 #define CFG_IPA_UC_TX_BUF_SIZE_DEFAULT (2048) 35 36 /** 37 * dp_tx_ipa_uc_detach - Free autonomy TX resources 38 * @soc: data path instance 39 * @pdev: core txrx pdev context 40 * 41 * Free allocated TX buffers with WBM SRNG 42 * 43 * Return: none 44 */ 45 static void dp_tx_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev) 46 { 47 int idx; 48 qdf_nbuf_t nbuf; 49 50 for (idx = 0; idx < soc->ipa_uc_tx_rsc.alloc_tx_buf_cnt; idx++) { 51 nbuf = (qdf_nbuf_t) 52 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned[idx]; 53 if (!nbuf) 54 continue; 55 qdf_nbuf_unmap_single(soc->osdev, nbuf, QDF_DMA_BIDIRECTIONAL); 56 qdf_nbuf_free(nbuf); 57 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned[idx] = 58 (void *)NULL; 59 } 60 61 qdf_mem_free(soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned); 62 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned = NULL; 63 } 64 65 /** 66 * dp_rx_ipa_uc_detach - free autonomy RX resources 67 * @soc: data path instance 68 * @pdev: core txrx pdev context 69 * 70 * This function will detach DP RX into main device context 71 * will free DP Rx resources. 72 * 73 * Return: none 74 */ 75 static void dp_rx_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev) 76 { 77 } 78 79 int dp_ipa_uc_detach(struct dp_soc *soc, struct dp_pdev *pdev) 80 { 81 /* TX resource detach */ 82 dp_tx_ipa_uc_detach(soc, pdev); 83 84 /* RX resource detach */ 85 dp_rx_ipa_uc_detach(soc, pdev); 86 87 return QDF_STATUS_SUCCESS; /* success */ 88 } 89 90 /** 91 * dp_tx_ipa_uc_attach - Allocate autonomy TX resources 92 * @soc: data path instance 93 * @pdev: Physical device handle 94 * 95 * Allocate TX buffer from non-cacheable memory 96 * Attache allocated TX buffers with WBM SRNG 97 * 98 * Return: int 99 */ 100 static int dp_tx_ipa_uc_attach(struct dp_soc *soc, struct dp_pdev *pdev) 101 { 102 uint32_t tx_buffer_count; 103 uint32_t ring_base_align = 8; 104 qdf_dma_addr_t buffer_paddr; 105 struct hal_srng *wbm_srng = 106 soc->tx_comp_ring[IPA_TX_COMP_RING_IDX].hal_srng; 107 struct hal_srng_params srng_params; 108 uint32_t paddr_lo; 109 uint32_t paddr_hi; 110 void *ring_entry; 111 int num_entries; 112 qdf_nbuf_t nbuf; 113 int retval = QDF_STATUS_SUCCESS; 114 /* 115 * Uncomment when dp_ops_cfg.cfg_attach is implemented 116 * unsigned int uc_tx_buf_sz = 117 * dp_cfg_ipa_uc_tx_buf_size(pdev->osif_pdev); 118 */ 119 unsigned int uc_tx_buf_sz = CFG_IPA_UC_TX_BUF_SIZE_DEFAULT; 120 unsigned int alloc_size = uc_tx_buf_sz + ring_base_align - 1; 121 122 hal_get_srng_params(soc->hal_soc, (void *)wbm_srng, &srng_params); 123 num_entries = srng_params.num_entries; 124 125 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, 126 "%s: requested %d buffers to be posted to wbm ring", 127 __func__, num_entries); 128 129 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned = 130 qdf_mem_malloc(num_entries * 131 sizeof(*soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned)); 132 if (!soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned) { 133 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 134 "%s: IPA WBM Ring Tx buf pool vaddr alloc fail", 135 __func__); 136 return -ENOMEM; 137 } 138 139 hal_srng_access_start(soc->hal_soc, (void *)wbm_srng); 140 141 /* 142 * Allocate Tx buffers as many as possible 143 * Populate Tx buffers into WBM2IPA ring 144 * This initial buffer population will simulate H/W as source ring, 145 * and update HP 146 */ 147 for (tx_buffer_count = 0; 148 tx_buffer_count < num_entries - 1; tx_buffer_count++) { 149 nbuf = qdf_nbuf_alloc(soc->osdev, alloc_size, 0, 256, FALSE); 150 if (!nbuf) 151 break; 152 153 ring_entry = hal_srng_dst_get_next_hp(soc->hal_soc, 154 (void *)wbm_srng); 155 if (!ring_entry) { 156 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, 157 "%s: Failed to get WBM ring entry", 158 __func__); 159 qdf_nbuf_free(nbuf); 160 break; 161 } 162 163 qdf_nbuf_map_single(soc->osdev, nbuf, 164 QDF_DMA_BIDIRECTIONAL); 165 buffer_paddr = qdf_nbuf_get_frag_paddr(nbuf, 0); 166 167 paddr_lo = ((uint64_t)buffer_paddr & 0x00000000ffffffff); 168 paddr_hi = ((uint64_t)buffer_paddr & 0x0000001f00000000) >> 32; 169 HAL_RXDMA_PADDR_LO_SET(ring_entry, paddr_lo); 170 HAL_RXDMA_PADDR_HI_SET(ring_entry, paddr_hi); 171 HAL_RXDMA_MANAGER_SET(ring_entry, (IPA_TCL_DATA_RING_IDX + 172 HAL_WBM_SW0_BM_ID)); 173 174 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned[tx_buffer_count] 175 = (void *)nbuf; 176 } 177 178 hal_srng_access_end(soc->hal_soc, wbm_srng); 179 180 soc->ipa_uc_tx_rsc.alloc_tx_buf_cnt = tx_buffer_count; 181 182 if (tx_buffer_count) { 183 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, 184 "%s: IPA WDI TX buffer: %d allocated", 185 __func__, tx_buffer_count); 186 } else { 187 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 188 "%s: No IPA WDI TX buffer allocated", 189 __func__); 190 qdf_mem_free(soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned); 191 soc->ipa_uc_tx_rsc.tx_buf_pool_vaddr_unaligned = NULL; 192 retval = -ENOMEM; 193 } 194 195 return retval; 196 } 197 198 /** 199 * dp_rx_ipa_uc_attach - Allocate autonomy RX resources 200 * @soc: data path instance 201 * @pdev: core txrx pdev context 202 * 203 * This function will attach a DP RX instance into the main 204 * device (SOC) context. 205 * 206 * Return: QDF_STATUS_SUCCESS: success 207 * QDF_STATUS_E_RESOURCES: Error return 208 */ 209 static int dp_rx_ipa_uc_attach(struct dp_soc *soc, struct dp_pdev *pdev) 210 { 211 return QDF_STATUS_SUCCESS; 212 } 213 214 int dp_ipa_uc_attach(struct dp_soc *soc, struct dp_pdev *pdev) 215 { 216 int error; 217 218 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 219 return QDF_STATUS_SUCCESS; 220 221 /* TX resource attach */ 222 error = dp_tx_ipa_uc_attach(soc, pdev); 223 if (error) { 224 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 225 "%s: DP IPA UC TX attach fail code %d", 226 __func__, error); 227 return error; 228 } 229 230 /* RX resource attach */ 231 error = dp_rx_ipa_uc_attach(soc, pdev); 232 if (error) { 233 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 234 "%s: DP IPA UC RX attach fail code %d", 235 __func__, error); 236 dp_tx_ipa_uc_detach(soc, pdev); 237 return error; 238 } 239 240 return QDF_STATUS_SUCCESS; /* success */ 241 } 242 243 /* 244 * dp_ipa_ring_resource_setup() - setup IPA ring resources 245 * @soc: data path SoC handle 246 * 247 * Return: none 248 */ 249 int dp_ipa_ring_resource_setup(struct dp_soc *soc, 250 struct dp_pdev *pdev) 251 { 252 struct hal_soc *hal_soc = (struct hal_soc *)soc->hal_soc; 253 struct hal_srng *hal_srng; 254 struct hal_srng_params srng_params; 255 qdf_dma_addr_t hp_addr; 256 unsigned long addr_offset, dev_base_paddr; 257 258 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 259 return QDF_STATUS_SUCCESS; 260 261 /* IPA TCL_DATA Ring - HAL_SRNG_SW2TCL3 */ 262 hal_srng = soc->tcl_data_ring[IPA_TCL_DATA_RING_IDX].hal_srng; 263 hal_get_srng_params(hal_soc, (void *)hal_srng, &srng_params); 264 265 soc->ipa_uc_tx_rsc.ipa_tcl_ring_base_paddr = 266 srng_params.ring_base_paddr; 267 soc->ipa_uc_tx_rsc.ipa_tcl_ring_base_vaddr = 268 srng_params.ring_base_vaddr; 269 soc->ipa_uc_tx_rsc.ipa_tcl_ring_size = 270 (srng_params.num_entries * srng_params.entry_size) << 2; 271 /* 272 * For the register backed memory addresses, use the scn->mem_pa to 273 * calculate the physical address of the shadow registers 274 */ 275 dev_base_paddr = 276 (unsigned long) 277 ((struct hif_softc *)(hal_soc->hif_handle))->mem_pa; 278 addr_offset = (unsigned long)(hal_srng->u.src_ring.hp_addr) - 279 (unsigned long)(hal_soc->dev_base_addr); 280 soc->ipa_uc_tx_rsc.ipa_tcl_hp_paddr = 281 (qdf_dma_addr_t)(addr_offset + dev_base_paddr); 282 283 dp_info("IPA TCL_DATA Ring addr_offset=%x, dev_base_paddr=%x, hp_paddr=%x paddr=%pK vaddr=%pK size= %u(%u bytes)", 284 (unsigned int)addr_offset, 285 (unsigned int)dev_base_paddr, 286 (unsigned int)(soc->ipa_uc_tx_rsc.ipa_tcl_hp_paddr), 287 (void *)soc->ipa_uc_tx_rsc.ipa_tcl_ring_base_paddr, 288 (void *)soc->ipa_uc_tx_rsc.ipa_tcl_ring_base_vaddr, 289 srng_params.num_entries, 290 soc->ipa_uc_tx_rsc.ipa_tcl_ring_size); 291 292 /* IPA TX COMP Ring - HAL_SRNG_WBM2SW2_RELEASE */ 293 hal_srng = soc->tx_comp_ring[IPA_TX_COMP_RING_IDX].hal_srng; 294 hal_get_srng_params(hal_soc, (void *)hal_srng, &srng_params); 295 296 soc->ipa_uc_tx_rsc.ipa_wbm_ring_base_paddr = 297 srng_params.ring_base_paddr; 298 soc->ipa_uc_tx_rsc.ipa_wbm_ring_base_vaddr = 299 srng_params.ring_base_vaddr; 300 soc->ipa_uc_tx_rsc.ipa_wbm_ring_size = 301 (srng_params.num_entries * srng_params.entry_size) << 2; 302 addr_offset = (unsigned long)(hal_srng->u.dst_ring.tp_addr) - 303 (unsigned long)(hal_soc->dev_base_addr); 304 soc->ipa_uc_tx_rsc.ipa_wbm_tp_paddr = 305 (qdf_dma_addr_t)(addr_offset + dev_base_paddr); 306 307 dp_info("IPA TX COMP Ring addr_offset=%x, dev_base_paddr=%x, ipa_wbm_tp_paddr=%x paddr=%pK vaddr=0%pK size= %u(%u bytes)", 308 (unsigned int)addr_offset, 309 (unsigned int)dev_base_paddr, 310 (unsigned int)(soc->ipa_uc_tx_rsc.ipa_wbm_tp_paddr), 311 (void *)soc->ipa_uc_tx_rsc.ipa_wbm_ring_base_paddr, 312 (void *)soc->ipa_uc_tx_rsc.ipa_wbm_ring_base_vaddr, 313 srng_params.num_entries, 314 soc->ipa_uc_tx_rsc.ipa_wbm_ring_size); 315 316 /* IPA REO_DEST Ring - HAL_SRNG_REO2SW4 */ 317 hal_srng = soc->reo_dest_ring[IPA_REO_DEST_RING_IDX].hal_srng; 318 hal_get_srng_params(hal_soc, (void *)hal_srng, &srng_params); 319 320 soc->ipa_uc_rx_rsc.ipa_reo_ring_base_paddr = 321 srng_params.ring_base_paddr; 322 soc->ipa_uc_rx_rsc.ipa_reo_ring_base_vaddr = 323 srng_params.ring_base_vaddr; 324 soc->ipa_uc_rx_rsc.ipa_reo_ring_size = 325 (srng_params.num_entries * srng_params.entry_size) << 2; 326 addr_offset = (unsigned long)(hal_srng->u.dst_ring.tp_addr) - 327 (unsigned long)(hal_soc->dev_base_addr); 328 soc->ipa_uc_rx_rsc.ipa_reo_tp_paddr = 329 (qdf_dma_addr_t)(addr_offset + dev_base_paddr); 330 331 dp_info("IPA REO_DEST Ring addr_offset=%x, dev_base_paddr=%x, tp_paddr=%x paddr=%pK vaddr=%pK size= %u(%u bytes)", 332 (unsigned int)addr_offset, 333 (unsigned int)dev_base_paddr, 334 (unsigned int)(soc->ipa_uc_rx_rsc.ipa_reo_tp_paddr), 335 (void *)soc->ipa_uc_rx_rsc.ipa_reo_ring_base_paddr, 336 (void *)soc->ipa_uc_rx_rsc.ipa_reo_ring_base_vaddr, 337 srng_params.num_entries, 338 soc->ipa_uc_rx_rsc.ipa_reo_ring_size); 339 340 hal_srng = pdev->rx_refill_buf_ring2.hal_srng; 341 hal_get_srng_params(hal_soc, (void *)hal_srng, &srng_params); 342 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_base_paddr = 343 srng_params.ring_base_paddr; 344 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_base_vaddr = 345 srng_params.ring_base_vaddr; 346 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_size = 347 (srng_params.num_entries * srng_params.entry_size) << 2; 348 hp_addr = hal_srng_get_hp_addr(hal_soc, (void *)hal_srng); 349 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_hp_paddr = hp_addr; 350 351 dp_info("IPA REFILL_BUF Ring hp_paddr=%x paddr=%pK vaddr=%pK size= %u(%u bytes)", 352 (unsigned int)(soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_hp_paddr), 353 (void *)soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_base_paddr, 354 (void *)soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_base_vaddr, 355 srng_params.num_entries, 356 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_size); 357 358 return 0; 359 } 360 361 /** 362 * dp_ipa_uc_get_resource() - Client request resource information 363 * @ppdev - handle to the device instance 364 * 365 * IPA client will request IPA UC related resource information 366 * Resource information will be distributed to IPA module 367 * All of the required resources should be pre-allocated 368 * 369 * Return: QDF_STATUS 370 */ 371 QDF_STATUS dp_ipa_get_resource(struct cdp_pdev *ppdev) 372 { 373 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 374 struct dp_soc *soc = pdev->soc; 375 struct dp_ipa_resources *ipa_res = &pdev->ipa_resource; 376 377 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 378 return QDF_STATUS_SUCCESS; 379 380 ipa_res->tx_ring_base_paddr = 381 soc->ipa_uc_tx_rsc.ipa_tcl_ring_base_paddr; 382 ipa_res->tx_ring_size = 383 soc->ipa_uc_tx_rsc.ipa_tcl_ring_size; 384 ipa_res->tx_num_alloc_buffer = 385 (uint32_t)soc->ipa_uc_tx_rsc.alloc_tx_buf_cnt; 386 387 ipa_res->tx_comp_ring_base_paddr = 388 soc->ipa_uc_tx_rsc.ipa_wbm_ring_base_paddr; 389 ipa_res->tx_comp_ring_size = 390 soc->ipa_uc_tx_rsc.ipa_wbm_ring_size; 391 392 ipa_res->rx_rdy_ring_base_paddr = 393 soc->ipa_uc_rx_rsc.ipa_reo_ring_base_paddr; 394 ipa_res->rx_rdy_ring_size = 395 soc->ipa_uc_rx_rsc.ipa_reo_ring_size; 396 397 ipa_res->rx_refill_ring_base_paddr = 398 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_base_paddr; 399 ipa_res->rx_refill_ring_size = 400 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_ring_size; 401 402 dp_debug("ipa_res->tx_ring_base_paddr:%pK ipa_res->tx_ring_size:%u ipa_res->tx_comp_ring_base_paddr:%pK ipa_res->tx_comp_ring_size:%u ipa_res->rx_refill_ring_base_paddr:%pK ipa_res->rx_refill_ring_size:%u", 403 (void *)ipa_res->tx_ring_base_paddr, 404 ipa_res->tx_ring_size, 405 (void *)ipa_res->tx_comp_ring_base_paddr, 406 ipa_res->tx_comp_ring_size, 407 (void *)ipa_res->rx_refill_ring_base_paddr, 408 ipa_res->rx_refill_ring_size); 409 410 if ((0 == ipa_res->tx_comp_ring_base_paddr) || 411 (0 == ipa_res->rx_rdy_ring_base_paddr)) 412 return QDF_STATUS_E_FAILURE; 413 414 return QDF_STATUS_SUCCESS; 415 } 416 417 /** 418 * dp_ipa_set_doorbell_paddr () - Set doorbell register physical address to SRNG 419 * @ppdev - handle to the device instance 420 * 421 * Set TX_COMP_DOORBELL register physical address to WBM Head_Ptr_MemAddr_LSB 422 * Set RX_READ_DOORBELL register physical address to REO Head_Ptr_MemAddr_LSB 423 * 424 * Return: none 425 */ 426 QDF_STATUS dp_ipa_set_doorbell_paddr(struct cdp_pdev *ppdev) 427 { 428 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 429 struct dp_soc *soc = pdev->soc; 430 struct dp_ipa_resources *ipa_res = &pdev->ipa_resource; 431 struct hal_srng *wbm_srng = 432 soc->tx_comp_ring[IPA_TX_COMP_RING_IDX].hal_srng; 433 struct hal_srng *reo_srng = 434 soc->reo_dest_ring[IPA_REO_DEST_RING_IDX].hal_srng; 435 436 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 437 return QDF_STATUS_SUCCESS; 438 439 hal_srng_dst_set_hp_paddr(wbm_srng, ipa_res->tx_comp_doorbell_paddr); 440 441 ipa_res->tx_comp_doorbell_vaddr = 442 ioremap(ipa_res->tx_comp_doorbell_paddr, 4); 443 dp_info("paddr %pK vaddr %pK", 444 (void *)ipa_res->tx_comp_doorbell_paddr, 445 (void *)ipa_res->tx_comp_doorbell_vaddr); 446 447 hal_srng_dst_init_hp(wbm_srng, ipa_res->tx_comp_doorbell_vaddr); 448 449 /* 450 * For RX, REO module on Napier/Hastings does reordering on incoming 451 * Ethernet packets and writes one or more descriptors to REO2IPA Rx 452 * ring.It then updates the ring’s Write/Head ptr and rings a doorbell 453 * to IPA. 454 * Set the doorbell addr for the REO ring. 455 */ 456 hal_srng_dst_set_hp_paddr(reo_srng, ipa_res->rx_ready_doorbell_paddr); 457 return QDF_STATUS_SUCCESS; 458 } 459 460 /** 461 * dp_ipa_op_response() - Handle OP command response from firmware 462 * @ppdev - handle to the device instance 463 * @op_msg: op response message from firmware 464 * 465 * Return: none 466 */ 467 QDF_STATUS dp_ipa_op_response(struct cdp_pdev *ppdev, uint8_t *op_msg) 468 { 469 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 470 471 if (!wlan_cfg_is_ipa_enabled(pdev->soc->wlan_cfg_ctx)) 472 return QDF_STATUS_SUCCESS; 473 474 if (pdev->ipa_uc_op_cb) { 475 pdev->ipa_uc_op_cb(op_msg, pdev->usr_ctxt); 476 } else { 477 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 478 "%s: IPA callback function is not registered", __func__); 479 qdf_mem_free(op_msg); 480 return QDF_STATUS_E_FAILURE; 481 } 482 483 return QDF_STATUS_SUCCESS; 484 } 485 486 /** 487 * dp_ipa_register_op_cb() - Register OP handler function 488 * @ppdev - handle to the device instance 489 * @op_cb: handler function pointer 490 * 491 * Return: none 492 */ 493 QDF_STATUS dp_ipa_register_op_cb(struct cdp_pdev *ppdev, 494 ipa_uc_op_cb_type op_cb, 495 void *usr_ctxt) 496 { 497 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 498 499 if (!wlan_cfg_is_ipa_enabled(pdev->soc->wlan_cfg_ctx)) 500 return QDF_STATUS_SUCCESS; 501 502 pdev->ipa_uc_op_cb = op_cb; 503 pdev->usr_ctxt = usr_ctxt; 504 505 return QDF_STATUS_SUCCESS; 506 } 507 508 /** 509 * dp_ipa_get_stat() - Get firmware wdi status 510 * @ppdev - handle to the device instance 511 * 512 * Return: none 513 */ 514 QDF_STATUS dp_ipa_get_stat(struct cdp_pdev *ppdev) 515 { 516 /* TBD */ 517 return QDF_STATUS_SUCCESS; 518 } 519 520 /** 521 * dp_tx_send_ipa_data_frame() - send IPA data frame 522 * @vdev: vdev 523 * @skb: skb 524 * 525 * Return: skb/ NULL is for success 526 */ 527 qdf_nbuf_t dp_tx_send_ipa_data_frame(struct cdp_vdev *vdev, qdf_nbuf_t skb) 528 { 529 qdf_nbuf_t ret; 530 531 /* Terminate the (single-element) list of tx frames */ 532 qdf_nbuf_set_next(skb, NULL); 533 ret = dp_tx_send((struct dp_vdev_t *)vdev, skb); 534 if (ret) { 535 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 536 "%s: Failed to tx", __func__); 537 return ret; 538 } 539 540 return NULL; 541 } 542 543 /** 544 * dp_ipa_enable_autonomy() – Enable autonomy RX path 545 * @pdev - handle to the device instance 546 * 547 * Set all RX packet route to IPA REO ring 548 * Program Destination_Ring_Ctrl_IX_0 REO register to point IPA REO ring 549 * Return: none 550 */ 551 QDF_STATUS dp_ipa_enable_autonomy(struct cdp_pdev *ppdev) 552 { 553 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 554 struct dp_soc *soc = pdev->soc; 555 uint32_t remap_val; 556 557 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 558 return QDF_STATUS_SUCCESS; 559 560 /* Call HAL API to remap REO rings to REO2IPA ring */ 561 remap_val = HAL_REO_REMAP_VAL(REO_REMAP_TCL, REO_REMAP_TCL) | 562 HAL_REO_REMAP_VAL(REO_REMAP_SW1, REO_REMAP_SW4) | 563 HAL_REO_REMAP_VAL(REO_REMAP_SW2, REO_REMAP_SW4) | 564 HAL_REO_REMAP_VAL(REO_REMAP_SW3, REO_REMAP_SW4) | 565 HAL_REO_REMAP_VAL(REO_REMAP_SW4, REO_REMAP_SW4) | 566 HAL_REO_REMAP_VAL(REO_REMAP_RELEASE, REO_REMAP_RELEASE) | 567 HAL_REO_REMAP_VAL(REO_REMAP_FW, REO_REMAP_FW) | 568 HAL_REO_REMAP_VAL(REO_REMAP_UNUSED, REO_REMAP_FW); 569 hal_reo_remap_IX0(soc->hal_soc, remap_val); 570 return QDF_STATUS_SUCCESS; 571 } 572 573 /** 574 * dp_ipa_disable_autonomy() – Disable autonomy RX path 575 * @ppdev - handle to the device instance 576 * 577 * Disable RX packet routing to IPA REO 578 * Program Destination_Ring_Ctrl_IX_0 REO register to disable 579 * Return: none 580 */ 581 QDF_STATUS dp_ipa_disable_autonomy(struct cdp_pdev *ppdev) 582 { 583 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 584 struct dp_soc *soc = pdev->soc; 585 uint32_t remap_val; 586 587 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 588 return QDF_STATUS_SUCCESS; 589 590 /* Call HAL API to remap REO rings to REO2IPA ring */ 591 remap_val = HAL_REO_REMAP_VAL(REO_REMAP_TCL, REO_REMAP_TCL) | 592 HAL_REO_REMAP_VAL(REO_REMAP_SW1, REO_REMAP_SW1) | 593 HAL_REO_REMAP_VAL(REO_REMAP_SW2, REO_REMAP_SW2) | 594 HAL_REO_REMAP_VAL(REO_REMAP_SW3, REO_REMAP_SW3) | 595 HAL_REO_REMAP_VAL(REO_REMAP_SW4, REO_REMAP_SW2) | 596 HAL_REO_REMAP_VAL(REO_REMAP_RELEASE, REO_REMAP_RELEASE) | 597 HAL_REO_REMAP_VAL(REO_REMAP_FW, REO_REMAP_FW) | 598 HAL_REO_REMAP_VAL(REO_REMAP_UNUSED, REO_REMAP_FW); 599 hal_reo_remap_IX0(soc->hal_soc, remap_val); 600 601 return QDF_STATUS_SUCCESS; 602 } 603 604 /* This should be configurable per H/W configuration enable status */ 605 #define L3_HEADER_PADDING 2 606 607 #ifdef CONFIG_IPA_WDI_UNIFIED_API 608 609 #ifndef QCA_LL_TX_FLOW_CONTROL_V2 610 static inline void dp_setup_mcc_sys_pipes( 611 qdf_ipa_sys_connect_params_t *sys_in, 612 qdf_ipa_wdi_conn_in_params_t *pipe_in) 613 { 614 /* Setup MCC sys pipe */ 615 QDF_IPA_WDI_CONN_IN_PARAMS_NUM_SYS_PIPE_NEEDED(pipe_in) = 616 DP_IPA_MAX_IFACE; 617 for (int i = 0; i < DP_IPA_MAX_IFACE; i++) 618 memcpy(&QDF_IPA_WDI_CONN_IN_PARAMS_SYS_IN(pipe_in)[i], 619 &sys_in[i], sizeof(qdf_ipa_sys_connect_params_t)); 620 } 621 #else 622 static inline void dp_setup_mcc_sys_pipes( 623 qdf_ipa_sys_connect_params_t *sys_in, 624 qdf_ipa_wdi_conn_in_params_t *pipe_in) 625 { 626 QDF_IPA_WDI_CONN_IN_PARAMS_NUM_SYS_PIPE_NEEDED(pipe_in) = 0; 627 } 628 #endif 629 630 /** 631 * dp_ipa_setup() - Setup and connect IPA pipes 632 * @ppdev - handle to the device instance 633 * @ipa_i2w_cb: IPA to WLAN callback 634 * @ipa_w2i_cb: WLAN to IPA callback 635 * @ipa_wdi_meter_notifier_cb: IPA WDI metering callback 636 * @ipa_desc_size: IPA descriptor size 637 * @ipa_priv: handle to the HTT instance 638 * @is_rm_enabled: Is IPA RM enabled or not 639 * @tx_pipe_handle: pointer to Tx pipe handle 640 * @rx_pipe_handle: pointer to Rx pipe handle 641 * @is_smmu_enabled: Is SMMU enabled or not 642 * @sys_in: parameters to setup sys pipe in mcc mode 643 * 644 * Return: QDF_STATUS 645 */ 646 QDF_STATUS dp_ipa_setup(struct cdp_pdev *ppdev, void *ipa_i2w_cb, 647 void *ipa_w2i_cb, void *ipa_wdi_meter_notifier_cb, 648 uint32_t ipa_desc_size, void *ipa_priv, 649 bool is_rm_enabled, uint32_t *tx_pipe_handle, 650 uint32_t *rx_pipe_handle, bool is_smmu_enabled, 651 qdf_ipa_sys_connect_params_t *sys_in) 652 { 653 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 654 struct dp_soc *soc = pdev->soc; 655 struct dp_ipa_resources *ipa_res = &pdev->ipa_resource; 656 qdf_ipa_ep_cfg_t *tx_cfg; 657 qdf_ipa_ep_cfg_t *rx_cfg; 658 qdf_ipa_wdi_pipe_setup_info_t *tx; 659 qdf_ipa_wdi_pipe_setup_info_t *rx; 660 qdf_ipa_wdi_pipe_setup_info_smmu_t *tx_smmu; 661 qdf_ipa_wdi_pipe_setup_info_smmu_t *rx_smmu; 662 qdf_ipa_wdi_conn_in_params_t pipe_in; 663 qdf_ipa_wdi_conn_out_params_t pipe_out; 664 struct tcl_data_cmd *tcl_desc_ptr; 665 uint8_t *desc_addr; 666 uint32_t desc_size; 667 int ret; 668 669 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 670 return QDF_STATUS_SUCCESS; 671 672 673 qdf_mem_zero(&tx, sizeof(qdf_ipa_wdi_pipe_setup_info_t)); 674 qdf_mem_zero(&rx, sizeof(qdf_ipa_wdi_pipe_setup_info_t)); 675 qdf_mem_zero(&pipe_in, sizeof(pipe_in)); 676 qdf_mem_zero(&pipe_out, sizeof(pipe_out)); 677 678 if (is_smmu_enabled) 679 QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in) = true; 680 else 681 QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in) = false; 682 683 dp_setup_mcc_sys_pipes(sys_in, &pipe_in); 684 685 /* TX PIPE */ 686 if (QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in)) { 687 tx_smmu = &QDF_IPA_WDI_CONN_IN_PARAMS_TX_SMMU(&pipe_in); 688 tx_cfg = &QDF_IPA_WDI_SETUP_INFO_SMMU_EP_CFG(tx_smmu); 689 } else { 690 tx = &QDF_IPA_WDI_CONN_IN_PARAMS_TX(&pipe_in); 691 tx_cfg = &QDF_IPA_WDI_SETUP_INFO_EP_CFG(tx); 692 } 693 694 QDF_IPA_EP_CFG_NAT_EN(tx_cfg) = IPA_BYPASS_NAT; 695 QDF_IPA_EP_CFG_HDR_LEN(tx_cfg) = DP_IPA_UC_WLAN_TX_HDR_LEN; 696 QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE_VALID(tx_cfg) = 0; 697 QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE(tx_cfg) = 0; 698 QDF_IPA_EP_CFG_HDR_ADDITIONAL_CONST_LEN(tx_cfg) = 0; 699 QDF_IPA_EP_CFG_MODE(tx_cfg) = IPA_BASIC; 700 QDF_IPA_EP_CFG_HDR_LITTLE_ENDIAN(tx_cfg) = true; 701 702 /** 703 * Transfer Ring: WBM Ring 704 * Transfer Ring Doorbell PA: WBM Tail Pointer Address 705 * Event Ring: TCL ring 706 * Event Ring Doorbell PA: TCL Head Pointer Address 707 */ 708 if (QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in)) { 709 /* TODO: SMMU implementation on WDI3 */ 710 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 711 "%s: SMMU is not implementation on host", __func__); 712 return QDF_STATUS_E_FAILURE; 713 } 714 715 QDF_IPA_WDI_SETUP_INFO_CLIENT(tx) = IPA_CLIENT_WLAN1_CONS; 716 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(tx) = 717 ipa_res->tx_comp_ring_base_paddr; 718 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(tx) = 719 ipa_res->tx_comp_ring_size; 720 /* WBM Tail Pointer Address */ 721 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(tx) = 722 soc->ipa_uc_tx_rsc.ipa_wbm_tp_paddr; 723 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(tx) = 724 ipa_res->tx_ring_base_paddr; 725 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(tx) = 726 ipa_res->tx_ring_size; 727 /* TCL Head Pointer Address */ 728 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(tx) = 729 soc->ipa_uc_tx_rsc.ipa_tcl_hp_paddr; 730 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(tx) = 731 ipa_res->tx_num_alloc_buffer; 732 QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(tx) = 0; 733 734 /* Preprogram TCL descriptor */ 735 desc_addr = 736 (uint8_t *)QDF_IPA_WDI_SETUP_INFO_DESC_FORMAT_TEMPLATE(tx); 737 desc_size = sizeof(struct tcl_data_cmd); 738 HAL_TX_DESC_SET_TLV_HDR(desc_addr, HAL_TX_TCL_DATA_TAG, desc_size); 739 tcl_desc_ptr = (struct tcl_data_cmd *) 740 (QDF_IPA_WDI_SETUP_INFO_DESC_FORMAT_TEMPLATE(tx) + 1); 741 tcl_desc_ptr->buf_addr_info.return_buffer_manager = 742 HAL_RX_BUF_RBM_SW2_BM; 743 tcl_desc_ptr->addrx_en = 1; /* Address X search enable in ASE */ 744 tcl_desc_ptr->encap_type = HAL_TX_ENCAP_TYPE_ETHERNET; 745 tcl_desc_ptr->packet_offset = 2; /* padding for alignment */ 746 747 748 /* RX PIPE */ 749 if (QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in)) { 750 rx_smmu = &QDF_IPA_WDI_CONN_IN_PARAMS_RX_SMMU(&pipe_in); 751 rx_cfg = &QDF_IPA_WDI_SETUP_INFO_SMMU_EP_CFG(rx_smmu); 752 } else { 753 rx = &QDF_IPA_WDI_CONN_IN_PARAMS_RX(&pipe_in); 754 rx_cfg = &QDF_IPA_WDI_SETUP_INFO_EP_CFG(rx); 755 } 756 757 QDF_IPA_EP_CFG_NAT_EN(rx_cfg) = IPA_BYPASS_NAT; 758 QDF_IPA_EP_CFG_HDR_LEN(rx_cfg) = DP_IPA_UC_WLAN_RX_HDR_LEN; 759 QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE_VALID(rx_cfg) = 1; 760 QDF_IPA_EP_CFG_HDR_OFST_PKT_SIZE(rx_cfg) = 0; 761 QDF_IPA_EP_CFG_HDR_ADDITIONAL_CONST_LEN(rx_cfg) = 0; 762 QDF_IPA_EP_CFG_HDR_OFST_METADATA_VALID(rx_cfg) = 0; 763 QDF_IPA_EP_CFG_HDR_METADATA_REG_VALID(rx_cfg) = 1; 764 QDF_IPA_EP_CFG_MODE(rx_cfg) = IPA_BASIC; 765 QDF_IPA_EP_CFG_HDR_LITTLE_ENDIAN(rx_cfg) = true; 766 767 /** 768 * Transfer Ring: REO Ring 769 * Transfer Ring Doorbell PA: REO Tail Pointer Address 770 * Event Ring: FW ring 771 * Event Ring Doorbell PA: FW Head Pointer Address 772 */ 773 if (QDF_IPA_WDI_CONN_IN_PARAMS_SMMU_ENABLED(&pipe_in)) { 774 /* TODO: SMMU implementation on WDI3 */ 775 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 776 "%s: SMMU is not implementation on host", __func__); 777 return QDF_STATUS_E_FAILURE; 778 } else { 779 QDF_IPA_WDI_SETUP_INFO_CLIENT(rx) = IPA_CLIENT_WLAN1_PROD; 780 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx) = 781 ipa_res->rx_rdy_ring_base_paddr; 782 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx) = 783 ipa_res->rx_rdy_ring_size; 784 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx) = 785 /* REO Tail Pointer Address */ 786 soc->ipa_uc_rx_rsc.ipa_reo_tp_paddr; 787 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx) = 788 ipa_res->rx_refill_ring_base_paddr; 789 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx) = 790 ipa_res->rx_refill_ring_size; 791 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx) = 792 /* FW Head Pointer Address */ 793 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_hp_paddr; 794 QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx) = 795 RX_PKT_TLVS_LEN + L3_HEADER_PADDING; 796 } 797 798 QDF_IPA_WDI_CONN_IN_PARAMS_NOTIFY(&pipe_in) = ipa_w2i_cb; 799 QDF_IPA_WDI_CONN_IN_PARAMS_PRIV(&pipe_in) = ipa_priv; 800 801 /* Connect WDI IPA PIPEs */ 802 ret = qdf_ipa_wdi_conn_pipes(&pipe_in, &pipe_out); 803 804 if (ret) { 805 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 806 "%s: ipa_wdi_conn_pipes: IPA pipe setup failed: ret=%d", 807 __func__, ret); 808 return QDF_STATUS_E_FAILURE; 809 } 810 811 /* IPA uC Doorbell registers */ 812 dp_info("Tx DB PA=0x%x, Rx DB PA=0x%x", 813 (unsigned int)QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out), 814 (unsigned int)QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out)); 815 816 ipa_res->tx_comp_doorbell_paddr = 817 QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out); 818 ipa_res->rx_ready_doorbell_paddr = 819 QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out); 820 821 dp_info("Tx: %s=%pK, %s=%d, %s=%pK, %s=%pK, %s=%d, %s=%pK, %s=%d, %s=%pK %s=%pK", 822 "transfer_ring_base_pa", 823 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(tx), 824 "transfer_ring_size", 825 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(tx), 826 "transfer_ring_doorbell_pa", 827 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(tx), 828 "event_ring_base_pa", 829 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(tx), 830 "event_ring_size", 831 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(tx), 832 "event_ring_doorbell_pa", 833 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(tx), 834 "num_pkt_buffers", 835 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(tx), 836 "tx_comp_doorbell_paddr", 837 (void *)ipa_res->tx_comp_doorbell_paddr, 838 "tx_comp_doorbell_vaddr", 839 (void *)ipa_res->tx_comp_doorbell_vaddr); 840 841 dp_info("Rx: %s=%pK, %s=%d, %s=%pK, %s=%pK, %s=%d, %s=%pK, %s=%d, %s=%u, %s=%pK", 842 "transfer_ring_base_pa", 843 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx), 844 "transfer_ring_size", 845 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx), 846 "transfer_ring_doorbell_pa", 847 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx), 848 "event_ring_base_pa", 849 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx), 850 "event_ring_size", 851 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx), 852 "event_ring_doorbell_pa", 853 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx), 854 "num_pkt_buffers", 855 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(rx), 856 "pkt_offset(rx)", 857 QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx), 858 "tx_comp_doorbell_paddr", 859 (void *)ipa_res->rx_ready_doorbell_paddr); 860 861 return QDF_STATUS_SUCCESS; 862 } 863 864 /** 865 * dp_ipa_setup_iface() - Setup IPA header and register interface 866 * @ifname: Interface name 867 * @mac_addr: Interface MAC address 868 * @prod_client: IPA prod client type 869 * @cons_client: IPA cons client type 870 * @session_id: Session ID 871 * @is_ipv6_enabled: Is IPV6 enabled or not 872 * 873 * Return: QDF_STATUS 874 */ 875 QDF_STATUS dp_ipa_setup_iface(char *ifname, uint8_t *mac_addr, 876 qdf_ipa_client_type_t prod_client, 877 qdf_ipa_client_type_t cons_client, 878 uint8_t session_id, bool is_ipv6_enabled) 879 { 880 qdf_ipa_wdi_reg_intf_in_params_t in; 881 qdf_ipa_wdi_hdr_info_t hdr_info; 882 struct dp_ipa_uc_tx_hdr uc_tx_hdr; 883 struct dp_ipa_uc_tx_hdr uc_tx_hdr_v6; 884 int ret = -EINVAL; 885 886 dp_debug("Add Partial hdr: %s, %pM", ifname, mac_addr); 887 qdf_mem_zero(&hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 888 qdf_ether_addr_copy(uc_tx_hdr.eth.h_source, mac_addr); 889 890 /* IPV4 header */ 891 uc_tx_hdr.eth.h_proto = qdf_htons(ETH_P_IP); 892 893 QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr; 894 QDF_IPA_WDI_HDR_INFO_HDR_LEN(&hdr_info) = DP_IPA_UC_WLAN_TX_HDR_LEN; 895 QDF_IPA_WDI_HDR_INFO_HDR_TYPE(&hdr_info) = IPA_HDR_L2_ETHERNET_II; 896 QDF_IPA_WDI_HDR_INFO_DST_MAC_ADDR_OFFSET(&hdr_info) = 897 DP_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; 898 899 QDF_IPA_WDI_REG_INTF_IN_PARAMS_NETDEV_NAME(&in) = ifname; 900 qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v4]), 901 &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 902 QDF_IPA_WDI_REG_INTF_IN_PARAMS_ALT_DST_PIPE(&in) = cons_client; 903 QDF_IPA_WDI_REG_INTF_IN_PARAMS_IS_META_DATA_VALID(&in) = 1; 904 QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA(&in) = 905 htonl(session_id << 16); 906 QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA_MASK(&in) = htonl(0x00FF0000); 907 908 /* IPV6 header */ 909 if (is_ipv6_enabled) { 910 qdf_mem_copy(&uc_tx_hdr_v6, &uc_tx_hdr, 911 DP_IPA_UC_WLAN_TX_HDR_LEN); 912 uc_tx_hdr_v6.eth.h_proto = qdf_htons(ETH_P_IPV6); 913 QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr_v6; 914 qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v6]), 915 &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 916 } 917 918 dp_debug("registering for session_id: %u", session_id); 919 920 ret = qdf_ipa_wdi_reg_intf(&in); 921 922 if (ret) { 923 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 924 "%s: ipa_wdi_reg_intf: register IPA interface falied: ret=%d", 925 __func__, ret); 926 return QDF_STATUS_E_FAILURE; 927 } 928 929 return QDF_STATUS_SUCCESS; 930 } 931 932 #else /* CONFIG_IPA_WDI_UNIFIED_API */ 933 934 /** 935 * dp_ipa_setup() - Setup and connect IPA pipes 936 * @ppdev - handle to the device instance 937 * @ipa_i2w_cb: IPA to WLAN callback 938 * @ipa_w2i_cb: WLAN to IPA callback 939 * @ipa_wdi_meter_notifier_cb: IPA WDI metering callback 940 * @ipa_desc_size: IPA descriptor size 941 * @ipa_priv: handle to the HTT instance 942 * @is_rm_enabled: Is IPA RM enabled or not 943 * @tx_pipe_handle: pointer to Tx pipe handle 944 * @rx_pipe_handle: pointer to Rx pipe handle 945 * 946 * Return: QDF_STATUS 947 */ 948 QDF_STATUS dp_ipa_setup(struct cdp_pdev *ppdev, void *ipa_i2w_cb, 949 void *ipa_w2i_cb, void *ipa_wdi_meter_notifier_cb, 950 uint32_t ipa_desc_size, void *ipa_priv, 951 bool is_rm_enabled, uint32_t *tx_pipe_handle, 952 uint32_t *rx_pipe_handle) 953 { 954 struct dp_pdev *pdev = (struct dp_pdev *)ppdev; 955 struct dp_soc *soc = pdev->soc; 956 struct dp_ipa_resources *ipa_res = &pdev->ipa_resource; 957 qdf_ipa_wdi_pipe_setup_info_t *tx; 958 qdf_ipa_wdi_pipe_setup_info_t *rx; 959 qdf_ipa_wdi_conn_in_params_t pipe_in; 960 qdf_ipa_wdi_conn_out_params_t pipe_out; 961 struct tcl_data_cmd *tcl_desc_ptr; 962 uint8_t *desc_addr; 963 uint32_t desc_size; 964 int ret; 965 966 if (!wlan_cfg_is_ipa_enabled(soc->wlan_cfg_ctx)) 967 return QDF_STATUS_SUCCESS; 968 969 qdf_mem_zero(&tx, sizeof(qdf_ipa_wdi_pipe_setup_info_t)); 970 qdf_mem_zero(&rx, sizeof(qdf_ipa_wdi_pipe_setup_info_t)); 971 qdf_mem_zero(&pipe_in, sizeof(pipe_in)); 972 qdf_mem_zero(&pipe_out, sizeof(pipe_out)); 973 974 /* TX PIPE */ 975 /** 976 * Transfer Ring: WBM Ring 977 * Transfer Ring Doorbell PA: WBM Tail Pointer Address 978 * Event Ring: TCL ring 979 * Event Ring Doorbell PA: TCL Head Pointer Address 980 */ 981 tx = &QDF_IPA_WDI_CONN_IN_PARAMS_TX(&pipe_in); 982 QDF_IPA_WDI_SETUP_INFO_NAT_EN(tx) = IPA_BYPASS_NAT; 983 QDF_IPA_WDI_SETUP_INFO_HDR_LEN(tx) = DP_IPA_UC_WLAN_TX_HDR_LEN; 984 QDF_IPA_WDI_SETUP_INFO_HDR_OFST_PKT_SIZE_VALID(tx) = 0; 985 QDF_IPA_WDI_SETUP_INFO_HDR_OFST_PKT_SIZE(tx) = 0; 986 QDF_IPA_WDI_SETUP_INFO_HDR_ADDITIONAL_CONST_LEN(tx) = 0; 987 QDF_IPA_WDI_SETUP_INFO_MODE(tx) = IPA_BASIC; 988 QDF_IPA_WDI_SETUP_INFO_HDR_LITTLE_ENDIAN(tx) = true; 989 QDF_IPA_WDI_SETUP_INFO_CLIENT(tx) = IPA_CLIENT_WLAN1_CONS; 990 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(tx) = 991 ipa_res->tx_comp_ring_base_paddr; 992 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(tx) = 993 ipa_res->tx_comp_ring_size; 994 /* WBM Tail Pointer Address */ 995 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(tx) = 996 soc->ipa_uc_tx_rsc.ipa_wbm_tp_paddr; 997 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(tx) = 998 ipa_res->tx_ring_base_paddr; 999 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(tx) = ipa_res->tx_ring_size; 1000 /* TCL Head Pointer Address */ 1001 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(tx) = 1002 soc->ipa_uc_tx_rsc.ipa_tcl_hp_paddr; 1003 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(tx) = 1004 ipa_res->tx_num_alloc_buffer; 1005 QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(tx) = 0; 1006 1007 /* Preprogram TCL descriptor */ 1008 desc_addr = 1009 (uint8_t *)QDF_IPA_WDI_SETUP_INFO_DESC_FORMAT_TEMPLATE(tx); 1010 desc_size = sizeof(struct tcl_data_cmd); 1011 HAL_TX_DESC_SET_TLV_HDR(desc_addr, HAL_TX_TCL_DATA_TAG, desc_size); 1012 tcl_desc_ptr = (struct tcl_data_cmd *) 1013 (QDF_IPA_WDI_SETUP_INFO_DESC_FORMAT_TEMPLATE(tx) + 1); 1014 tcl_desc_ptr->buf_addr_info.return_buffer_manager = 1015 HAL_RX_BUF_RBM_SW2_BM; 1016 tcl_desc_ptr->addrx_en = 1; /* Address X search enable in ASE */ 1017 tcl_desc_ptr->encap_type = HAL_TX_ENCAP_TYPE_ETHERNET; 1018 tcl_desc_ptr->packet_offset = 2; /* padding for alignment */ 1019 1020 /* RX PIPE */ 1021 /** 1022 * Transfer Ring: REO Ring 1023 * Transfer Ring Doorbell PA: REO Tail Pointer Address 1024 * Event Ring: FW ring 1025 * Event Ring Doorbell PA: FW Head Pointer Address 1026 */ 1027 rx = &QDF_IPA_WDI_CONN_IN_PARAMS_RX(&pipe_in); 1028 QDF_IPA_WDI_SETUP_INFO_NAT_EN(rx) = IPA_BYPASS_NAT; 1029 QDF_IPA_WDI_SETUP_INFO_HDR_LEN(rx) = DP_IPA_UC_WLAN_RX_HDR_LEN; 1030 QDF_IPA_WDI_SETUP_INFO_HDR_OFST_PKT_SIZE_VALID(rx) = 0; 1031 QDF_IPA_WDI_SETUP_INFO_HDR_OFST_PKT_SIZE(rx) = 0; 1032 QDF_IPA_WDI_SETUP_INFO_HDR_ADDITIONAL_CONST_LEN(rx) = 0; 1033 QDF_IPA_WDI_SETUP_INFO_HDR_OFST_METADATA_VALID(rx) = 0; 1034 QDF_IPA_WDI_SETUP_INFO_HDR_METADATA_REG_VALID(rx) = 1; 1035 QDF_IPA_WDI_SETUP_INFO_MODE(rx) = IPA_BASIC; 1036 QDF_IPA_WDI_SETUP_INFO_HDR_LITTLE_ENDIAN(rx) = true; 1037 QDF_IPA_WDI_SETUP_INFO_CLIENT(rx) = IPA_CLIENT_WLAN1_PROD; 1038 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx) = 1039 ipa_res->rx_rdy_ring_base_paddr; 1040 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx) = 1041 ipa_res->rx_rdy_ring_size; 1042 /* REO Tail Pointer Address */ 1043 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx) = 1044 soc->ipa_uc_rx_rsc.ipa_reo_tp_paddr; 1045 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx) = 1046 ipa_res->rx_refill_ring_base_paddr; 1047 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx) = 1048 ipa_res->rx_refill_ring_size; 1049 /* FW Head Pointer Address */ 1050 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx) = 1051 soc->ipa_uc_rx_rsc.ipa_rx_refill_buf_hp_paddr; 1052 QDF_IPA_WDI_SETUP_INFO_PKT_OFFSET(rx) = RX_PKT_TLVS_LEN + 1053 L3_HEADER_PADDING; 1054 QDF_IPA_WDI_CONN_IN_PARAMS_NOTIFY(&pipe_in) = ipa_w2i_cb; 1055 QDF_IPA_WDI_CONN_IN_PARAMS_PRIV(&pipe_in) = ipa_priv; 1056 1057 /* Connect WDI IPA PIPE */ 1058 ret = qdf_ipa_wdi_conn_pipes(&pipe_in, &pipe_out); 1059 if (ret) { 1060 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 1061 "%s: ipa_wdi_conn_pipes: IPA pipe setup failed: ret=%d", 1062 __func__, ret); 1063 return QDF_STATUS_E_FAILURE; 1064 } 1065 1066 /* IPA uC Doorbell registers */ 1067 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, 1068 "%s: Tx DB PA=0x%x, Rx DB PA=0x%x", 1069 __func__, 1070 (unsigned int)QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out), 1071 (unsigned int)QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out)); 1072 1073 ipa_res->tx_comp_doorbell_paddr = 1074 QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_PA(&pipe_out); 1075 ipa_res->tx_comp_doorbell_vaddr = 1076 QDF_IPA_WDI_CONN_OUT_PARAMS_TX_UC_DB_VA(&pipe_out); 1077 ipa_res->rx_ready_doorbell_paddr = 1078 QDF_IPA_WDI_CONN_OUT_PARAMS_RX_UC_DB_PA(&pipe_out); 1079 1080 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, 1081 "%s: Tx: %s=%pK, %s=%d, %s=%pK, %s=%pK, %s=%d, %s=%pK, %s=%d, %s=%pK", 1082 __func__, 1083 "transfer_ring_base_pa", 1084 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(tx), 1085 "transfer_ring_size", 1086 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(tx), 1087 "transfer_ring_doorbell_pa", 1088 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(tx), 1089 "event_ring_base_pa", 1090 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(tx), 1091 "event_ring_size", 1092 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(tx), 1093 "event_ring_doorbell_pa", 1094 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(tx), 1095 "num_pkt_buffers", 1096 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(tx), 1097 "tx_comp_doorbell_paddr", 1098 (void *)ipa_res->tx_comp_doorbell_paddr); 1099 1100 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, 1101 "%s: Rx: %s=%pK, %s=%d, %s=%pK, %s=%pK, %s=%d, %s=%pK, %s=%d, %s=%pK", 1102 __func__, 1103 "transfer_ring_base_pa", 1104 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_BASE_PA(rx), 1105 "transfer_ring_size", 1106 QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_SIZE(rx), 1107 "transfer_ring_doorbell_pa", 1108 (void *)QDF_IPA_WDI_SETUP_INFO_TRANSFER_RING_DOORBELL_PA(rx), 1109 "event_ring_base_pa", 1110 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_BASE_PA(rx), 1111 "event_ring_size", 1112 QDF_IPA_WDI_SETUP_INFO_EVENT_RING_SIZE(rx), 1113 "event_ring_doorbell_pa", 1114 (void *)QDF_IPA_WDI_SETUP_INFO_EVENT_RING_DOORBELL_PA(rx), 1115 "num_pkt_buffers", 1116 QDF_IPA_WDI_SETUP_INFO_NUM_PKT_BUFFERS(rx), 1117 "tx_comp_doorbell_paddr", 1118 (void *)ipa_res->rx_ready_doorbell_paddr); 1119 1120 return QDF_STATUS_SUCCESS; 1121 } 1122 1123 /** 1124 * dp_ipa_setup_iface() - Setup IPA header and register interface 1125 * @ifname: Interface name 1126 * @mac_addr: Interface MAC address 1127 * @prod_client: IPA prod client type 1128 * @cons_client: IPA cons client type 1129 * @session_id: Session ID 1130 * @is_ipv6_enabled: Is IPV6 enabled or not 1131 * 1132 * Return: QDF_STATUS 1133 */ 1134 QDF_STATUS dp_ipa_setup_iface(char *ifname, uint8_t *mac_addr, 1135 qdf_ipa_client_type_t prod_client, 1136 qdf_ipa_client_type_t cons_client, 1137 uint8_t session_id, bool is_ipv6_enabled) 1138 { 1139 qdf_ipa_wdi_reg_intf_in_params_t in; 1140 qdf_ipa_wdi_hdr_info_t hdr_info; 1141 struct dp_ipa_uc_tx_hdr uc_tx_hdr; 1142 struct dp_ipa_uc_tx_hdr uc_tx_hdr_v6; 1143 int ret = -EINVAL; 1144 1145 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, 1146 "%s: Add Partial hdr: %s, %pM", 1147 __func__, ifname, mac_addr); 1148 1149 qdf_mem_zero(&hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 1150 qdf_ether_addr_copy(uc_tx_hdr.eth.h_source, mac_addr); 1151 1152 /* IPV4 header */ 1153 uc_tx_hdr.eth.h_proto = qdf_htons(ETH_P_IP); 1154 1155 QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr; 1156 QDF_IPA_WDI_HDR_INFO_HDR_LEN(&hdr_info) = DP_IPA_UC_WLAN_TX_HDR_LEN; 1157 QDF_IPA_WDI_HDR_INFO_HDR_TYPE(&hdr_info) = IPA_HDR_L2_ETHERNET_II; 1158 QDF_IPA_WDI_HDR_INFO_DST_MAC_ADDR_OFFSET(&hdr_info) = 1159 DP_IPA_UC_WLAN_HDR_DES_MAC_OFFSET; 1160 1161 QDF_IPA_WDI_REG_INTF_IN_PARAMS_NETDEV_NAME(&in) = ifname; 1162 qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v4]), 1163 &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 1164 QDF_IPA_WDI_REG_INTF_IN_PARAMS_IS_META_DATA_VALID(&in) = 1; 1165 QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA(&in) = 1166 htonl(session_id << 16); 1167 QDF_IPA_WDI_REG_INTF_IN_PARAMS_META_DATA_MASK(&in) = htonl(0x00FF0000); 1168 1169 /* IPV6 header */ 1170 if (is_ipv6_enabled) { 1171 qdf_mem_copy(&uc_tx_hdr_v6, &uc_tx_hdr, 1172 DP_IPA_UC_WLAN_TX_HDR_LEN); 1173 uc_tx_hdr_v6.eth.h_proto = qdf_htons(ETH_P_IPV6); 1174 QDF_IPA_WDI_HDR_INFO_HDR(&hdr_info) = (uint8_t *)&uc_tx_hdr_v6; 1175 qdf_mem_copy(&(QDF_IPA_WDI_REG_INTF_IN_PARAMS_HDR_INFO(&in)[IPA_IP_v6]), 1176 &hdr_info, sizeof(qdf_ipa_wdi_hdr_info_t)); 1177 } 1178 1179 ret = qdf_ipa_wdi_reg_intf(&in); 1180 if (ret) { 1181 dp_err("ipa_wdi_reg_intf: register IPA interface falied: ret=%d", 1182 ret); 1183 return QDF_STATUS_E_FAILURE; 1184 } 1185 1186 return QDF_STATUS_SUCCESS; 1187 } 1188 1189 #endif /* CONFIG_IPA_WDI_UNIFIED_API */ 1190 1191 /** 1192 * dp_ipa_cleanup() - Disconnect IPA pipes 1193 * @tx_pipe_handle: Tx pipe handle 1194 * @rx_pipe_handle: Rx pipe handle 1195 * 1196 * Return: QDF_STATUS 1197 */ 1198 QDF_STATUS dp_ipa_cleanup(uint32_t tx_pipe_handle, uint32_t rx_pipe_handle) 1199 { 1200 int ret; 1201 1202 ret = qdf_ipa_wdi_disconn_pipes(); 1203 if (ret) { 1204 dp_err("ipa_wdi_disconn_pipes: IPA pipe cleanup failed: ret=%d", 1205 ret); 1206 return QDF_STATUS_E_FAILURE; 1207 } 1208 1209 return QDF_STATUS_SUCCESS; 1210 } 1211 1212 /** 1213 * dp_ipa_cleanup_iface() - Cleanup IPA header and deregister interface 1214 * @ifname: Interface name 1215 * @is_ipv6_enabled: Is IPV6 enabled or not 1216 * 1217 * Return: QDF_STATUS 1218 */ 1219 QDF_STATUS dp_ipa_cleanup_iface(char *ifname, bool is_ipv6_enabled) 1220 { 1221 int ret; 1222 1223 ret = qdf_ipa_wdi_dereg_intf(ifname); 1224 if (ret) { 1225 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 1226 "%s: ipa_wdi_dereg_intf: IPA pipe deregistration failed: ret=%d", 1227 __func__, ret); 1228 return QDF_STATUS_E_FAILURE; 1229 } 1230 1231 return QDF_STATUS_SUCCESS; 1232 } 1233 1234 /** 1235 * dp_ipa_uc_enable_pipes() - Enable and resume traffic on Tx/Rx pipes 1236 * @ppdev - handle to the device instance 1237 * 1238 * Return: QDF_STATUS 1239 */ 1240 QDF_STATUS dp_ipa_enable_pipes(struct cdp_pdev *ppdev) 1241 { 1242 QDF_STATUS result; 1243 1244 result = qdf_ipa_wdi_enable_pipes(); 1245 if (result) { 1246 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 1247 "%s: Enable WDI PIPE fail, code %d", 1248 __func__, result); 1249 return QDF_STATUS_E_FAILURE; 1250 } 1251 1252 return QDF_STATUS_SUCCESS; 1253 } 1254 1255 /** 1256 * dp_ipa_uc_disable_pipes() – Suspend traffic and disable Tx/Rx pipes 1257 * @ppdev - handle to the device instance 1258 * 1259 * Return: QDF_STATUS 1260 */ 1261 QDF_STATUS dp_ipa_disable_pipes(struct cdp_pdev *ppdev) 1262 { 1263 QDF_STATUS result; 1264 1265 result = qdf_ipa_wdi_disable_pipes(); 1266 if (result) { 1267 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 1268 "%s: Disable WDI PIPE fail, code %d", 1269 __func__, result); 1270 return QDF_STATUS_E_FAILURE; 1271 } 1272 1273 return QDF_STATUS_SUCCESS; 1274 } 1275 1276 /** 1277 * dp_ipa_set_perf_level() - Set IPA clock bandwidth based on data rates 1278 * @client: Client type 1279 * @max_supported_bw_mbps: Maximum bandwidth needed (in Mbps) 1280 * 1281 * Return: QDF_STATUS 1282 */ 1283 QDF_STATUS dp_ipa_set_perf_level(int client, uint32_t max_supported_bw_mbps) 1284 { 1285 qdf_ipa_wdi_perf_profile_t profile; 1286 QDF_STATUS result; 1287 1288 profile.client = client; 1289 profile.max_supported_bw_mbps = max_supported_bw_mbps; 1290 1291 result = qdf_ipa_wdi_set_perf_profile(&profile); 1292 if (result) { 1293 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, 1294 "%s: ipa_wdi_set_perf_profile fail, code %d", 1295 __func__, result); 1296 return QDF_STATUS_E_FAILURE; 1297 } 1298 1299 return QDF_STATUS_SUCCESS; 1300 } 1301 1302 #endif 1303