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 */
dp_get_umac_reset_intr_ctx(struct dp_soc * soc,int * intr_ctx)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
dp_umac_reset_send_setup_cmd(struct dp_soc * soc)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
dp_soc_umac_reset_init(struct cdp_soc_t * txrx_soc)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
dp_umac_reset_get_rx_event_from_shmem(struct dp_soc_umac_reset_ctx * umac_reset_ctx)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 */
dp_umac_reset_peek_rx_event_from_shmem(struct dp_soc_umac_reset_ctx * umac_reset_ctx)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
dp_umac_reset_get_rx_event(struct dp_soc_umac_reset_ctx * umac_reset_ctx)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
dp_umac_reset_validate_n_update_state_machine_on_rx(struct dp_soc_umac_reset_ctx * umac_reset_ctx,enum umac_reset_rx_event rx_event,enum umac_reset_state current_exp_state,enum umac_reset_state next_state)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
dp_umac_reset_peek_rx_event(void * dp_ctx)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 */
dp_check_umac_reset_in_progress(struct dp_soc * soc)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))
dp_get_global_tx_desc_cleanup_flag(struct dp_soc * soc)328 bool dp_get_global_tx_desc_cleanup_flag(struct dp_soc *soc)
329 {
330 return true;
331 }
332
dp_reset_global_tx_desc_cleanup_flag(struct dp_soc * soc)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 */
dp_umac_reset_initiate_umac_recovery(struct dp_soc * soc,struct dp_soc_umac_reset_ctx * umac_reset_ctx,enum umac_reset_rx_event rx_event,bool is_target_recovery)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 */
dp_umac_reset_complete_umac_recovery(struct dp_soc * soc)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 */
dp_umac_reset_handle_action_cb(struct dp_soc * soc,struct dp_soc_umac_reset_ctx * umac_reset_ctx,enum umac_reset_action action)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
dp_umac_reset_post_tx_cmd(struct dp_soc_umac_reset_ctx * umac_reset_ctx,enum umac_reset_tx_cmd tx_cmd)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 */
dp_umac_reset_initiator_check(struct dp_soc * soc)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 */
dp_umac_reset_target_recovery_check(struct dp_soc * soc)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 */
dp_umac_reset_is_soc_ignored(struct dp_soc * soc)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 */
dp_umac_reset_rx_event_handler(void * dp_ctx)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
dp_umac_reset_interrupt_attach(struct dp_soc * soc)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
dp_umac_reset_interrupt_detach(struct dp_soc * soc)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
dp_umac_reset_register_rx_action_callback(struct dp_soc * soc,QDF_STATUS (* handler)(struct dp_soc * soc),enum umac_reset_action action)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
dp_umac_reset_post_tx_cmd_via_shmem(struct dp_soc * soc,void * ctxt,int chip_id)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
dp_umac_reset_notify_target(struct dp_soc_umac_reset_ctx * umac_reset_ctx)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 */
dp_umac_reset_notify_completion(struct dp_soc * soc,enum umac_reset_state next_state)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
dp_umac_wait_for_quiescent_state(struct dp_soc * soc)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
dp_umac_reset_notify_action_completion(struct dp_soc * soc,enum umac_reset_action action)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 */
dp_soc_umac_reset_deinit(struct cdp_soc_t * txrx_soc)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
dp_umac_reset_current_state_to_str(enum umac_reset_state current_state)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
dp_umac_reset_pending_action_to_str(enum umac_reset_rx_event pending_action)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
dp_umac_reset_stats_print(struct dp_soc * soc)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