1  /*
2   * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for
6   * any purpose with or without fee is hereby granted, provided that the
7   * above copyright notice and this permission notice appear in all
8   * copies.
9   *
10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17   * PERFORMANCE OF THIS SOFTWARE.
18   */
19  
20  #include <hif_exec.h>
21  #include <ce_main.h>
22  #include "qdf_module.h"
23  #include "qdf_net_if.h"
24  #include <pld_common.h>
25  #ifdef DP_UMAC_HW_RESET_SUPPORT
26  #include "if_pci.h"
27  #endif
28  #include "qdf_ssr_driver_dump.h"
29  
30  /* mapping NAPI budget 0 to internal budget 0
31   * NAPI budget 1 to internal budget [1,scaler -1]
32   * NAPI budget 2 to internal budget [scaler, 2 * scaler - 1], etc
33   */
34  #define NAPI_BUDGET_TO_INTERNAL_BUDGET(n, s) \
35  	(((n) << (s)) - 1)
36  #define INTERNAL_BUDGET_TO_NAPI_BUDGET(n, s) \
37  	(((n) + 1) >> (s))
38  
39  static struct hif_exec_context *hif_exec_tasklet_create(void);
40  
41  #ifdef WLAN_FEATURE_DP_EVENT_HISTORY
42  struct hif_event_history hif_event_desc_history[HIF_NUM_INT_CONTEXTS];
43  uint32_t hif_event_hist_max = HIF_EVENT_HIST_MAX;
44  
hif_desc_history_log_register(void)45  void hif_desc_history_log_register(void)
46  {
47  	qdf_ssr_driver_dump_register_region("hif_event_history",
48  					    hif_event_desc_history,
49  					    sizeof(hif_event_desc_history));
50  	qdf_ssr_driver_dump_register_region("hif_event_hist_max",
51  					    &hif_event_hist_max,
52  					    sizeof(hif_event_hist_max));
53  }
54  
hif_desc_history_log_unregister(void)55  void hif_desc_history_log_unregister(void)
56  {
57  	qdf_ssr_driver_dump_unregister_region("hif_event_hist_max");
58  	qdf_ssr_driver_dump_unregister_region("hif_event_history");
59  }
60  
61  static inline
hif_get_next_record_index(qdf_atomic_t * table_index,int array_size)62  int hif_get_next_record_index(qdf_atomic_t *table_index,
63  			      int array_size)
64  {
65  	int record_index = qdf_atomic_inc_return(table_index);
66  
67  	return record_index & (array_size - 1);
68  }
69  
70  /**
71   * hif_hist_is_prev_record() - Check if index is the immediate
72   *  previous record wrt curr_index
73   * @curr_index: curr index in the event history
74   * @index: index to be checked
75   * @hist_size: history size
76   *
77   * Return: true if index is immediately behind curr_index else false
78   */
79  static inline
hif_hist_is_prev_record(int32_t curr_index,int32_t index,uint32_t hist_size)80  bool hif_hist_is_prev_record(int32_t curr_index, int32_t index,
81  			     uint32_t hist_size)
82  {
83  	return (((index + 1) & (hist_size - 1)) == curr_index) ?
84  			true : false;
85  }
86  
87  /**
88   * hif_hist_skip_event_record() - Check if current event needs to be
89   *  recorded or not
90   * @hist_ev: HIF event history
91   * @event: DP event entry
92   *
93   * Return: true if current event needs to be skipped else false
94   */
95  static bool
hif_hist_skip_event_record(struct hif_event_history * hist_ev,struct hif_event_record * event)96  hif_hist_skip_event_record(struct hif_event_history *hist_ev,
97  			   struct hif_event_record *event)
98  {
99  	struct hif_event_record *rec;
100  	struct hif_event_record *last_irq_rec;
101  	int32_t index;
102  
103  	index = qdf_atomic_read(&hist_ev->index);
104  	if (index < 0)
105  		return false;
106  
107  	index &= (HIF_EVENT_HIST_MAX - 1);
108  	rec = &hist_ev->event[index];
109  
110  	switch (event->type) {
111  	case HIF_EVENT_IRQ_TRIGGER:
112  		/*
113  		 * The prev record check is to prevent skipping the IRQ event
114  		 * record in case where BH got re-scheduled due to force_break
115  		 * but there are no entries to be reaped in the rings.
116  		 */
117  		if (rec->type == HIF_EVENT_BH_SCHED &&
118  		    hif_hist_is_prev_record(index,
119  					    hist_ev->misc.last_irq_index,
120  					    HIF_EVENT_HIST_MAX)) {
121  			last_irq_rec =
122  				&hist_ev->event[hist_ev->misc.last_irq_index];
123  			last_irq_rec->timestamp = hif_get_log_timestamp();
124  			last_irq_rec->cpu_id = qdf_get_cpu();
125  			last_irq_rec->hp++;
126  			last_irq_rec->tp = last_irq_rec->timestamp -
127  						hist_ev->misc.last_irq_ts;
128  			return true;
129  		}
130  		break;
131  	case HIF_EVENT_BH_SCHED:
132  		if (rec->type == HIF_EVENT_BH_SCHED) {
133  			rec->timestamp = hif_get_log_timestamp();
134  			rec->cpu_id = qdf_get_cpu();
135  			return true;
136  		}
137  		break;
138  	case HIF_EVENT_SRNG_ACCESS_START:
139  		if (event->hp == event->tp)
140  			return true;
141  		break;
142  	case HIF_EVENT_SRNG_ACCESS_END:
143  		if (rec->type != HIF_EVENT_SRNG_ACCESS_START)
144  			return true;
145  		break;
146  	case HIF_EVENT_BH_COMPLETE:
147  	case HIF_EVENT_BH_FORCE_BREAK:
148  		if (rec->type != HIF_EVENT_SRNG_ACCESS_END)
149  			return true;
150  		break;
151  	default:
152  		break;
153  	}
154  
155  	return false;
156  }
157  
hif_hist_record_event(struct hif_opaque_softc * hif_ctx,struct hif_event_record * event,uint8_t intr_grp_id)158  void hif_hist_record_event(struct hif_opaque_softc *hif_ctx,
159  			   struct hif_event_record *event, uint8_t intr_grp_id)
160  {
161  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
162  	struct hif_event_history *hist_ev;
163  	struct hif_event_record *record;
164  	int record_index;
165  
166  	if (!(scn->event_enable_mask & BIT(event->type)))
167  		return;
168  
169  	if (qdf_unlikely(intr_grp_id >= HIF_NUM_INT_CONTEXTS)) {
170  		hif_err("Invalid interrupt group id %d", intr_grp_id);
171  		return;
172  	}
173  
174  	hist_ev = scn->evt_hist[intr_grp_id];
175  	if (qdf_unlikely(!hist_ev))
176  		return;
177  
178  	if (hif_hist_skip_event_record(hist_ev, event))
179  		return;
180  
181  	record_index = hif_get_next_record_index(
182  			&hist_ev->index, HIF_EVENT_HIST_MAX);
183  
184  	record = &hist_ev->event[record_index];
185  
186  	if (event->type == HIF_EVENT_IRQ_TRIGGER) {
187  		hist_ev->misc.last_irq_index = record_index;
188  		hist_ev->misc.last_irq_ts = hif_get_log_timestamp();
189  	}
190  
191  	record->hal_ring_id = event->hal_ring_id;
192  	record->hp = event->hp;
193  	record->tp = event->tp;
194  	record->cpu_id = qdf_get_cpu();
195  	record->timestamp = hif_get_log_timestamp();
196  	record->type = event->type;
197  }
198  
hif_event_history_init(struct hif_opaque_softc * hif_ctx,uint8_t id)199  void hif_event_history_init(struct hif_opaque_softc *hif_ctx, uint8_t id)
200  {
201  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
202  
203  	scn->evt_hist[id] = &hif_event_desc_history[id];
204  	qdf_atomic_set(&scn->evt_hist[id]->index, -1);
205  
206  	hif_info("SRNG events history initialized for group: %d", id);
207  }
208  
hif_event_history_deinit(struct hif_opaque_softc * hif_ctx,uint8_t id)209  void hif_event_history_deinit(struct hif_opaque_softc *hif_ctx, uint8_t id)
210  {
211  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
212  
213  	scn->evt_hist[id] = NULL;
214  	hif_info("SRNG events history de-initialized for group: %d", id);
215  }
216  #endif /* WLAN_FEATURE_DP_EVENT_HISTORY */
217  
218  #ifndef QCA_WIFI_WCN6450
219  /**
220   * hif_print_napi_latency_stats() - print NAPI scheduling latency stats
221   * @hif_state: hif context
222   *
223   * return: void
224   */
225  #ifdef HIF_LATENCY_PROFILE_ENABLE
hif_print_napi_latency_stats(struct HIF_CE_state * hif_state)226  static void hif_print_napi_latency_stats(struct HIF_CE_state *hif_state)
227  {
228  	struct hif_exec_context *hif_ext_group;
229  	int i, j;
230  	int64_t cur_tstamp;
231  
232  	const char time_str[HIF_SCHED_LATENCY_BUCKETS][15] =  {
233  		"0-2   ms",
234  		"3-10  ms",
235  		"11-20 ms",
236  		"21-50 ms",
237  		"51-100 ms",
238  		"101-250 ms",
239  		"251-500 ms",
240  		"> 500 ms"
241  	};
242  
243  	cur_tstamp = qdf_ktime_to_ms(qdf_ktime_get());
244  
245  	QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_INFO_HIGH,
246  		  "Current timestamp: %lld", cur_tstamp);
247  
248  	for (i = 0; i < hif_state->hif_num_extgroup; i++) {
249  		if (hif_state->hif_ext_group[i]) {
250  			hif_ext_group = hif_state->hif_ext_group[i];
251  
252  			QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_INFO_HIGH,
253  				  "ext grp %d Last serviced timestamp: %lld",
254  				  i, hif_ext_group->tstamp);
255  
256  			QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_INFO_HIGH,
257  				  "Latency Bucket     | Time elapsed");
258  
259  			for (j = 0; j < HIF_SCHED_LATENCY_BUCKETS; j++) {
260  				if (hif_ext_group->sched_latency_stats[j])
261  					QDF_TRACE(QDF_MODULE_ID_HIF,
262  						  QDF_TRACE_LEVEL_INFO_HIGH,
263  						  "%s     |    %lld",
264  						  time_str[j],
265  						  hif_ext_group->
266  						  sched_latency_stats[j]);
267  			}
268  		}
269  	}
270  }
271  #else
hif_print_napi_latency_stats(struct HIF_CE_state * hif_state)272  static void hif_print_napi_latency_stats(struct HIF_CE_state *hif_state)
273  {
274  }
275  #endif
276  
277  /**
278   * hif_clear_napi_stats() - reset NAPI stats
279   * @hif_ctx: hif context
280   *
281   * return: void
282   */
hif_clear_napi_stats(struct hif_opaque_softc * hif_ctx)283  void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx)
284  {
285  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
286  	struct hif_exec_context *hif_ext_group;
287  	size_t i;
288  
289  	for (i = 0; i < hif_state->hif_num_extgroup; i++) {
290  		hif_ext_group = hif_state->hif_ext_group[i];
291  
292  		if (!hif_ext_group)
293  			return;
294  
295  		qdf_mem_set(hif_ext_group->sched_latency_stats,
296  			    sizeof(hif_ext_group->sched_latency_stats),
297  			    0x0);
298  	}
299  }
300  
301  qdf_export_symbol(hif_clear_napi_stats);
302  
303  #ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT
304  /**
305   * hif_get_poll_times_hist_str() - Get HIF poll times histogram string
306   * @stats: NAPI stats to get poll time buckets
307   * @buf: buffer to fill histogram string
308   * @buf_len: length of the buffer
309   *
310   * Return: void
311   */
hif_get_poll_times_hist_str(struct qca_napi_stat * stats,char * buf,uint8_t buf_len)312  static void hif_get_poll_times_hist_str(struct qca_napi_stat *stats, char *buf,
313  					uint8_t buf_len)
314  {
315  	int i;
316  	int str_index = 0;
317  
318  	for (i = 0; i < QCA_NAPI_NUM_BUCKETS; i++)
319  		str_index += qdf_scnprintf(buf + str_index, buf_len - str_index,
320  					   "%u|", stats->poll_time_buckets[i]);
321  }
322  
hif_print_napi_stats(struct hif_opaque_softc * hif_ctx)323  void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx)
324  {
325  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
326  	struct hif_exec_context *hif_ext_group;
327  	struct qca_napi_stat *napi_stats;
328  	int i, j;
329  
330  	/*
331  	 * Max value of uint_32 (poll_time_bucket) = 4294967295
332  	 * Thus we need 10 chars + 1 space =11 chars for each bucket value.
333  	 * +1 space for '\0'.
334  	 */
335  	char hist_str[(QCA_NAPI_NUM_BUCKETS * 11) + 1] = {'\0'};
336  
337  	QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_INFO_HIGH,
338  		  "NAPI[#]CPU[#] |scheds |polls  |comps  |dones  |t-lim  |max(us)|hist(500us buckets)");
339  
340  	for (i = 0;
341  	     (i < hif_state->hif_num_extgroup && hif_state->hif_ext_group[i]);
342  	     i++) {
343  		hif_ext_group = hif_state->hif_ext_group[i];
344  		for (j = 0; j < num_possible_cpus(); j++) {
345  			napi_stats = &hif_ext_group->stats[j];
346  			if (!napi_stats->napi_schedules)
347  				continue;
348  
349  			hif_get_poll_times_hist_str(napi_stats,
350  						    hist_str,
351  						    sizeof(hist_str));
352  			QDF_TRACE(QDF_MODULE_ID_HIF,
353  				  QDF_TRACE_LEVEL_INFO_HIGH,
354  				  "NAPI[%d]CPU[%d]: %7u %7u %7u %7u %7u %7llu %s",
355  				  i, j,
356  				  napi_stats->napi_schedules,
357  				  napi_stats->napi_polls,
358  				  napi_stats->napi_completes,
359  				  napi_stats->napi_workdone,
360  				  napi_stats->time_limit_reached,
361  				  qdf_do_div(napi_stats->napi_max_poll_time,
362  					     1000),
363  				  hist_str);
364  		}
365  	}
366  
367  	hif_print_napi_latency_stats(hif_state);
368  }
369  
370  qdf_export_symbol(hif_print_napi_stats);
371  #else
372  static inline
hif_get_poll_times_hist_str(struct qca_napi_stat * stats,char * buf,uint8_t buf_len)373  void hif_get_poll_times_hist_str(struct qca_napi_stat *stats, char *buf,
374  				 uint8_t buf_len)
375  {
376  }
377  
hif_print_napi_stats(struct hif_opaque_softc * hif_ctx)378  void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx)
379  {
380  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
381  	struct hif_exec_context *hif_ext_group;
382  	struct qca_napi_stat *napi_stats;
383  	int i, j;
384  
385  	QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL,
386  		"NAPI[#ctx]CPU[#] |schedules |polls |completes |workdone");
387  
388  	for (i = 0; i < hif_state->hif_num_extgroup; i++) {
389  		if (hif_state->hif_ext_group[i]) {
390  			hif_ext_group = hif_state->hif_ext_group[i];
391  			for (j = 0; j < num_possible_cpus(); j++) {
392  				napi_stats = &(hif_ext_group->stats[j]);
393  				if (napi_stats->napi_schedules != 0)
394  					QDF_TRACE(QDF_MODULE_ID_HIF,
395  						QDF_TRACE_LEVEL_FATAL,
396  						"NAPI[%2d]CPU[%d]: "
397  						"%7d %7d %7d %7d ",
398  						i, j,
399  						napi_stats->napi_schedules,
400  						napi_stats->napi_polls,
401  						napi_stats->napi_completes,
402  						napi_stats->napi_workdone);
403  			}
404  		}
405  	}
406  
407  	hif_print_napi_latency_stats(hif_state);
408  }
409  qdf_export_symbol(hif_print_napi_stats);
410  #endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */
411  #endif /* QCA_WIFI_WCN6450 */
412  
413  #ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT
414  /**
415   * hif_exec_fill_poll_time_histogram() - fills poll time histogram for a NAPI
416   * @hif_ext_group: hif_ext_group of type NAPI
417   *
418   * The function is called at the end of a NAPI poll to calculate poll time
419   * buckets.
420   *
421   * Return: void
422   */
423  static
hif_exec_fill_poll_time_histogram(struct hif_exec_context * hif_ext_group)424  void hif_exec_fill_poll_time_histogram(struct hif_exec_context *hif_ext_group)
425  {
426  	struct qca_napi_stat *napi_stat;
427  	unsigned long long poll_time_ns;
428  	uint32_t poll_time_us;
429  	uint32_t bucket_size_us = 500;
430  	uint32_t bucket;
431  	uint32_t cpu_id = qdf_get_cpu();
432  
433  	poll_time_ns = qdf_time_sched_clock() - hif_ext_group->poll_start_time;
434  	poll_time_us = qdf_do_div(poll_time_ns, 1000);
435  
436  	napi_stat = &hif_ext_group->stats[cpu_id];
437  	if (poll_time_ns > hif_ext_group->stats[cpu_id].napi_max_poll_time)
438  		hif_ext_group->stats[cpu_id].napi_max_poll_time = poll_time_ns;
439  
440  	bucket = poll_time_us / bucket_size_us;
441  	if (bucket >= QCA_NAPI_NUM_BUCKETS)
442  		bucket = QCA_NAPI_NUM_BUCKETS - 1;
443  	++napi_stat->poll_time_buckets[bucket];
444  }
445  
446  /**
447   * hif_exec_poll_should_yield() - Local function deciding if NAPI should yield
448   * @hif_ext_group: hif_ext_group of type NAPI
449   *
450   * Return: true if NAPI needs to yield, else false
451   */
hif_exec_poll_should_yield(struct hif_exec_context * hif_ext_group)452  static bool hif_exec_poll_should_yield(struct hif_exec_context *hif_ext_group)
453  {
454  	bool time_limit_reached = false;
455  	unsigned long long poll_time_ns;
456  	int cpu_id = qdf_get_cpu();
457  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
458  	struct hif_config_info *cfg = &scn->hif_config;
459  
460  	poll_time_ns = qdf_time_sched_clock() - hif_ext_group->poll_start_time;
461  	time_limit_reached =
462  		poll_time_ns > cfg->rx_softirq_max_yield_duration_ns ? 1 : 0;
463  
464  	if (time_limit_reached) {
465  		hif_ext_group->stats[cpu_id].time_limit_reached++;
466  		hif_ext_group->force_break = true;
467  	}
468  
469  	return time_limit_reached;
470  }
471  
hif_exec_should_yield(struct hif_opaque_softc * hif_ctx,uint grp_id)472  bool hif_exec_should_yield(struct hif_opaque_softc *hif_ctx, uint grp_id)
473  {
474  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
475  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
476  	struct hif_exec_context *hif_ext_group;
477  	bool ret_val = false;
478  
479  	if (!(grp_id < hif_state->hif_num_extgroup) ||
480  	    !(grp_id < HIF_MAX_GROUP))
481  		return false;
482  
483  	hif_ext_group = hif_state->hif_ext_group[grp_id];
484  
485  	if (hif_ext_group->type == HIF_EXEC_NAPI_TYPE)
486  		ret_val = hif_exec_poll_should_yield(hif_ext_group);
487  
488  	return ret_val;
489  }
490  
491  /**
492   * hif_exec_update_service_start_time() - Update NAPI poll start time
493   * @hif_ext_group: hif_ext_group of type NAPI
494   *
495   * The function is called at the beginning of a NAPI poll to record the poll
496   * start time.
497   *
498   * Return: None
499   */
500  static inline
hif_exec_update_service_start_time(struct hif_exec_context * hif_ext_group)501  void hif_exec_update_service_start_time(struct hif_exec_context *hif_ext_group)
502  {
503  	hif_ext_group->poll_start_time = qdf_time_sched_clock();
504  }
505  
506  #else
507  static inline
hif_exec_update_service_start_time(struct hif_exec_context * hif_ext_group)508  void hif_exec_update_service_start_time(struct hif_exec_context *hif_ext_group)
509  {
510  }
511  
512  static inline
hif_exec_fill_poll_time_histogram(struct hif_exec_context * hif_ext_group)513  void hif_exec_fill_poll_time_histogram(struct hif_exec_context *hif_ext_group)
514  {
515  }
516  #endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */
517  
hif_exec_tasklet_schedule(struct hif_exec_context * ctx)518  static void hif_exec_tasklet_schedule(struct hif_exec_context *ctx)
519  {
520  	struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx);
521  
522  	tasklet_schedule(&t_ctx->tasklet);
523  }
524  
525  /**
526   * hif_exec_tasklet_fn() - grp tasklet
527   * @data: context
528   *
529   * Return: void
530   */
hif_exec_tasklet_fn(unsigned long data)531  static void hif_exec_tasklet_fn(unsigned long data)
532  {
533  	struct hif_exec_context *hif_ext_group =
534  			(struct hif_exec_context *)data;
535  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
536  	unsigned int work_done;
537  	int cpu = smp_processor_id();
538  
539  	work_done =
540  		hif_ext_group->handler(hif_ext_group->context, HIF_MAX_BUDGET,
541  				       cpu);
542  
543  	if (hif_ext_group->work_complete(hif_ext_group, work_done)) {
544  		qdf_atomic_dec(&(scn->active_grp_tasklet_cnt));
545  		hif_ext_group->irq_enable(hif_ext_group);
546  	} else {
547  		hif_exec_tasklet_schedule(hif_ext_group);
548  	}
549  }
550  
551  /**
552   * hif_latency_profile_measure() - calculate latency and update histogram
553   * @hif_ext_group: hif exec context
554   *
555   * Return: None
556   */
557  #ifdef HIF_LATENCY_PROFILE_ENABLE
hif_latency_profile_measure(struct hif_exec_context * hif_ext_group)558  static void hif_latency_profile_measure(struct hif_exec_context *hif_ext_group)
559  {
560  	int64_t cur_tstamp;
561  	int64_t time_elapsed;
562  
563  	cur_tstamp = qdf_ktime_to_ms(qdf_ktime_get());
564  
565  	if (cur_tstamp > hif_ext_group->tstamp)
566  		time_elapsed = (cur_tstamp - hif_ext_group->tstamp);
567  	else
568  		time_elapsed = ~0x0 - (hif_ext_group->tstamp - cur_tstamp);
569  
570  	hif_ext_group->tstamp = cur_tstamp;
571  
572  	if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_0_2)
573  		hif_ext_group->sched_latency_stats[0]++;
574  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_3_10)
575  		hif_ext_group->sched_latency_stats[1]++;
576  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_11_20)
577  		hif_ext_group->sched_latency_stats[2]++;
578  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_21_50)
579  		hif_ext_group->sched_latency_stats[3]++;
580  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_51_100)
581  		hif_ext_group->sched_latency_stats[4]++;
582  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_101_250)
583  		hif_ext_group->sched_latency_stats[5]++;
584  	else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_251_500)
585  		hif_ext_group->sched_latency_stats[6]++;
586  	else
587  		hif_ext_group->sched_latency_stats[7]++;
588  }
589  #else
590  static inline
hif_latency_profile_measure(struct hif_exec_context * hif_ext_group)591  void hif_latency_profile_measure(struct hif_exec_context *hif_ext_group)
592  {
593  }
594  #endif
595  
596  /**
597   * hif_latency_profile_start() - Update the start timestamp for HIF ext group
598   * @hif_ext_group: hif exec context
599   *
600   * Return: None
601   */
602  #ifdef HIF_LATENCY_PROFILE_ENABLE
hif_latency_profile_start(struct hif_exec_context * hif_ext_group)603  static void hif_latency_profile_start(struct hif_exec_context *hif_ext_group)
604  {
605  	hif_ext_group->tstamp = qdf_ktime_to_ms(qdf_ktime_get());
606  }
607  #else
608  static inline
hif_latency_profile_start(struct hif_exec_context * hif_ext_group)609  void hif_latency_profile_start(struct hif_exec_context *hif_ext_group)
610  {
611  }
612  #endif
613  
614  #ifdef FEATURE_NAPI
615  #ifdef FEATURE_IRQ_AFFINITY
616  static inline int32_t
hif_is_force_napi_complete_required(struct hif_exec_context * hif_ext_group)617  hif_is_force_napi_complete_required(struct hif_exec_context *hif_ext_group)
618  {
619  	return qdf_atomic_inc_not_zero(&hif_ext_group->force_napi_complete);
620  }
621  #else
622  static inline int32_t
hif_is_force_napi_complete_required(struct hif_exec_context * hif_ext_group)623  hif_is_force_napi_complete_required(struct hif_exec_context *hif_ext_group)
624  {
625  	return 0;
626  }
627  #endif
628  
629  /**
630   * hif_irq_disabled_time_limit_reached() - determine if irq disabled limit
631   * reached for single MSI
632   * @hif_ext_group: hif exec context
633   *
634   * Return: true if reached, else false.
635   */
636  static bool
hif_irq_disabled_time_limit_reached(struct hif_exec_context * hif_ext_group)637  hif_irq_disabled_time_limit_reached(struct hif_exec_context *hif_ext_group)
638  {
639  	unsigned long long irq_disabled_duration_ns;
640  
641  	if (hif_ext_group->type != HIF_EXEC_NAPI_TYPE)
642  		return false;
643  
644  	irq_disabled_duration_ns = qdf_time_sched_clock() -
645  					hif_ext_group->irq_disabled_start_time;
646  	if (irq_disabled_duration_ns >= IRQ_DISABLED_MAX_DURATION_NS) {
647  		hif_record_event(hif_ext_group->hif, hif_ext_group->grp_id,
648  				 0, 0, 0, HIF_EVENT_IRQ_DISABLE_EXPIRED);
649  		return true;
650  	}
651  
652  	return false;
653  }
654  
655  /**
656   * hif_exec_poll() - napi poll
657   * @napi: napi struct
658   * @budget: budget for napi
659   *
660   * Return: mapping of internal budget to napi
661   */
hif_exec_poll(struct napi_struct * napi,int budget)662  static int hif_exec_poll(struct napi_struct *napi, int budget)
663  {
664  	struct hif_napi_exec_context *napi_exec_ctx =
665  		    qdf_container_of(napi, struct hif_napi_exec_context, napi);
666  	struct hif_exec_context *hif_ext_group = &napi_exec_ctx->exec_ctx;
667  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
668  	int work_done;
669  	int normalized_budget = 0;
670  	int actual_dones;
671  	int shift = hif_ext_group->scale_bin_shift;
672  	int cpu = smp_processor_id();
673  	bool force_complete = false;
674  
675  	hif_record_event(hif_ext_group->hif, hif_ext_group->grp_id,
676  			 0, 0, 0, HIF_EVENT_BH_SCHED);
677  
678  	hif_ext_group->force_break = false;
679  	hif_exec_update_service_start_time(hif_ext_group);
680  
681  	if (budget)
682  		normalized_budget = NAPI_BUDGET_TO_INTERNAL_BUDGET(budget, shift);
683  
684  	hif_latency_profile_measure(hif_ext_group);
685  
686  	work_done = hif_ext_group->handler(hif_ext_group->context,
687  					   normalized_budget, cpu);
688  
689  	actual_dones = work_done;
690  
691  	if (hif_is_force_napi_complete_required(hif_ext_group)) {
692  		force_complete = true;
693  		if (work_done >= normalized_budget)
694  			work_done = normalized_budget - 1;
695  	}
696  
697  	if (qdf_unlikely(force_complete) ||
698  	    (!hif_ext_group->force_break && work_done < normalized_budget) ||
699  	    ((pld_is_one_msi(scn->qdf_dev->dev) &&
700  	    hif_irq_disabled_time_limit_reached(hif_ext_group)))) {
701  		hif_record_event(hif_ext_group->hif, hif_ext_group->grp_id,
702  				 0, 0, 0, HIF_EVENT_BH_COMPLETE);
703  		napi_complete(napi);
704  		qdf_atomic_dec(&scn->active_grp_tasklet_cnt);
705  		hif_ext_group->irq_enable(hif_ext_group);
706  		hif_ext_group->stats[cpu].napi_completes++;
707  	} else {
708  		/* if the ext_group supports time based yield, claim full work
709  		 * done anyways */
710  		hif_record_event(hif_ext_group->hif, hif_ext_group->grp_id,
711  				 0, 0, 0, HIF_EVENT_BH_FORCE_BREAK);
712  		work_done = normalized_budget;
713  	}
714  
715  	hif_ext_group->stats[cpu].napi_polls++;
716  	hif_ext_group->stats[cpu].napi_workdone += actual_dones;
717  
718  	/* map internal budget to NAPI budget */
719  	if (work_done)
720  		work_done = INTERNAL_BUDGET_TO_NAPI_BUDGET(work_done, shift);
721  
722  	hif_exec_fill_poll_time_histogram(hif_ext_group);
723  
724  	return work_done;
725  }
726  
727  /**
728   * hif_exec_napi_schedule() - schedule the napi exec instance
729   * @ctx: a hif_exec_context known to be of napi type
730   */
hif_exec_napi_schedule(struct hif_exec_context * ctx)731  static void hif_exec_napi_schedule(struct hif_exec_context *ctx)
732  {
733  	struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx);
734  	ctx->stats[smp_processor_id()].napi_schedules++;
735  
736  	napi_schedule(&n_ctx->napi);
737  }
738  
739  /**
740   * hif_exec_napi_kill() - stop a napi exec context from being rescheduled
741   * @ctx: a hif_exec_context known to be of napi type
742   */
hif_exec_napi_kill(struct hif_exec_context * ctx)743  static void hif_exec_napi_kill(struct hif_exec_context *ctx)
744  {
745  	struct hif_napi_exec_context *n_ctx = hif_exec_get_napi(ctx);
746  	int irq_ind;
747  
748  	if (ctx->inited) {
749  		qdf_napi_disable(&n_ctx->napi);
750  		ctx->inited = 0;
751  	}
752  
753  	for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++)
754  		hif_irq_affinity_remove(ctx->os_irq[irq_ind]);
755  
756  	hif_core_ctl_set_boost(false);
757  	qdf_netif_napi_del(&(n_ctx->napi));
758  }
759  
760  struct hif_execution_ops napi_sched_ops = {
761  	.schedule = &hif_exec_napi_schedule,
762  	.kill = &hif_exec_napi_kill,
763  };
764  
765  /**
766   * hif_exec_napi_create() - allocate and initialize a napi exec context
767   * @scale: a binary shift factor to map NAPI budget from\to internal
768   *         budget
769   */
hif_exec_napi_create(uint32_t scale)770  static struct hif_exec_context *hif_exec_napi_create(uint32_t scale)
771  {
772  	struct hif_napi_exec_context *ctx;
773  
774  	ctx = qdf_mem_malloc(sizeof(struct hif_napi_exec_context));
775  	if (!ctx)
776  		return NULL;
777  
778  	ctx->exec_ctx.sched_ops = &napi_sched_ops;
779  	ctx->exec_ctx.inited = true;
780  	ctx->exec_ctx.scale_bin_shift = scale;
781  	qdf_net_if_create_dummy_if((struct qdf_net_if *)&ctx->netdev);
782  	qdf_netif_napi_add(&(ctx->netdev), &(ctx->napi), hif_exec_poll,
783  			   QCA_NAPI_BUDGET);
784  	qdf_napi_enable(&ctx->napi);
785  
786  	return &ctx->exec_ctx;
787  }
788  #else
hif_exec_napi_create(uint32_t scale)789  static struct hif_exec_context *hif_exec_napi_create(uint32_t scale)
790  {
791  	hif_warn("FEATURE_NAPI not defined, making tasklet");
792  	return hif_exec_tasklet_create();
793  }
794  #endif
795  
796  
797  /**
798   * hif_exec_tasklet_kill() - stop a tasklet exec context from being rescheduled
799   * @ctx: a hif_exec_context known to be of tasklet type
800   */
hif_exec_tasklet_kill(struct hif_exec_context * ctx)801  static void hif_exec_tasklet_kill(struct hif_exec_context *ctx)
802  {
803  	struct hif_tasklet_exec_context *t_ctx = hif_exec_get_tasklet(ctx);
804  	int irq_ind;
805  
806  	if (ctx->inited) {
807  		tasklet_disable(&t_ctx->tasklet);
808  		tasklet_kill(&t_ctx->tasklet);
809  	}
810  	ctx->inited = false;
811  
812  	for (irq_ind = 0; irq_ind < ctx->numirq; irq_ind++)
813  		hif_irq_affinity_remove(ctx->os_irq[irq_ind]);
814  }
815  
816  struct hif_execution_ops tasklet_sched_ops = {
817  	.schedule = &hif_exec_tasklet_schedule,
818  	.kill = &hif_exec_tasklet_kill,
819  };
820  
821  /**
822   * hif_exec_tasklet_create() -  allocate and initialize a tasklet exec context
823   */
hif_exec_tasklet_create(void)824  static struct hif_exec_context *hif_exec_tasklet_create(void)
825  {
826  	struct hif_tasklet_exec_context *ctx;
827  
828  	ctx = qdf_mem_malloc(sizeof(struct hif_tasklet_exec_context));
829  	if (!ctx)
830  		return NULL;
831  
832  	ctx->exec_ctx.sched_ops = &tasklet_sched_ops;
833  	tasklet_init(&ctx->tasklet, hif_exec_tasklet_fn,
834  		     (unsigned long)ctx);
835  
836  	ctx->exec_ctx.inited = true;
837  
838  	return &ctx->exec_ctx;
839  }
840  
841  /**
842   * hif_exec_get_ctx() - retrieve an exec context based on an id
843   * @softc: the hif context owning the exec context
844   * @id: the id of the exec context
845   *
846   * mostly added to make it easier to rename or move the context array
847   */
hif_exec_get_ctx(struct hif_opaque_softc * softc,uint8_t id)848  struct hif_exec_context *hif_exec_get_ctx(struct hif_opaque_softc *softc,
849  					  uint8_t id)
850  {
851  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(softc);
852  
853  	if (id < hif_state->hif_num_extgroup)
854  		return hif_state->hif_ext_group[id];
855  
856  	return NULL;
857  }
858  
hif_get_int_ctx_irq_num(struct hif_opaque_softc * softc,uint8_t id)859  int32_t hif_get_int_ctx_irq_num(struct hif_opaque_softc *softc,
860  				uint8_t id)
861  {
862  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(softc);
863  
864  	if (id < hif_state->hif_num_extgroup)
865  		return hif_state->hif_ext_group[id]->os_irq[0];
866  	return -EINVAL;
867  }
868  
869  qdf_export_symbol(hif_get_int_ctx_irq_num);
870  
hif_configure_ext_group_interrupts(struct hif_opaque_softc * hif_ctx)871  QDF_STATUS hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx)
872  {
873  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
874  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
875  	struct hif_exec_context *hif_ext_group;
876  	int i, status;
877  
878  	if (scn->ext_grp_irq_configured) {
879  		hif_err("Called after ext grp irq configured");
880  		return QDF_STATUS_E_FAILURE;
881  	}
882  
883  	for (i = 0; i < hif_state->hif_num_extgroup; i++) {
884  		hif_ext_group = hif_state->hif_ext_group[i];
885  		status = 0;
886  		qdf_spinlock_create(&hif_ext_group->irq_lock);
887  		if (hif_ext_group->configured &&
888  		    hif_ext_group->irq_requested == false) {
889  			hif_ext_group->irq_enabled = true;
890  			status = hif_grp_irq_configure(scn, hif_ext_group);
891  		}
892  		if (status != 0) {
893  			hif_err("Failed for group %d", i);
894  			hif_ext_group->irq_enabled = false;
895  		}
896  	}
897  
898  	scn->ext_grp_irq_configured = true;
899  
900  	return QDF_STATUS_SUCCESS;
901  }
902  
903  qdf_export_symbol(hif_configure_ext_group_interrupts);
904  
hif_deconfigure_ext_group_interrupts(struct hif_opaque_softc * hif_ctx)905  void hif_deconfigure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx)
906  {
907  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
908  
909  	if (!scn || !scn->ext_grp_irq_configured) {
910  		hif_err("scn(%pk) is NULL or grp irq not configured", scn);
911  		return;
912  	}
913  
914  	hif_grp_irq_deconfigure(scn);
915  	scn->ext_grp_irq_configured = false;
916  }
917  
918  qdf_export_symbol(hif_deconfigure_ext_group_interrupts);
919  
920  #ifdef WLAN_SUSPEND_RESUME_TEST
921  /**
922   * hif_check_and_trigger_ut_resume() - check if unit-test command was used to
923   *				       to trigger fake-suspend command, if yes
924   *				       then issue resume procedure.
925   * @scn: opaque HIF software context
926   *
927   * This API checks if unit-test command was used to trigger fake-suspend command
928   * and if answer is yes then it would trigger resume procedure.
929   *
930   * Make this API inline to save API-switch overhead and do branch-prediction to
931   * optimize performance impact.
932   *
933   * Return: void
934   */
hif_check_and_trigger_ut_resume(struct hif_softc * scn)935  static inline void hif_check_and_trigger_ut_resume(struct hif_softc *scn)
936  {
937  	if (qdf_unlikely(hif_irq_trigger_ut_resume(scn)))
938  		hif_ut_fw_resume(scn);
939  }
940  #else
hif_check_and_trigger_ut_resume(struct hif_softc * scn)941  static inline void hif_check_and_trigger_ut_resume(struct hif_softc *scn)
942  {
943  }
944  #endif
945  
946  /**
947   * hif_check_and_trigger_sys_resume() - Check for bus suspend and
948   *  trigger system resume
949   * @scn: hif context
950   * @irq: irq number
951   *
952   * Return: None
953   */
954  static inline void
hif_check_and_trigger_sys_resume(struct hif_softc * scn,int irq)955  hif_check_and_trigger_sys_resume(struct hif_softc *scn, int irq)
956  {
957  	if (scn->bus_suspended && scn->linkstate_vote) {
958  		hif_info_rl("interrupt rcvd:%d trigger sys resume", irq);
959  		qdf_pm_system_wakeup();
960  	}
961  }
962  
963  /**
964   * hif_ext_group_interrupt_handler() - handler for related interrupts
965   * @irq: irq number of the interrupt
966   * @context: the associated hif_exec_group context
967   *
968   * This callback function takes care of disabling the associated interrupts
969   * and scheduling the expected bottom half for the exec_context.
970   * This callback function also helps keep track of the count running contexts.
971   */
hif_ext_group_interrupt_handler(int irq,void * context)972  irqreturn_t hif_ext_group_interrupt_handler(int irq, void *context)
973  {
974  	struct hif_exec_context *hif_ext_group = context;
975  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
976  
977  	if (hif_ext_group->irq_requested) {
978  		hif_latency_profile_start(hif_ext_group);
979  
980  		hif_record_event(hif_ext_group->hif, hif_ext_group->grp_id,
981  				 0, 0, 0, HIF_EVENT_IRQ_TRIGGER);
982  
983  		hif_ext_group->irq_disable(hif_ext_group);
984  
985  		if (pld_is_one_msi(scn->qdf_dev->dev))
986  			hif_ext_group->irq_disabled_start_time =
987  							qdf_time_sched_clock();
988  		/*
989  		 * if private ioctl has issued fake suspend command to put
990  		 * FW in D0-WOW state then here is our chance to bring FW out
991  		 * of WOW mode.
992  		 *
993  		 * The reason why you need to explicitly wake-up the FW is here:
994  		 * APSS should have been in fully awake through-out when
995  		 * fake APSS suspend command was issued (to put FW in WOW mode)
996  		 * hence organic way of waking-up the FW
997  		 * (as part-of APSS-host wake-up) won't happen because
998  		 * in reality APSS didn't really suspend.
999  		 */
1000  		hif_check_and_trigger_ut_resume(scn);
1001  
1002  		hif_check_and_trigger_sys_resume(scn, irq);
1003  
1004  		qdf_atomic_inc(&scn->active_grp_tasklet_cnt);
1005  
1006  		hif_ext_group->sched_ops->schedule(hif_ext_group);
1007  	}
1008  
1009  	return IRQ_HANDLED;
1010  }
1011  
1012  /**
1013   * hif_exec_kill() - grp tasklet kill
1014   * @hif_ctx: hif_softc
1015   *
1016   * return: void
1017   */
hif_exec_kill(struct hif_opaque_softc * hif_ctx)1018  void hif_exec_kill(struct hif_opaque_softc *hif_ctx)
1019  {
1020  	int i;
1021  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
1022  
1023  	for (i = 0; i < hif_state->hif_num_extgroup; i++)
1024  		hif_state->hif_ext_group[i]->sched_ops->kill(
1025  			hif_state->hif_ext_group[i]);
1026  
1027  	qdf_atomic_set(&hif_state->ol_sc.active_grp_tasklet_cnt, 0);
1028  }
1029  
1030  #ifdef FEATURE_IRQ_AFFINITY
1031  static inline void
hif_init_force_napi_complete(struct hif_exec_context * hif_ext_group)1032  hif_init_force_napi_complete(struct hif_exec_context *hif_ext_group)
1033  {
1034  	qdf_atomic_init(&hif_ext_group->force_napi_complete);
1035  }
1036  #else
1037  static inline void
hif_init_force_napi_complete(struct hif_exec_context * hif_ext_group)1038  hif_init_force_napi_complete(struct hif_exec_context *hif_ext_group)
1039  {
1040  }
1041  #endif
1042  
1043  /**
1044   * hif_register_ext_group() - API to register external group
1045   * interrupt handler.
1046   * @hif_ctx : HIF Context
1047   * @numirq: number of irq's in the group
1048   * @irq: array of irq values
1049   * @handler: callback interrupt handler function
1050   * @cb_ctx: context to passed in callback
1051   * @context_name: context name
1052   * @type: napi vs tasklet
1053   * @scale:
1054   *
1055   * Return: QDF_STATUS
1056   */
hif_register_ext_group(struct hif_opaque_softc * hif_ctx,uint32_t numirq,uint32_t irq[],ext_intr_handler handler,void * cb_ctx,const char * context_name,enum hif_exec_type type,uint32_t scale)1057  QDF_STATUS hif_register_ext_group(struct hif_opaque_softc *hif_ctx,
1058  				  uint32_t numirq, uint32_t irq[],
1059  				  ext_intr_handler handler,
1060  				  void *cb_ctx, const char *context_name,
1061  				  enum hif_exec_type type, uint32_t scale)
1062  {
1063  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
1064  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
1065  	struct hif_exec_context *hif_ext_group;
1066  
1067  	if (scn->ext_grp_irq_configured) {
1068  		hif_err("Called after ext grp irq configured");
1069  		return QDF_STATUS_E_FAILURE;
1070  	}
1071  
1072  	if (hif_state->hif_num_extgroup >= HIF_MAX_GROUP) {
1073  		hif_err("Max groups: %d reached", hif_state->hif_num_extgroup);
1074  		return QDF_STATUS_E_FAILURE;
1075  	}
1076  
1077  	if (numirq >= HIF_MAX_GRP_IRQ) {
1078  		hif_err("Invalid numirq: %d", numirq);
1079  		return QDF_STATUS_E_FAILURE;
1080  	}
1081  
1082  	hif_ext_group = hif_exec_create(type, scale);
1083  	if (!hif_ext_group)
1084  		return QDF_STATUS_E_FAILURE;
1085  
1086  	hif_state->hif_ext_group[hif_state->hif_num_extgroup] =
1087  		hif_ext_group;
1088  
1089  	hif_ext_group->numirq = numirq;
1090  	qdf_mem_copy(&hif_ext_group->irq[0], irq, numirq * sizeof(irq[0]));
1091  	hif_ext_group->context = cb_ctx;
1092  	hif_ext_group->handler = handler;
1093  	hif_ext_group->configured = true;
1094  	hif_ext_group->grp_id = hif_state->hif_num_extgroup;
1095  	hif_ext_group->hif = hif_ctx;
1096  	hif_ext_group->context_name = context_name;
1097  	hif_ext_group->type = type;
1098  	hif_init_force_napi_complete(hif_ext_group);
1099  
1100  	hif_state->hif_num_extgroup++;
1101  	return QDF_STATUS_SUCCESS;
1102  }
1103  qdf_export_symbol(hif_register_ext_group);
1104  
1105  /**
1106   * hif_exec_create() - create an execution context
1107   * @type: the type of execution context to create
1108   * @scale:
1109   */
hif_exec_create(enum hif_exec_type type,uint32_t scale)1110  struct hif_exec_context *hif_exec_create(enum hif_exec_type type,
1111  						uint32_t scale)
1112  {
1113  	hif_debug("%s: create exec_type %d budget %d",
1114  		  __func__, type, QCA_NAPI_BUDGET * scale);
1115  
1116  	switch (type) {
1117  	case HIF_EXEC_NAPI_TYPE:
1118  		return hif_exec_napi_create(scale);
1119  
1120  	case HIF_EXEC_TASKLET_TYPE:
1121  		return hif_exec_tasklet_create();
1122  	default:
1123  		return NULL;
1124  	}
1125  }
1126  
1127  /**
1128   * hif_exec_destroy() - free the hif_exec context
1129   * @ctx: context to free
1130   *
1131   * please kill the context before freeing it to avoid a use after free.
1132   */
hif_exec_destroy(struct hif_exec_context * ctx)1133  void hif_exec_destroy(struct hif_exec_context *ctx)
1134  {
1135  	struct hif_softc *scn = HIF_GET_SOFTC(ctx->hif);
1136  
1137  	if (scn->ext_grp_irq_configured)
1138  		qdf_spinlock_destroy(&ctx->irq_lock);
1139  	qdf_mem_free(ctx);
1140  }
1141  
1142  /**
1143   * hif_deregister_exec_group() - API to free the exec contexts
1144   * @hif_ctx: HIF context
1145   * @context_name: name of the module whose contexts need to be deregistered
1146   *
1147   * This function deregisters the contexts of the requestor identified
1148   * based on the context_name & frees the memory.
1149   *
1150   * Return: void
1151   */
hif_deregister_exec_group(struct hif_opaque_softc * hif_ctx,const char * context_name)1152  void hif_deregister_exec_group(struct hif_opaque_softc *hif_ctx,
1153  				const char *context_name)
1154  {
1155  	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
1156  	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
1157  	struct hif_exec_context *hif_ext_group;
1158  	int i;
1159  
1160  	for (i = 0; i < HIF_MAX_GROUP; i++) {
1161  		hif_ext_group = hif_state->hif_ext_group[i];
1162  
1163  		if (!hif_ext_group)
1164  			continue;
1165  
1166  		hif_debug("%s: Deregistering grp id %d name %s",
1167  			  __func__,
1168  			  hif_ext_group->grp_id,
1169  			  hif_ext_group->context_name);
1170  
1171  		if (strcmp(hif_ext_group->context_name, context_name) == 0) {
1172  			hif_ext_group->sched_ops->kill(hif_ext_group);
1173  			hif_state->hif_ext_group[i] = NULL;
1174  			hif_exec_destroy(hif_ext_group);
1175  			hif_state->hif_num_extgroup--;
1176  		}
1177  
1178  	}
1179  }
1180  qdf_export_symbol(hif_deregister_exec_group);
1181  
1182  #ifdef DP_UMAC_HW_RESET_SUPPORT
1183  /**
1184   * hif_umac_reset_handler_tasklet() - Tasklet for UMAC HW reset interrupt
1185   * @data: UMAC HW reset HIF context
1186   *
1187   * return: void
1188   */
hif_umac_reset_handler_tasklet(unsigned long data)1189  static void hif_umac_reset_handler_tasklet(unsigned long data)
1190  {
1191  	struct hif_umac_reset_ctx *umac_reset_ctx =
1192  		(struct hif_umac_reset_ctx *)data;
1193  
1194  	/* call the callback handler */
1195  	umac_reset_ctx->cb_handler(umac_reset_ctx->cb_ctx);
1196  }
1197  
1198  /**
1199   * hif_umac_reset_irq_handler() - Interrupt service routine of UMAC HW reset
1200   * @irq: irq coming from kernel
1201   * @ctx: UMAC HW reset HIF context
1202   *
1203   * return: IRQ_HANDLED if success, else IRQ_NONE
1204   */
hif_umac_reset_irq_handler(int irq,void * ctx)1205  static irqreturn_t hif_umac_reset_irq_handler(int irq, void *ctx)
1206  {
1207  	struct hif_umac_reset_ctx *umac_reset_ctx = ctx;
1208  
1209  	/* Schedule the tasklet if it is umac reset interrupt and exit */
1210  	if (umac_reset_ctx->irq_handler(umac_reset_ctx->cb_ctx))
1211  		tasklet_hi_schedule(&umac_reset_ctx->intr_tq);
1212  
1213  	return IRQ_HANDLED;
1214  }
1215  
hif_get_umac_reset_irq(struct hif_opaque_softc * hif_scn,int * umac_reset_irq)1216  QDF_STATUS hif_get_umac_reset_irq(struct hif_opaque_softc *hif_scn,
1217  				  int *umac_reset_irq)
1218  {
1219  	int ret;
1220  	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_scn);
1221  	struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(hif_sc);
1222  	struct platform_device *pdev = (struct platform_device *)sc->pdev;
1223  
1224  	ret = pfrm_get_irq(&pdev->dev, (struct qdf_pfm_hndl *)pdev,
1225  			   "umac_reset", 0, umac_reset_irq);
1226  
1227  	if (ret) {
1228  		hif_err("umac reset get irq failed ret %d", ret);
1229  		return QDF_STATUS_E_FAILURE;
1230  	}
1231  	return QDF_STATUS_SUCCESS;
1232  }
1233  
1234  qdf_export_symbol(hif_get_umac_reset_irq);
1235  
hif_register_umac_reset_handler(struct hif_opaque_softc * hif_scn,bool (* irq_handler)(void * cb_ctx),int (* tl_handler)(void * cb_ctx),void * cb_ctx,int irq)1236  QDF_STATUS hif_register_umac_reset_handler(struct hif_opaque_softc *hif_scn,
1237  					   bool (*irq_handler)(void *cb_ctx),
1238  					   int (*tl_handler)(void *cb_ctx),
1239  					   void *cb_ctx, int irq)
1240  {
1241  	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_scn);
1242  	struct hif_umac_reset_ctx *umac_reset_ctx;
1243  	int ret;
1244  
1245  	if (!hif_sc) {
1246  		hif_err("scn is null");
1247  		return QDF_STATUS_E_NULL_VALUE;
1248  	}
1249  
1250  	umac_reset_ctx = &hif_sc->umac_reset_ctx;
1251  
1252  	umac_reset_ctx->irq_handler = irq_handler;
1253  	umac_reset_ctx->cb_handler = tl_handler;
1254  	umac_reset_ctx->cb_ctx = cb_ctx;
1255  	umac_reset_ctx->os_irq = irq;
1256  
1257  	/* Init the tasklet */
1258  	tasklet_init(&umac_reset_ctx->intr_tq,
1259  		     hif_umac_reset_handler_tasklet,
1260  		     (unsigned long)umac_reset_ctx);
1261  
1262  	/* Register the interrupt handler */
1263  	ret  = pfrm_request_irq(hif_sc->qdf_dev->dev, irq,
1264  				hif_umac_reset_irq_handler,
1265  				IRQF_NO_SUSPEND,
1266  				"umac_hw_reset_irq",
1267  				umac_reset_ctx);
1268  	if (ret) {
1269  		hif_err("request_irq failed: %d", ret);
1270  		return qdf_status_from_os_return(ret);
1271  	}
1272  
1273  	umac_reset_ctx->irq_configured = true;
1274  
1275  	return QDF_STATUS_SUCCESS;
1276  }
1277  
1278  qdf_export_symbol(hif_register_umac_reset_handler);
1279  
hif_unregister_umac_reset_handler(struct hif_opaque_softc * hif_scn)1280  QDF_STATUS hif_unregister_umac_reset_handler(struct hif_opaque_softc *hif_scn)
1281  {
1282  	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_scn);
1283  	struct hif_umac_reset_ctx *umac_reset_ctx;
1284  	int ret;
1285  
1286  	if (!hif_sc) {
1287  		hif_err("scn is null");
1288  		return QDF_STATUS_E_NULL_VALUE;
1289  	}
1290  
1291  	umac_reset_ctx = &hif_sc->umac_reset_ctx;
1292  	if (!umac_reset_ctx->irq_configured) {
1293  		hif_err("unregister called without a prior IRQ configuration");
1294  		return QDF_STATUS_E_FAILURE;
1295  	}
1296  
1297  	ret  = pfrm_free_irq(hif_sc->qdf_dev->dev,
1298  			     umac_reset_ctx->os_irq,
1299  			     umac_reset_ctx);
1300  	if (ret) {
1301  		hif_err("free_irq failed: %d", ret);
1302  		return qdf_status_from_os_return(ret);
1303  	}
1304  	umac_reset_ctx->irq_configured = false;
1305  
1306  	tasklet_disable(&umac_reset_ctx->intr_tq);
1307  	tasklet_kill(&umac_reset_ctx->intr_tq);
1308  
1309  	umac_reset_ctx->cb_handler = NULL;
1310  	umac_reset_ctx->cb_ctx = NULL;
1311  
1312  	return QDF_STATUS_SUCCESS;
1313  }
1314  
1315  qdf_export_symbol(hif_unregister_umac_reset_handler);
1316  #endif
1317