dp_umac_reset.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * Copyright (c) 2022 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_types.h>
  17. #include <wlan_cfg.h>
  18. #include <hif.h>
  19. /**
  20. * dp_get_umac_reset_intr_ctx() - Get the interrupt context to be used by
  21. * UMAC reset feature
  22. * @soc: DP soc object
  23. * @intr_ctx: Interrupt context variable to be populated by this API
  24. *
  25. * Return: QDF_STATUS of operation
  26. */
  27. static QDF_STATUS dp_get_umac_reset_intr_ctx(struct dp_soc *soc, int *intr_ctx)
  28. {
  29. int umac_reset_mask, i;
  30. /**
  31. * Go over all the contexts and check which interrupt context has
  32. * the UMAC reset mask set.
  33. */
  34. for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
  35. umac_reset_mask = wlan_cfg_get_umac_reset_intr_mask(
  36. soc->wlan_cfg_ctx, i);
  37. if (umac_reset_mask) {
  38. *intr_ctx = i;
  39. return QDF_STATUS_SUCCESS;
  40. }
  41. }
  42. *intr_ctx = -1;
  43. return QDF_STATUS_E_FAILURE;
  44. }
  45. QDF_STATUS dp_soc_umac_reset_init(struct dp_soc *soc)
  46. {
  47. struct dp_soc_umac_reset_ctx *umac_reset_ctx;
  48. size_t alloc_size;
  49. QDF_STATUS status;
  50. if (!soc) {
  51. dp_umac_reset_err("DP SOC is null");
  52. return QDF_STATUS_E_NULL_VALUE;
  53. }
  54. umac_reset_ctx = &soc->umac_reset_ctx;
  55. qdf_mem_zero(umac_reset_ctx, sizeof(*umac_reset_ctx));
  56. umac_reset_ctx->supported = true;
  57. umac_reset_ctx->current_state = UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET;
  58. status = dp_get_umac_reset_intr_ctx(soc, &umac_reset_ctx->intr_offset);
  59. if (QDF_IS_STATUS_ERROR(status)) {
  60. dp_umac_reset_err("No interrupt assignment");
  61. return status;
  62. }
  63. alloc_size = sizeof(htt_umac_hang_recovery_msg_shmem_t) +
  64. DP_UMAC_RESET_SHMEM_ALIGN - 1;
  65. umac_reset_ctx->shmem_vaddr_unaligned =
  66. qdf_mem_alloc_consistent(soc->osdev, soc->osdev->dev,
  67. alloc_size,
  68. &umac_reset_ctx->shmem_paddr_unaligned);
  69. if (!umac_reset_ctx->shmem_vaddr_unaligned) {
  70. dp_umac_reset_err("shmem allocation failed");
  71. return QDF_STATUS_E_NOMEM;
  72. }
  73. umac_reset_ctx->shmem_vaddr_aligned = (void *)(uintptr_t)qdf_roundup(
  74. (uint64_t)(uintptr_t)umac_reset_ctx->shmem_vaddr_unaligned,
  75. DP_UMAC_RESET_SHMEM_ALIGN);
  76. umac_reset_ctx->shmem_paddr_aligned = qdf_roundup(
  77. (uint64_t)umac_reset_ctx->shmem_paddr_unaligned,
  78. DP_UMAC_RESET_SHMEM_ALIGN);
  79. return QDF_STATUS_SUCCESS;
  80. }
  81. /**
  82. * dp_umac_reset_get_rx_event() - Extract the Rx event from the shared memory
  83. * @umac_reset_ctx: UMAC reset context
  84. *
  85. * Return: Extracted Rx event in the form of enumeration umac_reset_rx_event
  86. */
  87. static enum umac_reset_rx_event
  88. dp_umac_reset_get_rx_event_from_shmem(
  89. struct dp_soc_umac_reset_ctx *umac_reset_ctx)
  90. {
  91. htt_umac_hang_recovery_msg_shmem_t *shmem_vaddr;
  92. uint32_t t2h_msg;
  93. uint8_t num_events = 0;
  94. enum umac_reset_rx_event rx_event;
  95. shmem_vaddr = umac_reset_ctx->shmem_vaddr_aligned;
  96. if (!shmem_vaddr) {
  97. dp_umac_reset_err("Shared memory address is NULL");
  98. goto err;
  99. }
  100. if (shmem_vaddr->magic_num != umac_reset_ctx->shmem_exp_magic_num) {
  101. dp_umac_reset_err("Shared memory got corrupted");
  102. goto err;
  103. }
  104. /* Read the shared memory into a local variable */
  105. t2h_msg = shmem_vaddr->t2h_msg;
  106. /* Clear the shared memory right away */
  107. shmem_vaddr->t2h_msg = 0;
  108. dp_umac_reset_debug("shmem value - t2h_msg: 0x%x", t2h_msg);
  109. rx_event = UMAC_RESET_RX_EVENT_NONE;
  110. if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_PRE_RESET_GET(t2h_msg)) {
  111. rx_event |= UMAC_RESET_RX_EVENT_DO_PRE_RESET;
  112. num_events++;
  113. }
  114. if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_POST_RESET_START_GET(t2h_msg)) {
  115. rx_event |= UMAC_RESET_RX_EVENT_DO_POST_RESET_START;
  116. num_events++;
  117. }
  118. if (HTT_UMAC_HANG_RECOVERY_MSG_SHMEM_DO_POST_RESET_COMPLETE_GET(t2h_msg)) {
  119. rx_event |= UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE;
  120. num_events++;
  121. }
  122. dp_umac_reset_debug("deduced rx event: 0x%x", rx_event);
  123. /* There should not be more than 1 event */
  124. if (num_events > 1) {
  125. dp_umac_reset_err("Multiple events(0x%x) got posted", rx_event);
  126. goto err;
  127. }
  128. return rx_event;
  129. err:
  130. qdf_assert_always(0);
  131. return UMAC_RESET_RX_EVENT_ERROR;
  132. }
  133. /**
  134. * dp_umac_reset_get_rx_event() - Extract the Rx event
  135. * @umac_reset_ctx: UMAC reset context
  136. *
  137. * Return: Extracted Rx event in the form of enumeration umac_reset_rx_event
  138. */
  139. static inline enum umac_reset_rx_event
  140. dp_umac_reset_get_rx_event(struct dp_soc_umac_reset_ctx *umac_reset_ctx)
  141. {
  142. return dp_umac_reset_get_rx_event_from_shmem(umac_reset_ctx);
  143. }
  144. /**
  145. * dp_umac_reset_validate_n_update_state_machine_on_rx() - Validate the state
  146. * machine for a given rx event and update the state machine
  147. * @umac_reset_ctx: UMAC reset context
  148. * @rx_event: Rx event
  149. * @current_exp_state: Expected state
  150. * @next_state: The state to which the state machine needs to be updated
  151. *
  152. * Return: QDF_STATUS of operation
  153. */
  154. static QDF_STATUS
  155. dp_umac_reset_validate_n_update_state_machine_on_rx(
  156. struct dp_soc_umac_reset_ctx *umac_reset_ctx,
  157. enum umac_reset_rx_event rx_event,
  158. enum umac_reset_state current_exp_state,
  159. enum umac_reset_state next_state)
  160. {
  161. if (umac_reset_ctx->current_state != current_exp_state) {
  162. dp_umac_reset_err("state machine validation failed on rx event: %d, current state is %d",
  163. rx_event,
  164. umac_reset_ctx->current_state);
  165. qdf_assert_always(0);
  166. return QDF_STATUS_E_FAILURE;
  167. }
  168. /* Update the state */
  169. umac_reset_ctx->current_state = next_state;
  170. return QDF_STATUS_SUCCESS;
  171. }
  172. /**
  173. * dp_umac_reset_rx_event_handler() - Main Rx event handler for UMAC reset
  174. * @dp_ctx: Interrupt context corresponding to UMAC reset
  175. *
  176. * Return: 0 incase of success, else failure
  177. */
  178. static int dp_umac_reset_rx_event_handler(void *dp_ctx)
  179. {
  180. struct dp_intr *int_ctx = (struct dp_intr *)dp_ctx;
  181. struct dp_soc *soc = int_ctx->soc;
  182. struct dp_soc_umac_reset_ctx *umac_reset_ctx;
  183. enum umac_reset_rx_event rx_event;
  184. QDF_STATUS status = QDF_STATUS_E_INVAL;
  185. enum umac_reset_action action;
  186. if (!soc) {
  187. dp_umac_reset_err("DP SOC is null");
  188. goto exit;
  189. }
  190. umac_reset_ctx = &soc->umac_reset_ctx;
  191. dp_umac_reset_debug("enter");
  192. rx_event = dp_umac_reset_get_rx_event(umac_reset_ctx);
  193. switch (rx_event) {
  194. case UMAC_RESET_RX_EVENT_NONE:
  195. /* This interrupt is not meant for us, so exit */
  196. dp_umac_reset_debug("Not a UMAC reset event");
  197. status = QDF_STATUS_SUCCESS;
  198. goto exit;
  199. case UMAC_RESET_RX_EVENT_DO_PRE_RESET:
  200. status = dp_umac_reset_validate_n_update_state_machine_on_rx(
  201. umac_reset_ctx, rx_event,
  202. UMAC_RESET_STATE_WAIT_FOR_DO_PRE_RESET,
  203. UMAC_RESET_STATE_DO_PRE_RESET_RECEIVED);
  204. action = UMAC_RESET_ACTION_DO_PRE_RESET;
  205. break;
  206. case UMAC_RESET_RX_EVENT_DO_POST_RESET_START:
  207. status = dp_umac_reset_validate_n_update_state_machine_on_rx(
  208. umac_reset_ctx, rx_event,
  209. UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_START,
  210. UMAC_RESET_STATE_DO_POST_RESET_START_RECEIVED);
  211. action = UMAC_RESET_ACTION_DO_POST_RESET_START;
  212. break;
  213. case UMAC_RESET_RX_EVENT_DO_POST_RESET_COMPELTE:
  214. status = dp_umac_reset_validate_n_update_state_machine_on_rx(
  215. umac_reset_ctx, rx_event,
  216. UMAC_RESET_STATE_WAIT_FOR_DO_POST_RESET_COMPLETE,
  217. UMAC_RESET_STATE_DO_POST_RESET_COMPLETE_RECEIVED);
  218. action = UMAC_RESET_ACTION_DO_POST_RESET_COMPLETE;
  219. break;
  220. case UMAC_RESET_RX_EVENT_ERROR:
  221. dp_umac_reset_err("Error Rx event");
  222. goto exit;
  223. default:
  224. dp_umac_reset_err("Invalid value(%u) for Rx event", rx_event);
  225. goto exit;
  226. }
  227. /* Call the handler for this event */
  228. if (QDF_IS_STATUS_SUCCESS(status)) {
  229. if (!umac_reset_ctx->rx_actions.cb[action]) {
  230. dp_umac_reset_err("rx callback is NULL");
  231. goto exit;
  232. }
  233. status = umac_reset_ctx->rx_actions.cb[action](soc);
  234. }
  235. exit:
  236. return qdf_status_to_os_return(status);
  237. }
  238. QDF_STATUS dp_umac_reset_interrupt_attach(struct dp_soc *soc)
  239. {
  240. struct dp_soc_umac_reset_ctx *umac_reset_ctx;
  241. int msi_vector_count, ret;
  242. uint32_t msi_base_data, msi_vector_start;
  243. uint32_t umac_reset_vector, umac_reset_irq;
  244. if (!soc) {
  245. dp_umac_reset_err("DP SOC is null");
  246. return QDF_STATUS_E_NULL_VALUE;
  247. }
  248. umac_reset_ctx = &soc->umac_reset_ctx;
  249. /* return if feature is not supported */
  250. if (!umac_reset_ctx->supported) {
  251. dp_umac_reset_info("UMAC reset is not supported on this SOC");
  252. return QDF_STATUS_SUCCESS;
  253. }
  254. if (pld_get_enable_intx(soc->osdev->dev)) {
  255. dp_umac_reset_err("UMAC reset is not supported in legacy interrupt mode");
  256. return QDF_STATUS_E_FAILURE;
  257. }
  258. ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP",
  259. &msi_vector_count, &msi_base_data,
  260. &msi_vector_start);
  261. if (ret) {
  262. dp_umac_reset_err("UMAC reset is only supported in MSI interrupt mode");
  263. return QDF_STATUS_E_FAILURE;
  264. }
  265. if (umac_reset_ctx->intr_offset < 0 ||
  266. umac_reset_ctx->intr_offset >= WLAN_CFG_INT_NUM_CONTEXTS) {
  267. dp_umac_reset_err("Invalid interrupt offset");
  268. return QDF_STATUS_E_FAILURE;
  269. }
  270. umac_reset_vector = msi_vector_start +
  271. (umac_reset_ctx->intr_offset % msi_vector_count);
  272. /* Get IRQ number */
  273. umac_reset_irq = pld_get_msi_irq(soc->osdev->dev, umac_reset_vector);
  274. /* Finally register to this IRQ from HIF layer */
  275. return hif_register_umac_reset_handler(
  276. soc->hif_handle,
  277. dp_umac_reset_rx_event_handler,
  278. &soc->intr_ctx[umac_reset_ctx->intr_offset],
  279. umac_reset_irq);
  280. }
  281. QDF_STATUS dp_umac_reset_interrupt_detach(struct dp_soc *soc)
  282. {
  283. struct dp_soc_umac_reset_ctx *umac_reset_ctx;
  284. if (!soc) {
  285. dp_umac_reset_err("DP SOC is null");
  286. return QDF_STATUS_E_NULL_VALUE;
  287. }
  288. umac_reset_ctx = &soc->umac_reset_ctx;
  289. /* return if feature is not supported */
  290. if (!umac_reset_ctx->supported) {
  291. dp_umac_reset_info("UMAC reset is not supported on this SOC");
  292. return QDF_STATUS_SUCCESS;
  293. }
  294. return hif_unregister_umac_reset_handler(soc->hif_handle);
  295. }
  296. QDF_STATUS dp_umac_reset_register_rx_action_callback(
  297. struct dp_soc *soc,
  298. QDF_STATUS (*handler)(struct dp_soc *soc),
  299. enum umac_reset_action action)
  300. {
  301. struct dp_soc_umac_reset_ctx *umac_reset_ctx;
  302. if (!soc) {
  303. dp_umac_reset_err("DP SOC is null");
  304. return QDF_STATUS_E_NULL_VALUE;
  305. }
  306. if (action >= UMAC_RESET_ACTION_MAX) {
  307. dp_umac_reset_err("invalid action: %d", action);
  308. return QDF_STATUS_E_INVAL;
  309. }
  310. umac_reset_ctx = &soc->umac_reset_ctx;
  311. umac_reset_ctx->rx_actions.cb[action] = handler;
  312. return QDF_STATUS_SUCCESS;
  313. }