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(&params, 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, &params);
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