1 /* 2 * Copyright (c) 2018,2020-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 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 "htc_debug.h" 21 #include "htc_internal.h" 22 #include "htc_credit_history.h" 23 #include <qdf_lock.h> 24 #include <qdf_hang_event_notifier.h> 25 #include <qdf_notifier.h> 26 #include "qdf_ssr_driver_dump.h" 27 28 struct HTC_CREDIT_HISTORY { 29 enum htc_credit_exchange_type type; 30 uint64_t time; 31 uint32_t tx_credit; 32 uint32_t htc_tx_queue_depth; 33 }; 34 35 struct htc_hang_data_fixed_param { 36 uint16_t tlv_header; 37 struct HTC_CREDIT_HISTORY credit_hist; 38 } qdf_packed; 39 40 static qdf_spinlock_t g_htc_credit_lock; 41 static uint32_t g_htc_credit_history_idx; 42 static uint32_t g_htc_credit_history_length; 43 static 44 struct HTC_CREDIT_HISTORY htc_credit_history_buffer[HTC_CREDIT_HISTORY_MAX]; 45 46 #define NUM_HANG_CREDIT_HISTORY 1 47 48 #ifdef QCA_WIFI_EMULATION 49 #define HTC_EMULATION_DELAY_IN_MS 20 50 /** 51 * htc_add_emulation_delay() - Adds a delay in before proceeding, only for 52 * emulation 53 * 54 * Return: None 55 */ 56 static inline void htc_add_emulation_delay(void) 57 { 58 qdf_mdelay(HTC_EMULATION_DELAY_IN_MS); 59 } 60 #else 61 static inline void htc_add_emulation_delay(void) 62 { 63 } 64 #endif 65 66 void htc_credit_history_deinit(void) 67 { 68 qdf_ssr_driver_dump_unregister_region("htc_credit_history_length"); 69 qdf_ssr_driver_dump_unregister_region("htc_credit_history_idx"); 70 qdf_ssr_driver_dump_unregister_region("htc_credit"); 71 qdf_minidump_remove(&htc_credit_history_buffer, 72 sizeof(htc_credit_history_buffer), "htc_credit"); 73 } 74 void htc_credit_history_init(void) 75 { 76 qdf_spinlock_create(&g_htc_credit_lock); 77 g_htc_credit_history_idx = 0; 78 g_htc_credit_history_length = 0; 79 qdf_minidump_log(&htc_credit_history_buffer, 80 sizeof(htc_credit_history_buffer), "htc_credit"); 81 qdf_ssr_driver_dump_register_region("htc_credit", 82 htc_credit_history_buffer, 83 sizeof(htc_credit_history_buffer)); 84 qdf_ssr_driver_dump_register_region("htc_credit_history_idx", 85 &g_htc_credit_history_idx, 86 sizeof(g_htc_credit_history_idx)); 87 qdf_ssr_driver_dump_register_region("htc_credit_history_length", 88 &g_htc_credit_history_length, 89 sizeof(g_htc_credit_history_length)); 90 } 91 92 /** 93 * htc_credit_record() - records tx que state & credit transactions 94 * @type: type of echange can be HTC_REQUEST_CREDIT 95 * or HTC_PROCESS_CREDIT_REPORT 96 * @tx_credit: current number of tx_credits 97 * @htc_tx_queue_depth: current hct tx queue depth 98 * 99 * This function records the credits and pending commands whenever a command is 100 * sent or credits are returned. Call this after the credits have been updated 101 * according to the transaction. Call this before dequeing commands. 102 * 103 * Consider making this function accept an HTC_ENDPOINT and find the current 104 * credits and queue depth itself. 105 * 106 */ 107 void htc_credit_record(enum htc_credit_exchange_type type, uint32_t tx_credit, 108 uint32_t htc_tx_queue_depth) 109 { 110 qdf_spin_lock_bh(&g_htc_credit_lock); 111 if (g_htc_credit_history_idx >= HTC_CREDIT_HISTORY_MAX) 112 g_htc_credit_history_idx = 0; 113 114 htc_credit_history_buffer[g_htc_credit_history_idx].type = type; 115 htc_credit_history_buffer[g_htc_credit_history_idx].time = 116 qdf_get_log_timestamp(); 117 htc_credit_history_buffer[g_htc_credit_history_idx].tx_credit = 118 tx_credit; 119 htc_credit_history_buffer[g_htc_credit_history_idx].htc_tx_queue_depth = 120 htc_tx_queue_depth; 121 122 g_htc_credit_history_idx++; 123 g_htc_credit_history_length++; 124 htc_add_emulation_delay(); 125 qdf_spin_unlock_bh(&g_htc_credit_lock); 126 } 127 128 void htc_print_credit_history(HTC_HANDLE htc, uint32_t count, 129 qdf_abstract_print *print, void *print_priv) 130 { 131 uint32_t idx; 132 133 print(print_priv, "HTC Credit History (count %u)", count); 134 qdf_spin_lock_bh(&g_htc_credit_lock); 135 136 if (count > HTC_CREDIT_HISTORY_MAX) 137 count = HTC_CREDIT_HISTORY_MAX; 138 if (count > g_htc_credit_history_length) 139 count = g_htc_credit_history_length; 140 141 /* subtract count from index, and wrap if necessary */ 142 idx = HTC_CREDIT_HISTORY_MAX + g_htc_credit_history_idx - count; 143 idx %= HTC_CREDIT_HISTORY_MAX; 144 145 print(print_priv, 146 "Time (seconds) Type Credits Queue Depth"); 147 while (count) { 148 struct HTC_CREDIT_HISTORY *hist = 149 &htc_credit_history_buffer[idx]; 150 uint64_t secs, usecs; 151 152 qdf_log_timestamp_to_secs(hist->time, &secs, &usecs); 153 print(print_priv, "% 8lld.%06lld %-25s %-7.d %d", 154 secs, 155 usecs, 156 htc_credit_exchange_type_str(hist->type), 157 hist->tx_credit, 158 hist->htc_tx_queue_depth); 159 160 --count; 161 ++idx; 162 if (idx >= HTC_CREDIT_HISTORY_MAX) 163 idx = 0; 164 } 165 166 qdf_spin_unlock_bh(&g_htc_credit_lock); 167 } 168 169 #ifdef WLAN_HANG_EVENT 170 void htc_log_hang_credit_history(struct notifier_block *block, void *data) 171 { 172 qdf_notif_block *notif_block = qdf_container_of(block, qdf_notif_block, 173 notif_block); 174 struct qdf_notifer_data *htc_hang_data = data; 175 uint32_t count = NUM_HANG_CREDIT_HISTORY, idx, total_len; 176 HTC_HANDLE htc; 177 struct htc_hang_data_fixed_param *cmd; 178 uint8_t *htc_buf_ptr; 179 180 htc = notif_block->priv_data; 181 182 if (!htc) 183 return; 184 185 if (!htc_hang_data) 186 return; 187 188 total_len = sizeof(struct htc_hang_data_fixed_param); 189 qdf_spin_lock_bh(&g_htc_credit_lock); 190 191 if (count > HTC_CREDIT_HISTORY_MAX) 192 count = HTC_CREDIT_HISTORY_MAX; 193 if (count > g_htc_credit_history_length) 194 count = g_htc_credit_history_length; 195 196 idx = HTC_CREDIT_HISTORY_MAX + g_htc_credit_history_idx - count; 197 idx %= HTC_CREDIT_HISTORY_MAX; 198 199 qdf_spin_unlock_bh(&g_htc_credit_lock); 200 201 while (count) { 202 struct HTC_CREDIT_HISTORY *hist = 203 &htc_credit_history_buffer[idx]; 204 htc_buf_ptr = htc_hang_data->hang_data + htc_hang_data->offset; 205 cmd = (struct htc_hang_data_fixed_param *)htc_buf_ptr; 206 207 if (htc_hang_data->offset + total_len > QDF_WLAN_HANG_FW_OFFSET) 208 return; 209 210 QDF_HANG_EVT_SET_HDR(&cmd->tlv_header, 211 HANG_EVT_TAG_HTC_CREDIT_HIST, 212 QDF_HANG_GET_STRUCT_TLVLEN(struct htc_hang_data_fixed_param)); 213 qdf_mem_copy(&cmd->credit_hist, hist, sizeof(*hist)); 214 --count; 215 ++idx; 216 if (idx >= HTC_CREDIT_HISTORY_MAX) 217 idx = 0; 218 htc_hang_data->offset += total_len; 219 } 220 } 221 #endif 222