hif_exec.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. * Copyright (c) 2017 The Linux Foundation. All rights reserved.
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for
  5. * any purpose with or without fee is hereby granted, provided that the
  6. * above copyright notice and this permission notice appear in all
  7. * copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  10. * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  11. * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
  12. * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  13. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  14. * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  16. * PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <hif_exec.h>
  19. #include <ce_main.h>
  20. #include <hif_irq_affinity.h>
  21. static void hif_exec_tasklet_schedule(struct hif_exec_context *ctx)
  22. {
  23. struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx);
  24. tasklet_schedule(&t_ctx->tasklet);
  25. }
  26. /**
  27. * hif_exec_tasklet() - grp tasklet
  28. * data: context
  29. *
  30. * return: void
  31. */
  32. static void hif_exec_tasklet_fn(unsigned long data)
  33. {
  34. struct hif_exec_context *hif_ext_group =
  35. (struct hif_exec_context *)data;
  36. struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
  37. unsigned int work_done;
  38. work_done =
  39. hif_ext_group->handler(hif_ext_group->context, HIF_MAX_BUDGET);
  40. if (hif_ext_group->work_complete(hif_ext_group, work_done)) {
  41. qdf_atomic_dec(&(scn->active_grp_tasklet_cnt));
  42. hif_ext_group->irq_enable(hif_ext_group);
  43. } else {
  44. hif_exec_tasklet_schedule(hif_ext_group);
  45. }
  46. }
  47. /**
  48. * hif_exec_poll() - grp tasklet
  49. * data: context
  50. *
  51. * return: void
  52. */
  53. static int hif_exec_poll(struct napi_struct *napi, int budget)
  54. {
  55. struct hif_napi_exec_context *exec_ctx =
  56. qdf_container_of(napi, struct hif_napi_exec_context, napi);
  57. struct hif_exec_context *hif_ext_group = &exec_ctx->exec_ctx;
  58. struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
  59. int work_done;
  60. work_done = hif_ext_group->handler(hif_ext_group->context, budget);
  61. if (hif_ext_group->work_complete(hif_ext_group, work_done)) {
  62. if (work_done >= budget)
  63. work_done = budget - 1;
  64. napi_complete(napi);
  65. qdf_atomic_dec(&scn->active_grp_tasklet_cnt);
  66. hif_ext_group->irq_enable(hif_ext_group);
  67. } else {
  68. /* if the ext_group supports time based yield, claim full work
  69. * done anyways */
  70. work_done = budget;
  71. }
  72. return work_done;
  73. }
  74. /**
  75. * hif_exec_napi_schedule() - schedule the napi exec instance
  76. * @ctx: a hif_exec_context known to be of napi type
  77. */
  78. static void hif_exec_napi_schedule(struct hif_exec_context *ctx)
  79. {
  80. struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx);
  81. napi_schedule(&n_ctx->napi);
  82. }
  83. /**
  84. * hif_exec_napi_kill() - stop a napi exec context from being rescheduled
  85. * @ctx: a hif_exec_context known to be of napi type
  86. */
  87. static void hif_exec_napi_kill(struct hif_exec_context *ctx)
  88. {
  89. struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx);
  90. int irq_ind;
  91. if (ctx->inited) {
  92. napi_disable(&n_ctx->napi);
  93. ctx->inited = 0;
  94. }
  95. for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++)
  96. hif_irq_affinity_remove(ctx->os_irq[irq_ind]);
  97. }
  98. struct hif_execution_ops napi_sched_ops = {
  99. .schedule = &hif_exec_napi_schedule,
  100. .kill = &hif_exec_napi_kill,
  101. };
  102. #ifdef FEATURE_NAPI
  103. /**
  104. * hif_exec_napi_create() - allocate and initialize a napi exec context
  105. */
  106. static struct hif_exec_context *hif_exec_napi_create(void)
  107. {
  108. struct hif_napi_exec_context *ctx;
  109. ctx = qdf_mem_malloc(sizeof(struct hif_napi_exec_context));
  110. if (ctx == NULL)
  111. return NULL;
  112. ctx->exec_ctx.sched_ops = &napi_sched_ops;
  113. ctx->exec_ctx.inited = true;
  114. init_dummy_netdev(&(ctx->netdev));
  115. netif_napi_add(&(ctx->netdev), &(ctx->napi), hif_exec_poll,
  116. QCA_NAPI_BUDGET);
  117. napi_enable(&ctx->napi);
  118. return &ctx->exec_ctx;
  119. }
  120. #else
  121. static struct hif_exec_context *hif_exec_napi_create(void)
  122. {
  123. HIF_WARN("%s: FEATURE_NAPI not defined, making tasklet");
  124. return hif_exec_tasklet_create();
  125. }
  126. #endif
  127. /**
  128. * hif_exec_tasklet_kill() - stop a tasklet exec context from being rescheduled
  129. * @ctx: a hif_exec_context known to be of tasklet type
  130. */
  131. static void hif_exec_tasklet_kill(struct hif_exec_context *ctx)
  132. {
  133. struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx);
  134. int irq_ind;
  135. if (ctx->inited) {
  136. tasklet_disable(&t_ctx->tasklet);
  137. tasklet_kill(&t_ctx->tasklet);
  138. }
  139. ctx->inited = false;
  140. for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++)
  141. hif_irq_affinity_remove(ctx->os_irq[irq_ind]);
  142. }
  143. struct hif_execution_ops tasklet_sched_ops = {
  144. .schedule = &hif_exec_tasklet_schedule,
  145. .kill = &hif_exec_tasklet_kill,
  146. };
  147. /**
  148. * hif_exec_tasklet_schedule() - allocate and initialize a tasklet exec context
  149. */
  150. static struct hif_exec_context *hif_exec_tasklet_create(void)
  151. {
  152. struct hif_tasklet_exec_context *ctx;
  153. ctx = qdf_mem_malloc(sizeof(struct hif_tasklet_exec_context));
  154. if (ctx == NULL)
  155. return NULL;
  156. ctx->exec_ctx.sched_ops = &tasklet_sched_ops;
  157. tasklet_init(&ctx->tasklet, hif_exec_tasklet_fn,
  158. (unsigned long)ctx);
  159. ctx->exec_ctx.inited = true;
  160. return &ctx->exec_ctx;
  161. }
  162. /**
  163. * hif_exec_get_ctx() - retrieve an exec context based on an id
  164. * @softc: the hif context owning the exec context
  165. * @id: the id of the exec context
  166. *
  167. * mostly added to make it easier to rename or move the context array
  168. */
  169. struct hif_exec_context *hif_exec_get_ctx(struct hif_opaque_softc *softc,
  170. uint8_t id)
  171. {
  172. struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(softc);
  173. if (id < hif_state->hif_num_extgroup)
  174. return hif_state->hif_ext_group[id];
  175. return NULL;
  176. }
  177. /**
  178. * hif_configure_ext_group_interrupts() - API to configure external group
  179. * interrpts
  180. * @hif_ctx : HIF Context
  181. *
  182. * Return: status
  183. */
  184. uint32_t hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx)
  185. {
  186. struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
  187. struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
  188. struct hif_exec_context *hif_ext_group;
  189. int i, status;
  190. if (scn->ext_grp_irq_configured) {
  191. HIF_ERROR("%s Called after ext grp irq configured\n", __func__);
  192. return QDF_STATUS_E_FAILURE;
  193. }
  194. for (i = 0; i < hif_state->hif_num_extgroup; i++) {
  195. hif_ext_group = hif_state->hif_ext_group[i];
  196. status = 0;
  197. if (hif_ext_group->configured &&
  198. hif_ext_group->irq_requested == false)
  199. status = hif_grp_irq_configure(scn, hif_ext_group);
  200. if (status != 0)
  201. HIF_ERROR("%s: failed for group %d", __func__, i);
  202. }
  203. scn->ext_grp_irq_configured = true;
  204. return QDF_STATUS_SUCCESS;
  205. }
  206. /**
  207. * hif_ext_group_interrupt_handler() - handler for related interrupts
  208. * @irq: irq number of the interrupt
  209. * @context: the associated hif_exec_group context
  210. *
  211. * This callback function takes care of dissabling the associated interrupts
  212. * and scheduling the expected bottom half for the exec_context.
  213. * This callback function also helps keep track of the count running contexts.
  214. */
  215. irqreturn_t hif_ext_group_interrupt_handler(int irq, void *context)
  216. {
  217. struct hif_exec_context *hif_ext_group = context;
  218. struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
  219. hif_ext_group->irq_disable(hif_ext_group);
  220. qdf_atomic_inc(&scn->active_grp_tasklet_cnt);
  221. hif_ext_group->sched_ops->schedule(hif_ext_group);
  222. return IRQ_HANDLED;
  223. }
  224. /**
  225. * hif_exec_kill() - grp tasklet kill
  226. * scn: hif_softc
  227. *
  228. * return: void
  229. */
  230. void hif_exec_kill(struct hif_opaque_softc *hif_ctx)
  231. {
  232. int i;
  233. struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
  234. for (i = 0; i < hif_state->hif_num_extgroup; i++)
  235. hif_state->hif_ext_group[i]->sched_ops->kill(
  236. hif_state->hif_ext_group[i]);
  237. qdf_atomic_set(&hif_state->ol_sc.active_grp_tasklet_cnt, 0);
  238. }
  239. /**
  240. * hif_register_ext_group() - API to register external group
  241. * interrupt handler.
  242. * @hif_ctx : HIF Context
  243. * @numirq: number of irq's in the group
  244. * @irq: array of irq values
  245. * @handler: callback interrupt handler function
  246. * @cb_ctx: context to passed in callback
  247. * @type: napi vs tasklet
  248. *
  249. * Return: status
  250. */
  251. uint32_t hif_register_ext_group(struct hif_opaque_softc *hif_ctx,
  252. uint32_t numirq, uint32_t irq[], ext_intr_handler handler,
  253. void *cb_ctx, const char *context_name, enum hif_exec_type type)
  254. {
  255. struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
  256. struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
  257. struct hif_exec_context *hif_ext_group;
  258. if (scn->ext_grp_irq_configured) {
  259. HIF_ERROR("%s Called after ext grp irq configured\n", __func__);
  260. return QDF_STATUS_E_FAILURE;
  261. }
  262. if (hif_state->hif_num_extgroup >= HIF_MAX_GROUP) {
  263. HIF_ERROR("%s Max groups reached\n", __func__);
  264. return QDF_STATUS_E_FAILURE;
  265. }
  266. if (numirq >= HIF_MAX_GRP_IRQ) {
  267. HIF_ERROR("%s invalid numirq\n", __func__);
  268. return QDF_STATUS_E_FAILURE;
  269. }
  270. hif_ext_group = hif_exec_create(type);
  271. if (hif_ext_group == NULL)
  272. return QDF_STATUS_E_FAILURE;
  273. hif_state->hif_ext_group[hif_state->hif_num_extgroup] =
  274. hif_ext_group;
  275. hif_ext_group->numirq = numirq;
  276. qdf_mem_copy(&hif_ext_group->irq[0], irq, numirq * sizeof(irq[0]));
  277. hif_ext_group->context = cb_ctx;
  278. hif_ext_group->handler = handler;
  279. hif_ext_group->configured = true;
  280. hif_ext_group->grp_id = hif_state->hif_num_extgroup;
  281. hif_ext_group->hif = hif_ctx;
  282. hif_ext_group->context_name = context_name;
  283. hif_state->hif_num_extgroup++;
  284. return QDF_STATUS_SUCCESS;
  285. }
  286. /**
  287. * hif_exec_create() - create an execution context
  288. * @type: the type of execution context to create
  289. */
  290. struct hif_exec_context *hif_exec_create(enum hif_exec_type type)
  291. {
  292. switch (type) {
  293. case HIF_EXEC_NAPI_TYPE:
  294. return hif_exec_napi_create();
  295. case HIF_EXEC_TASKLET_TYPE:
  296. return hif_exec_tasklet_create();
  297. default:
  298. return NULL;
  299. }
  300. }
  301. /**
  302. * hif_exec_destroy() - free the hif_exec context
  303. * @ctx: context to free
  304. *
  305. * please kill the context before freeing it to avoid a use after free.
  306. */
  307. void hif_exec_destroy(struct hif_exec_context *ctx)
  308. {
  309. qdf_mem_free(ctx);
  310. }