1 /* 2 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 #include <dp_internal.h> 17 #include <wlan_cfg.h> 18 #include <hif.h> 19 #include <dp_htt.h> 20 21 /** 22 * dp_get_umac_reset_intr_ctx() - Get the interrupt context to be used by 23 * UMAC reset feature 24 * @soc: DP soc object 25 * @intr_ctx: Interrupt context variable to be populated by this API 26 * 27 * Return: QDF_STATUS of operation 28 */ 29 static QDF_STATUS dp_get_umac_reset_intr_ctx(struct dp_soc *soc, int *intr_ctx) 30 { 31 int umac_reset_mask, i; 32 33 /** 34 * Go over all the contexts and check which interrupt context has 35 * the UMAC reset mask set. 36 */ 37 for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) { 38 umac_reset_mask = wlan_cfg_get_umac_reset_intr_mask( 39 soc->wlan_cfg_ctx, i); 40 41 if (umac_reset_mask) { 42 *intr_ctx = i; 43 return QDF_STATUS_SUCCESS; 44 } 45 } 46 47 *intr_ctx = -1; 48 return QDF_STATUS_E_FAILURE; 49 } 50 51 /** 52 * dp_umac_reset_send_setup_cmd(): Send the UMAC reset setup command 53 * @soc: dp soc object 54 * 55 * Return: QDF_STATUS of operation 56 */ 57 static QDF_STATUS 58 dp_umac_reset_send_setup_cmd(struct dp_soc *soc) 59 { 60 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 61 int msi_vector_count, ret; 62 uint32_t msi_base_data, msi_vector_start; 63 struct dp_htt_umac_reset_setup_cmd_params params; 64 65 umac_reset_ctx = &soc->umac_reset_ctx; 66 qdf_mem_zero(¶ms, sizeof(params)); 67 ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP", 68 &msi_vector_count, &msi_base_data, 69 &msi_vector_start); 70 if (ret) { 71 params.msi_data = UMAC_RESET_IPC; 72 } else { 73 params.msi_data = (umac_reset_ctx->intr_offset % 74 msi_vector_count) + msi_base_data; 75 } 76 77 params.shmem_addr_low = 78 qdf_get_lower_32_bits(umac_reset_ctx->shmem_paddr_aligned); 79 params.shmem_addr_high = 80 qdf_get_upper_32_bits(umac_reset_ctx->shmem_paddr_aligned); 81 82 return dp_htt_umac_reset_send_setup_cmd(soc, ¶ms); 83 } 84 85 QDF_STATUS dp_soc_umac_reset_init(struct cdp_soc_t *txrx_soc) 86 { 87 struct dp_soc *soc = (struct dp_soc *)txrx_soc; 88 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 89 size_t alloc_size; 90 QDF_STATUS status; 91 92 if (!soc) { 93 dp_umac_reset_err("DP SOC is null"); 94 return QDF_STATUS_E_NULL_VALUE; 95 } 96 97 if (!soc->features.umac_hw_reset_support) { 98 dp_umac_reset_info("Target doesn't support the UMAC HW reset feature"); 99 return QDF_STATUS_E_NOSUPPORT; 100 } 101 102 umac_reset_ctx = &soc->umac_reset_ctx; 103 qdf_mem_zero(umac_reset_ctx, sizeof(*umac_reset_ctx)); 104 105 umac_reset_ctx->current_state = UMAC_RESET_STATE_WAIT_FOR_TRIGGER; 106 umac_reset_ctx->shmem_exp_magic_num = DP_UMAC_RESET_SHMEM_MAGIC_NUM; 107 108 status = dp_get_umac_reset_intr_ctx(soc, &umac_reset_ctx->intr_offset); 109 if (QDF_IS_STATUS_ERROR(status)) { 110 dp_umac_reset_err("No interrupt assignment"); 111 return status; 112 } 113 114 alloc_size = sizeof(htt_umac_hang_recovery_msg_shmem_t) + 115 DP_UMAC_RESET_SHMEM_ALIGN - 1; 116 umac_reset_ctx->shmem_vaddr_unaligned = 117 qdf_mem_alloc_consistent(soc->osdev, soc->osdev->dev, 118 alloc_size, 119 &umac_reset_ctx->shmem_paddr_unaligned); 120 if (!umac_reset_ctx->shmem_vaddr_unaligned) { 121 dp_umac_reset_err("shmem allocation failed"); 122 return QDF_STATUS_E_NOMEM; 123 } 124 125 umac_reset_ctx->shmem_vaddr_aligned = (void *)(uintptr_t)qdf_roundup( 126 (uint64_t)(uintptr_t)umac_reset_ctx->shmem_vaddr_unaligned, 127 DP_UMAC_RESET_SHMEM_ALIGN); 128 umac_reset_ctx->shmem_paddr_aligned = qdf_roundup( 129 (uint64_t)umac_reset_ctx->shmem_paddr_unaligned, 130 DP_UMAC_RESET_SHMEM_ALIGN); 131 umac_reset_ctx->shmem_size = alloc_size; 132 133 /* Write the magic number to the shared memory */ 134 umac_reset_ctx->shmem_vaddr_aligned->magic_num = 135 DP_UMAC_RESET_SHMEM_MAGIC_NUM; 136 137 /* Attach the interrupts */ 138 status = dp_umac_reset_interrupt_attach(soc); 139 if (QDF_IS_STATUS_ERROR(status)) { 140 dp_umac_reset_err("Interrupt attach failed"); 141 qdf_mem_free_consistent(soc->osdev, soc->osdev->dev, 142 umac_reset_ctx->shmem_size, 143 umac_reset_ctx->shmem_vaddr_unaligned, 144 umac_reset_ctx->shmem_paddr_unaligned, 145 0); 146 return status; 147 } 148 149 /* Send the setup cmd to the target */ 150 return dp_umac_reset_send_setup_cmd(soc); 151 } 152 153 /** 154 * dp_umac_reset_get_rx_event_from_shmem() - Extract the Rx event from the 155 * shared memory 156 * @umac_reset_ctx: UMAC reset context 157 * 158 * Return: Extracted Rx event in the form of enumeration umac_reset_rx_event 159 */ 160 static enum umac_reset_rx_event 161 dp_umac_reset_get_rx_event_from_shmem( 162 struct dp_soc_umac_reset_ctx *umac_reset_ctx) 163 { 164 htt_umac_hang_recovery_msg_shmem_t *shmem_vaddr; 165 uint32_t t2h_msg; 166 uint8_t num_events = 0; 167 enum umac_reset_rx_event rx_event; 168 169 shmem_vaddr = umac_reset_ctx->shmem_vaddr_aligned; 170 if (!shmem_vaddr) { 171 dp_umac_reset_err("Shared memory address is NULL"); 172 goto err; 173 } 174 175 if (shmem_vaddr->magic_num != umac_reset_ctx->shmem_exp_magic_num) { 176 dp_umac_reset_err("Shared memory got corrupted"); 177 goto err; 178 } 179 180 /* Read the shared memory into a local variable */ 181 t2h_msg = shmem_vaddr->t2h_msg; 182 183 /* Clear the shared memory right away */ 184 shmem_vaddr->t2h_msg = 0; 185 186 dp_umac_reset_debug("shmem value - t2h_msg: 0x%x", t2h_msg); 187 188 rx_event = UMAC_RESET_RX_EVENT_NONE; 189 190 if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_INITIATE_UMAC_RECOVERY_GET(t2h_msg)) { 191 rx_event |= UMAC_RESET_RX_EVENT_DO_TRIGGER_RECOVERY; 192 num_events++; 193 } 194 195 if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_INITIATE_TARGET_RECOVERY_SYNC_USING_UMAC_GET(t2h_msg)) { 196 rx_event |= UMAC_RESET_RX_EVENT_DO_TRIGGER_TR_SYNC; 197 num_events++; 198 } 199 200 if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_PRE_RESET_GET(t2h_msg)) { 201 rx_event |= UMAC_RESET_RX_EVENT_DO_PRE_RESET; 202 num_events++; 203 } 204 205 if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_POST_RESET_START_GET(t2h_msg)) { 206 rx_event |= UMAC_RESET_RX_EVENT_DO_POST_RESET_START; 207 num_events++; 208 } 209 210 if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_POST_RESET_COMPLETE_GET(t2h_msg)) { 211 rx_event |= UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE; 212 num_events++; 213 } 214 215 dp_umac_reset_debug("deduced rx event: 0x%x", rx_event); 216 /* There should not be more than 1 event */ 217 if (num_events > 1) { 218 dp_umac_reset_err("Multiple events(0x%x) got posted", rx_event); 219 goto err; 220 } 221 222 return rx_event; 223 err: 224 qdf_assert_always(0); 225 return UMAC_RESET_RX_EVENT_ERROR; 226 } 227 228 /** 229 * dp_umac_reset_peek_rx_event_from_shmem() - Peek the Rx event from the 230 * shared memory without clearing the bit 231 * @umac_reset_ctx: UMAC reset context 232 * 233 * Return: true if the shared memory has any valid bits set 234 */ 235 static inline bool dp_umac_reset_peek_rx_event_from_shmem( 236 struct dp_soc_umac_reset_ctx *umac_reset_ctx) 237 { 238 htt_umac_hang_recovery_msg_shmem_t *shmem_vaddr; 239 240 shmem_vaddr = umac_reset_ctx->shmem_vaddr_aligned; 241 if (!shmem_vaddr) { 242 dp_umac_reset_debug("Shared memory address is NULL"); 243 goto err; 244 } 245 246 if (shmem_vaddr->magic_num != umac_reset_ctx->shmem_exp_magic_num) { 247 dp_umac_reset_debug("Shared memory got corrupted"); 248 goto err; 249 } 250 251 /* Read the shared memory into a local variable */ 252 return !!shmem_vaddr->t2h_msg; 253 254 err: 255 return false; 256 } 257 258 /** 259 * dp_umac_reset_get_rx_event() - Extract the Rx event 260 * @umac_reset_ctx: UMAC reset context 261 * 262 * Return: Extracted Rx event in the form of enumeration umac_reset_rx_event 263 */ 264 static inline enum umac_reset_rx_event 265 dp_umac_reset_get_rx_event(struct dp_soc_umac_reset_ctx *umac_reset_ctx) 266 { 267 return dp_umac_reset_get_rx_event_from_shmem(umac_reset_ctx); 268 } 269 270 /** 271 * dp_umac_reset_validate_n_update_state_machine_on_rx() - Validate the state 272 * machine for a given rx event and update the state machine 273 * @umac_reset_ctx: UMAC reset context 274 * @rx_event: Rx event 275 * @current_exp_state: Expected state 276 * @next_state: The state to which the state machine needs to be updated 277 * 278 * Return: QDF_STATUS of operation 279 */ 280 QDF_STATUS 281 dp_umac_reset_validate_n_update_state_machine_on_rx( 282 struct dp_soc_umac_reset_ctx *umac_reset_ctx, 283 enum umac_reset_rx_event rx_event, 284 enum umac_reset_state current_exp_state, 285 enum umac_reset_state next_state) 286 { 287 if (umac_reset_ctx->current_state != current_exp_state) { 288 dp_umac_reset_err("state machine validation failed on rx event: %d, current state is %d", 289 rx_event, 290 umac_reset_ctx->current_state); 291 292 if ((rx_event != UMAC_RESET_RX_EVENT_DO_TRIGGER_RECOVERY) && 293 (rx_event != UMAC_RESET_RX_EVENT_DO_TRIGGER_TR_SYNC)) 294 qdf_assert_always(0); 295 296 return QDF_STATUS_E_FAILURE; 297 } 298 299 /* Update the state */ 300 umac_reset_ctx->current_state = next_state; 301 return QDF_STATUS_SUCCESS; 302 } 303 304 static bool dp_umac_reset_peek_rx_event(void *dp_ctx) 305 { 306 struct dp_intr *int_ctx = (struct dp_intr *)dp_ctx; 307 struct dp_soc *soc = int_ctx->soc; 308 struct dp_soc_umac_reset_ctx *umac_reset_ctx = &soc->umac_reset_ctx; 309 310 return dp_umac_reset_peek_rx_event_from_shmem(umac_reset_ctx); 311 } 312 313 /** 314 * dp_check_umac_reset_in_progress() - Check if Umac reset is in progress 315 * @soc: dp soc handle 316 * 317 * Return: true if Umac reset is in progress or false otherwise 318 */ 319 bool dp_check_umac_reset_in_progress(struct dp_soc *soc) 320 { 321 return !!soc->umac_reset_ctx.intr_ctx_bkp; 322 } 323 324 325 #if !defined(QCA_SUPPORT_DP_GLOBAL_CTX) || \ 326 (defined(QCA_SUPPORT_DP_GLOBAL_CTX) && \ 327 !defined(WLAN_FEATURE_11BE_MLO) || !defined(WLAN_MLO_MULTI_CHIP)) 328 bool dp_get_global_tx_desc_cleanup_flag(struct dp_soc *soc) 329 { 330 return true; 331 } 332 333 void dp_reset_global_tx_desc_cleanup_flag(struct dp_soc *soc) 334 { 335 } 336 #endif 337 338 #if !defined(WLAN_FEATURE_11BE_MLO) || !defined(WLAN_MLO_MULTI_CHIP) 339 /** 340 * dp_umac_reset_initiate_umac_recovery() - Initiate Umac reset session 341 * @soc: dp soc handle 342 * @umac_reset_ctx: Umac reset context 343 * @rx_event: Rx event received 344 * @is_target_recovery: Flag to indicate if it is triggered for target recovery 345 * 346 * Return: status 347 */ 348 static QDF_STATUS dp_umac_reset_initiate_umac_recovery(struct dp_soc *soc, 349 struct dp_soc_umac_reset_ctx *umac_reset_ctx, 350 enum umac_reset_rx_event rx_event, 351 bool is_target_recovery) 352 { 353 return dp_umac_reset_validate_n_update_state_machine_on_rx( 354 umac_reset_ctx, rx_event, 355 UMAC_RESET_STATE_WAIT_FOR_TRIGGER, 356 UMAC_RESET_STATE_DO_TRIGGER_RECEIVED); 357 } 358 359 /** 360 * dp_umac_reset_complete_umac_recovery() - Complete Umac reset session 361 * @soc: dp soc handle 362 * 363 * Return: void 364 */ 365 static void dp_umac_reset_complete_umac_recovery(struct dp_soc *soc) 366 { 367 dp_umac_reset_alert("Umac reset was handled successfully on soc %pK", 368 soc); 369 } 370 371 /** 372 * dp_umac_reset_handle_action_cb() - Function to call action callback 373 * @soc: dp soc handle 374 * @umac_reset_ctx: Umac reset context 375 * @action: Action to call the callback for 376 * 377 * Return: QDF_STATUS status 378 */ 379 static QDF_STATUS dp_umac_reset_handle_action_cb(struct dp_soc *soc, 380 struct dp_soc_umac_reset_ctx *umac_reset_ctx, 381 enum umac_reset_action action) 382 { 383 QDF_STATUS status = QDF_STATUS_SUCCESS; 384 385 if (!umac_reset_ctx->rx_actions.cb[action]) { 386 dp_umac_reset_err("rx callback is NULL"); 387 return QDF_STATUS_E_FAILURE; 388 } 389 390 status = umac_reset_ctx->rx_actions.cb[action](soc); 391 392 return QDF_STATUS_SUCCESS; 393 } 394 395 /** 396 * dp_umac_reset_post_tx_cmd() - Iterate partner socs and post Tx command 397 * @umac_reset_ctx: UMAC reset context 398 * @tx_cmd: Tx command to be posted 399 * 400 * Return: QDF status of operation 401 */ 402 static QDF_STATUS 403 dp_umac_reset_post_tx_cmd(struct dp_soc_umac_reset_ctx *umac_reset_ctx, 404 enum umac_reset_tx_cmd tx_cmd) 405 { 406 struct dp_soc *soc = container_of(umac_reset_ctx, struct dp_soc, 407 umac_reset_ctx); 408 409 dp_umac_reset_post_tx_cmd_via_shmem(soc, &tx_cmd, 0); 410 return QDF_STATUS_SUCCESS; 411 } 412 413 /** 414 * dp_umac_reset_initiator_check() - Check if soc is the Umac reset initiator 415 * @soc: dp soc handle 416 * 417 * Return: true if the soc is initiator or false otherwise 418 */ 419 static bool dp_umac_reset_initiator_check(struct dp_soc *soc) 420 { 421 return true; 422 } 423 424 /** 425 * dp_umac_reset_target_recovery_check() - Check if this is for target recovery 426 * @soc: dp soc handle 427 * 428 * Return: true if the session is for target recovery or false otherwise 429 */ 430 static bool dp_umac_reset_target_recovery_check(struct dp_soc *soc) 431 { 432 return false; 433 } 434 435 /** 436 * dp_umac_reset_is_soc_ignored() - Check if this soc is to be ignored 437 * @soc: dp soc handle 438 * 439 * Return: true if the soc is ignored or false otherwise 440 */ 441 static bool dp_umac_reset_is_soc_ignored(struct dp_soc *soc) 442 { 443 return false; 444 } 445 #endif 446 447 /** 448 * dp_umac_reset_rx_event_handler() - Main Rx event handler for UMAC reset 449 * @dp_ctx: Interrupt context corresponding to UMAC reset 450 * 451 * Return: 0 incase of success, else failure 452 */ 453 static int dp_umac_reset_rx_event_handler(void *dp_ctx) 454 { 455 struct dp_intr *int_ctx = (struct dp_intr *)dp_ctx; 456 struct dp_soc *soc = int_ctx->soc; 457 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 458 enum umac_reset_rx_event rx_event; 459 QDF_STATUS status = QDF_STATUS_E_INVAL; 460 enum umac_reset_action action = UMAC_RESET_ACTION_NONE; 461 bool target_recovery = false; 462 463 if (!soc) { 464 dp_umac_reset_err("DP SOC is null"); 465 goto exit; 466 } 467 468 umac_reset_ctx = &soc->umac_reset_ctx; 469 470 dp_umac_reset_debug("enter"); 471 rx_event = dp_umac_reset_get_rx_event(umac_reset_ctx); 472 473 if (umac_reset_ctx->pending_action) { 474 if (rx_event != UMAC_RESET_RX_EVENT_NONE) { 475 dp_umac_reset_err("Invalid value(%u) for Rx event when " 476 "action %u is pending\n", rx_event, 477 umac_reset_ctx->pending_action); 478 qdf_assert_always(0); 479 } 480 } 481 482 switch (rx_event) { 483 case UMAC_RESET_RX_EVENT_NONE: 484 if (umac_reset_ctx->pending_action) 485 action = umac_reset_ctx->pending_action; 486 else 487 dp_umac_reset_err("Not a UMAC reset event!!"); 488 489 status = QDF_STATUS_SUCCESS; 490 break; 491 492 case UMAC_RESET_RX_EVENT_DO_TRIGGER_TR_SYNC: 493 target_recovery = true; 494 /* Fall through */ 495 case UMAC_RESET_RX_EVENT_DO_TRIGGER_RECOVERY: 496 status = 497 dp_umac_reset_initiate_umac_recovery(soc, umac_reset_ctx, 498 rx_event, target_recovery); 499 500 if (status != QDF_STATUS_SUCCESS) 501 break; 502 503 umac_reset_ctx->ts.trigger_start = 504 qdf_get_log_timestamp_usecs(); 505 506 action = UMAC_RESET_ACTION_DO_TRIGGER_RECOVERY; 507 508 break; 509 510 case UMAC_RESET_RX_EVENT_DO_PRE_RESET: 511 status = dp_umac_reset_validate_n_update_state_machine_on_rx( 512 umac_reset_ctx, rx_event, 513 UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET, 514 UMAC_RESET_STATE_DO_PRE_RESET_RECEIVED); 515 516 umac_reset_ctx->ts.pre_reset_start = 517 qdf_get_log_timestamp_usecs(); 518 519 action = UMAC_RESET_ACTION_DO_PRE_RESET; 520 break; 521 522 case UMAC_RESET_RX_EVENT_DO_POST_RESET_START: 523 status = dp_umac_reset_validate_n_update_state_machine_on_rx( 524 umac_reset_ctx, rx_event, 525 UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_START, 526 UMAC_RESET_STATE_DO_POST_RESET_START_RECEIVED); 527 528 umac_reset_ctx->ts.post_reset_start = 529 qdf_get_log_timestamp_usecs(); 530 531 action = UMAC_RESET_ACTION_DO_POST_RESET_START; 532 break; 533 534 case UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE: 535 status = dp_umac_reset_validate_n_update_state_machine_on_rx( 536 umac_reset_ctx, rx_event, 537 UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_COMPLETE, 538 UMAC_RESET_STATE_DO_POST_RESET_COMPLETE_RECEIVED); 539 540 umac_reset_ctx->ts.post_reset_complete_start = 541 qdf_get_log_timestamp_usecs(); 542 543 action = UMAC_RESET_ACTION_DO_POST_RESET_COMPLETE; 544 break; 545 546 case UMAC_RESET_RX_EVENT_ERROR: 547 dp_umac_reset_err("Error Rx event"); 548 goto exit; 549 550 default: 551 dp_umac_reset_err("Invalid value(%u) for Rx event", rx_event); 552 goto exit; 553 } 554 555 /* Call the handler for this event */ 556 if (QDF_IS_STATUS_SUCCESS(status)) { 557 dp_umac_reset_handle_action_cb(soc, umac_reset_ctx, action); 558 } 559 560 exit: 561 return qdf_status_to_os_return(status); 562 } 563 564 QDF_STATUS dp_umac_reset_interrupt_attach(struct dp_soc *soc) 565 { 566 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 567 int msi_vector_count, ret; 568 uint32_t msi_base_data, msi_vector_start; 569 uint32_t umac_reset_vector, umac_reset_irq; 570 QDF_STATUS status; 571 572 if (!soc) { 573 dp_umac_reset_err("DP SOC is null"); 574 return QDF_STATUS_E_NULL_VALUE; 575 } 576 577 if (!soc->features.umac_hw_reset_support) { 578 dp_umac_reset_info("Target doesn't support the UMAC HW reset feature"); 579 return QDF_STATUS_SUCCESS; 580 } 581 582 umac_reset_ctx = &soc->umac_reset_ctx; 583 584 if (pld_get_enable_intx(soc->osdev->dev)) { 585 dp_umac_reset_err("UMAC reset is not supported in legacy interrupt mode"); 586 return QDF_STATUS_E_FAILURE; 587 } 588 589 ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP", 590 &msi_vector_count, &msi_base_data, 591 &msi_vector_start); 592 if (ret) { 593 /* UMAC reset uses IPC interrupt for AHB devices */ 594 status = hif_get_umac_reset_irq(soc->hif_handle, 595 &umac_reset_irq); 596 if (status) { 597 dp_umac_reset_err("get_umac_reset_irq failed status %d", 598 status); 599 return QDF_STATUS_E_FAILURE; 600 } 601 } else { 602 if (umac_reset_ctx->intr_offset < 0 || 603 umac_reset_ctx->intr_offset >= WLAN_CFG_INT_NUM_CONTEXTS) { 604 dp_umac_reset_err("Invalid interrupt offset: %d", 605 umac_reset_ctx->intr_offset); 606 return QDF_STATUS_E_FAILURE; 607 } 608 609 umac_reset_vector = msi_vector_start + 610 (umac_reset_ctx->intr_offset % msi_vector_count); 611 612 /* Get IRQ number */ 613 umac_reset_irq = pld_get_msi_irq(soc->osdev->dev, 614 umac_reset_vector); 615 } 616 617 /* Finally register to this IRQ from HIF layer */ 618 return hif_register_umac_reset_handler( 619 soc->hif_handle, 620 dp_umac_reset_peek_rx_event, 621 dp_umac_reset_rx_event_handler, 622 &soc->intr_ctx[umac_reset_ctx->intr_offset], 623 umac_reset_irq); 624 } 625 626 QDF_STATUS dp_umac_reset_interrupt_detach(struct dp_soc *soc) 627 { 628 if (!soc) { 629 dp_umac_reset_err("DP SOC is null"); 630 return QDF_STATUS_E_NULL_VALUE; 631 } 632 633 if (!soc->features.umac_hw_reset_support) { 634 dp_umac_reset_info("Target doesn't support the UMAC HW reset feature"); 635 return QDF_STATUS_SUCCESS; 636 } 637 638 return hif_unregister_umac_reset_handler(soc->hif_handle); 639 } 640 641 QDF_STATUS dp_umac_reset_register_rx_action_callback( 642 struct dp_soc *soc, 643 QDF_STATUS (*handler)(struct dp_soc *soc), 644 enum umac_reset_action action) 645 { 646 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 647 648 if (!soc) { 649 dp_umac_reset_err("DP SOC is null"); 650 return QDF_STATUS_E_NULL_VALUE; 651 } 652 653 if (!soc->features.umac_hw_reset_support) { 654 dp_umac_reset_info("Target doesn't support UMAC HW reset"); 655 return QDF_STATUS_E_NOSUPPORT; 656 } 657 658 if (action >= UMAC_RESET_ACTION_MAX) { 659 dp_umac_reset_err("invalid action: %d", action); 660 return QDF_STATUS_E_INVAL; 661 } 662 663 umac_reset_ctx = &soc->umac_reset_ctx; 664 665 umac_reset_ctx->rx_actions.cb[action] = handler; 666 667 return QDF_STATUS_SUCCESS; 668 } 669 670 /** 671 * dp_umac_reset_post_tx_cmd_via_shmem() - Post Tx command using shared memory 672 * @soc: DP soc object 673 * @ctxt: Tx command to be posted 674 * @chip_id: Chip id of the mlo soc 675 * 676 * Return: None 677 */ 678 void 679 dp_umac_reset_post_tx_cmd_via_shmem(struct dp_soc *soc, void *ctxt, int chip_id) 680 { 681 enum umac_reset_tx_cmd tx_cmd = *((enum umac_reset_tx_cmd *)ctxt); 682 htt_umac_hang_recovery_msg_shmem_t *shmem_vaddr; 683 struct dp_soc_umac_reset_ctx *umac_reset_ctx = &soc->umac_reset_ctx; 684 bool initiator; 685 QDF_STATUS status; 686 687 if (dp_umac_reset_is_soc_ignored(soc)) { 688 dp_umac_reset_debug("Skipping soc (chip id %d)", chip_id); 689 return; 690 } 691 692 shmem_vaddr = umac_reset_ctx->shmem_vaddr_aligned; 693 if (!shmem_vaddr) { 694 dp_umac_reset_err("Shared memory address is NULL"); 695 return; 696 } 697 698 dp_umac_reset_debug("Sending txcmd %u for chip id %u", tx_cmd, chip_id); 699 700 switch (tx_cmd) { 701 case UMAC_RESET_TX_CMD_TRIGGER_DONE: 702 /* Send htt message to the partner soc */ 703 initiator = dp_umac_reset_initiator_check(soc); 704 if (!initiator) 705 umac_reset_ctx->current_state = 706 UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET; 707 708 status = dp_htt_umac_reset_send_start_pre_reset_cmd(soc, 709 initiator, 710 !dp_umac_reset_target_recovery_check(soc)); 711 712 if (status != QDF_STATUS_SUCCESS) { 713 dp_umac_reset_err("Unable to send Umac trigger"); 714 qdf_assert_always(0); 715 } else { 716 dp_umac_reset_debug("Sent trigger for soc (chip_id %d)", 717 chip_id); 718 } 719 720 umac_reset_ctx->ts.trigger_done = qdf_get_log_timestamp_usecs(); 721 break; 722 723 case UMAC_RESET_TX_CMD_PRE_RESET_DONE: 724 HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_PRE_RESET_DONE_SET( 725 shmem_vaddr->h2t_msg, 1); 726 727 umac_reset_ctx->ts.pre_reset_done = 728 qdf_get_log_timestamp_usecs(); 729 break; 730 731 case UMAC_RESET_TX_CMD_POST_RESET_START_DONE: 732 HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_POST_RESET_START_DONE_SET( 733 shmem_vaddr->h2t_msg, 1); 734 735 umac_reset_ctx->ts.post_reset_done = 736 qdf_get_log_timestamp_usecs(); 737 break; 738 739 case UMAC_RESET_TX_CMD_POST_RESET_COMPLETE_DONE: 740 HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_POST_RESET_COMPLETE_DONE_SET( 741 shmem_vaddr->h2t_msg, 1); 742 743 umac_reset_ctx->ts.post_reset_complete_done = 744 qdf_get_log_timestamp_usecs(); 745 break; 746 747 default: 748 dp_umac_reset_err("Invalid tx cmd: %d", tx_cmd); 749 return; 750 } 751 752 return; 753 } 754 755 /** 756 * dp_umac_reset_notify_target() - Notify the target about completion of action. 757 * @umac_reset_ctx: UMAC reset context 758 * 759 * This API figures out the Tx command that needs to be posted based on the 760 * current state in the state machine. Also, updates the state machine once the 761 * Tx command has been posted. 762 * 763 * Return: QDF status of operation 764 */ 765 static QDF_STATUS 766 dp_umac_reset_notify_target(struct dp_soc_umac_reset_ctx *umac_reset_ctx) 767 { 768 enum umac_reset_state next_state; 769 enum umac_reset_tx_cmd tx_cmd; 770 QDF_STATUS status; 771 772 switch (umac_reset_ctx->current_state) { 773 case UMAC_RESET_STATE_HOST_TRIGGER_DONE: 774 tx_cmd = UMAC_RESET_TX_CMD_TRIGGER_DONE; 775 next_state = UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET; 776 break; 777 778 case UMAC_RESET_STATE_HOST_PRE_RESET_DONE: 779 tx_cmd = UMAC_RESET_TX_CMD_PRE_RESET_DONE; 780 next_state = UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_START; 781 break; 782 783 case UMAC_RESET_STATE_HOST_POST_RESET_START_DONE: 784 tx_cmd = UMAC_RESET_TX_CMD_POST_RESET_START_DONE; 785 next_state = UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_COMPLETE; 786 break; 787 788 case UMAC_RESET_STATE_HOST_POST_RESET_COMPLETE_DONE: 789 tx_cmd = UMAC_RESET_TX_CMD_POST_RESET_COMPLETE_DONE; 790 next_state = UMAC_RESET_STATE_WAIT_FOR_TRIGGER; 791 break; 792 793 default: 794 dp_umac_reset_err("Invalid state(%d) during Tx", 795 umac_reset_ctx->current_state); 796 qdf_assert_always(0); 797 return QDF_STATUS_E_FAILURE; 798 } 799 800 /* 801 * Update the state machine before sending the command to firmware 802 * as we might get the response from firmware even before the state 803 * is updated. 804 */ 805 umac_reset_ctx->current_state = next_state; 806 807 status = dp_umac_reset_post_tx_cmd(umac_reset_ctx, tx_cmd); 808 if (QDF_IS_STATUS_ERROR(status)) { 809 dp_umac_reset_err("Couldn't post Tx cmd"); 810 qdf_assert_always(0); 811 return status; 812 } 813 814 return status; 815 } 816 817 /** 818 * dp_umac_reset_notify_completion() - Notify that a given action has been 819 * completed 820 * @soc: DP soc object 821 * @next_state: The state to which the state machine needs to be updated due to 822 * this completion 823 * 824 * Return: QDF status of operation 825 */ 826 static QDF_STATUS dp_umac_reset_notify_completion( 827 struct dp_soc *soc, 828 enum umac_reset_state next_state) 829 { 830 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 831 832 if (!soc) { 833 dp_umac_reset_err("DP SOC is null"); 834 return QDF_STATUS_E_NULL_VALUE; 835 } 836 837 umac_reset_ctx = &soc->umac_reset_ctx; 838 839 /* Update the state first */ 840 umac_reset_ctx->current_state = next_state; 841 842 return dp_umac_reset_notify_target(umac_reset_ctx); 843 } 844 845 static void dp_umac_wait_for_quiescent_state(struct dp_soc *soc) 846 { 847 enum umac_reset_state current_state; 848 849 do { 850 msleep(10); 851 barrier(); 852 current_state = soc->umac_reset_ctx.current_state; 853 854 } while ((current_state == UMAC_RESET_STATE_DO_TRIGGER_RECEIVED) || 855 (current_state == UMAC_RESET_STATE_DO_PRE_RESET_RECEIVED) || 856 (current_state == UMAC_RESET_STATE_DO_POST_RESET_START_RECEIVED) || 857 (current_state == UMAC_RESET_STATE_DO_POST_RESET_COMPLETE_RECEIVED)); 858 } 859 860 QDF_STATUS dp_umac_reset_notify_action_completion( 861 struct dp_soc *soc, 862 enum umac_reset_action action) 863 { 864 enum umac_reset_state next_state; 865 866 if (!soc) { 867 dp_umac_reset_err("DP SOC is null"); 868 return QDF_STATUS_E_NULL_VALUE; 869 } 870 871 if (!soc->features.umac_hw_reset_support) { 872 dp_umac_reset_info("Target doesn't support the UMAC HW reset feature"); 873 return QDF_STATUS_E_NOSUPPORT; 874 } 875 876 switch (action) { 877 case UMAC_RESET_ACTION_DO_TRIGGER_RECOVERY: 878 next_state = UMAC_RESET_STATE_HOST_TRIGGER_DONE; 879 break; 880 881 case UMAC_RESET_ACTION_DO_PRE_RESET: 882 next_state = UMAC_RESET_STATE_HOST_PRE_RESET_DONE; 883 break; 884 885 case UMAC_RESET_ACTION_DO_POST_RESET_START: 886 next_state = UMAC_RESET_STATE_HOST_POST_RESET_START_DONE; 887 break; 888 889 case UMAC_RESET_ACTION_DO_POST_RESET_COMPLETE: 890 next_state = UMAC_RESET_STATE_HOST_POST_RESET_COMPLETE_DONE; 891 break; 892 893 case UMAC_RESET_ACTION_ABORT: 894 next_state = UMAC_RESET_STATE_WAIT_FOR_TRIGGER; 895 break; 896 897 default: 898 dp_umac_reset_err("Invalid action: %u", action); 899 return QDF_STATUS_E_FAILURE; 900 } 901 902 return dp_umac_reset_notify_completion(soc, next_state); 903 } 904 905 /** 906 * dp_soc_umac_reset_deinit() - Deinitialize the umac reset module 907 * @txrx_soc: DP soc object 908 * 909 * Return: QDF status of operation 910 */ 911 QDF_STATUS dp_soc_umac_reset_deinit(struct cdp_soc_t *txrx_soc) 912 { 913 struct dp_soc *soc = (struct dp_soc *)txrx_soc; 914 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 915 qdf_nbuf_t nbuf_list; 916 917 if (!soc) { 918 dp_umac_reset_err("DP SOC is null"); 919 return QDF_STATUS_E_NULL_VALUE; 920 } 921 922 if (!soc->features.umac_hw_reset_support) { 923 dp_umac_reset_info("No target support for UMAC reset feature"); 924 return QDF_STATUS_E_NOSUPPORT; 925 } 926 927 if (dp_check_umac_reset_in_progress(soc)) { 928 dp_umac_reset_info("Cleaning up Umac reset context"); 929 dp_umac_wait_for_quiescent_state(soc); 930 dp_resume_reo_send_cmd(soc); 931 dp_umac_reset_notify_action_completion(soc, 932 UMAC_RESET_ACTION_ABORT); 933 } 934 935 nbuf_list = soc->umac_reset_ctx.nbuf_list; 936 soc->umac_reset_ctx.nbuf_list = NULL; 937 938 while (nbuf_list) { 939 qdf_nbuf_t nbuf = nbuf_list->next; 940 941 qdf_nbuf_free(nbuf_list); 942 nbuf_list = nbuf; 943 } 944 945 dp_umac_reset_interrupt_detach(soc); 946 947 umac_reset_ctx = &soc->umac_reset_ctx; 948 qdf_mem_free_consistent(soc->osdev, soc->osdev->dev, 949 umac_reset_ctx->shmem_size, 950 umac_reset_ctx->shmem_vaddr_unaligned, 951 umac_reset_ctx->shmem_paddr_unaligned, 952 0); 953 954 return QDF_STATUS_SUCCESS; 955 } 956 957 static inline const char *dp_umac_reset_current_state_to_str( 958 enum umac_reset_state current_state) 959 { 960 switch (current_state) { 961 case UMAC_RESET_STATE_WAIT_FOR_TRIGGER: 962 return "UMAC_RESET_STATE_WAIT_FOR_TRIGGER"; 963 case UMAC_RESET_STATE_DO_TRIGGER_RECEIVED: 964 return "UMAC_RESET_STATE_DO_TRIGGER_RECEIVED"; 965 case UMAC_RESET_STATE_HOST_TRIGGER_DONE: 966 return "UMAC_RESET_STATE_HOST_TRIGGER_DONE"; 967 case UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET: 968 return "UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET"; 969 case UMAC_RESET_STATE_DO_PRE_RESET_RECEIVED: 970 return "UMAC_RESET_STATE_DO_PRE_RESET_RECEIVED"; 971 case UMAC_RESET_STATE_HOST_PRE_RESET_DONE: 972 return "UMAC_RESET_STATE_HOST_PRE_RESET_DONE"; 973 case UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_START: 974 return "UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_START"; 975 case UMAC_RESET_STATE_DO_POST_RESET_START_RECEIVED: 976 return "UMAC_RESET_STATE_DO_POST_RESET_START_RECEIVED"; 977 case UMAC_RESET_STATE_HOST_POST_RESET_START_DONE: 978 return "UMAC_RESET_STATE_HOST_POST_RESET_START_DONE"; 979 case UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_COMPLETE: 980 return "UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_COMPLETE"; 981 case UMAC_RESET_STATE_DO_POST_RESET_COMPLETE_RECEIVED: 982 return "UMAC_RESET_STATE_DO_POST_RESET_COMPLETE_RECEIVED"; 983 case UMAC_RESET_STATE_HOST_POST_RESET_COMPLETE_DONE: 984 return "UMAC_RESET_STATE_HOST_POST_RESET_COMPLETE_DONE"; 985 default: 986 return "Invalid UMAC Reset state"; 987 } 988 } 989 990 static inline const char *dp_umac_reset_pending_action_to_str( 991 enum umac_reset_rx_event pending_action) 992 { 993 switch (pending_action) { 994 case UMAC_RESET_RX_EVENT_NONE: 995 return "UMAC_RESET_RX_EVENT_NONE"; 996 case UMAC_RESET_RX_EVENT_DO_TRIGGER_RECOVERY: 997 return "UMAC_RESET_RX_EVENT_DO_TRIGGER_RECOVERY"; 998 case UMAC_RESET_RX_EVENT_DO_TRIGGER_TR_SYNC: 999 return "UMAC_RESET_RX_EVENT_DO_TRIGGER_TR_SYNC"; 1000 case UMAC_RESET_RX_EVENT_DO_PRE_RESET: 1001 return "UMAC_RESET_RX_EVENT_DO_PRE_RESET"; 1002 case UMAC_RESET_RX_EVENT_DO_POST_RESET_START: 1003 return "UMAC_RESET_RX_EVENT_DO_POST_RESET_START"; 1004 case UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE: 1005 return "UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE"; 1006 default: 1007 return "Invalid pending action"; 1008 } 1009 } 1010 1011 QDF_STATUS dp_umac_reset_stats_print(struct dp_soc *soc) 1012 { 1013 struct dp_soc_umac_reset_ctx *umac_reset_ctx; 1014 1015 umac_reset_ctx = &soc->umac_reset_ctx; 1016 1017 DP_UMAC_RESET_PRINT_STATS("UMAC reset stats for soc:%pK\n" 1018 "\t\ttrigger time :%llu us\n" 1019 "\t\tPre_reset time :%llu us\n" 1020 "\t\tPost_reset time :%llu us\n" 1021 "\t\tPost_reset_complete time :%llu us\n" 1022 "\t\tCurrent state :%s\n" 1023 "\t\tPending action :%s", 1024 soc, 1025 umac_reset_ctx->ts.trigger_done - 1026 umac_reset_ctx->ts.trigger_start, 1027 umac_reset_ctx->ts.pre_reset_done - 1028 umac_reset_ctx->ts.pre_reset_start, 1029 umac_reset_ctx->ts.post_reset_done - 1030 umac_reset_ctx->ts.post_reset_start, 1031 umac_reset_ctx->ts.post_reset_complete_done - 1032 umac_reset_ctx->ts.post_reset_complete_start, 1033 dp_umac_reset_current_state_to_str( 1034 umac_reset_ctx->current_state), 1035 dp_umac_reset_pending_action_to_str( 1036 umac_reset_ctx->pending_action)); 1037 1038 return dp_mlo_umac_reset_stats_print(soc); 1039 } 1040