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