Commit | Line | Data |
---|---|---|
2f89a5d7 GBA |
1 | /****************************************************************************** |
2 | * | |
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | * redistributing this file, you may do so under either license. | |
5 | * | |
6 | * GPL LICENSE SUMMARY | |
7 | * | |
8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. | |
9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | |
7174beb6 | 10 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
ea695b7c | 11 | * Copyright(c) 2018 - 2019 Intel Corporation |
2f89a5d7 GBA |
12 | * |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of version 2 of the GNU General Public License as | |
15 | * published by the Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | * General Public License for more details. | |
21 | * | |
2f89a5d7 GBA |
22 | * The full GNU General Public License is included in this distribution |
23 | * in the file called COPYING. | |
24 | * | |
25 | * Contact Information: | |
d01c5366 | 26 | * Intel Linux Wireless <linuxwifi@intel.com> |
2f89a5d7 GBA |
27 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
28 | * | |
29 | * BSD LICENSE | |
30 | * | |
31 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. | |
32 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | |
7174beb6 | 33 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
ea695b7c | 34 | * Copyright(c) 2018 - 2019 Intel Corporation |
2f89a5d7 GBA |
35 | * All rights reserved. |
36 | * | |
37 | * Redistribution and use in source and binary forms, with or without | |
38 | * modification, are permitted provided that the following conditions | |
39 | * are met: | |
40 | * | |
41 | * * Redistributions of source code must retain the above copyright | |
42 | * notice, this list of conditions and the following disclaimer. | |
43 | * * Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in | |
45 | * the documentation and/or other materials provided with the | |
46 | * distribution. | |
47 | * * Neither the name Intel Corporation nor the names of its | |
48 | * contributors may be used to endorse or promote products derived | |
49 | * from this software without specific prior written permission. | |
50 | * | |
51 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
52 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
53 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
54 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
55 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
56 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
57 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
58 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
59 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
60 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
61 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
62 | * | |
63 | *****************************************************************************/ | |
64 | ||
7174beb6 JB |
65 | #ifndef __iwl_fw_dbg_h__ |
66 | #define __iwl_fw_dbg_h__ | |
67 | #include <linux/workqueue.h> | |
68 | #include <net/cfg80211.h> | |
69 | #include "runtime.h" | |
1efc3843 GBA |
70 | #include "iwl-prph.h" |
71 | #include "iwl-io.h" | |
7174beb6 JB |
72 | #include "file.h" |
73 | #include "error-dump.h" | |
5cfe79c8 | 74 | #include "api/commands.h" |
d47902f9 | 75 | #include "api/dbg-tlv.h" |
0a3a3e9e | 76 | #include "api/alive.h" |
7174beb6 JB |
77 | |
78 | /** | |
79 | * struct iwl_fw_dump_desc - describes the dump | |
80 | * @len: length of trig_desc->data | |
81 | * @trig_desc: the description of the dump | |
82 | */ | |
83 | struct iwl_fw_dump_desc { | |
84 | size_t len; | |
85 | /* must be last */ | |
86 | struct iwl_fw_error_dump_trigger_desc trig_desc; | |
87 | }; | |
88 | ||
d25eec30 SS |
89 | /** |
90 | * struct iwl_fw_dbg_params - register values to restore | |
91 | * @in_sample: DBGC_IN_SAMPLE value | |
92 | * @out_ctrl: DBGC_OUT_CTRL value | |
93 | */ | |
94 | struct iwl_fw_dbg_params { | |
95 | u32 in_sample; | |
96 | u32 out_ctrl; | |
97 | }; | |
98 | ||
7174beb6 JB |
99 | extern const struct iwl_fw_dump_desc iwl_dump_desc_assert; |
100 | ||
101 | static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt) | |
102 | { | |
103 | if (fwrt->dump.desc != &iwl_dump_desc_assert) | |
104 | kfree(fwrt->dump.desc); | |
105 | fwrt->dump.desc = NULL; | |
d3561e0e SM |
106 | fwrt->dump.lmac_err_id[0] = 0; |
107 | if (fwrt->smem_cfg.num_lmacs > 1) | |
108 | fwrt->dump.lmac_err_id[1] = 0; | |
109 | fwrt->dump.umac_err_id = 0; | |
7174beb6 JB |
110 | } |
111 | ||
7174beb6 JB |
112 | int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, |
113 | const struct iwl_fw_dump_desc *desc, | |
124e0eba | 114 | bool monitor_only, unsigned int delay); |
700b3799 SM |
115 | int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, |
116 | enum iwl_fw_dbg_trigger trig_type); | |
3b589d56 SM |
117 | int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, |
118 | struct iwl_fwrt_dump_data *dump_data); | |
7174beb6 | 119 | int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, |
4b49e34e SM |
120 | enum iwl_fw_dbg_trigger trig, const char *str, |
121 | size_t len, struct iwl_fw_dbg_trigger_tlv *trigger); | |
7174beb6 JB |
122 | int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, |
123 | struct iwl_fw_dbg_trigger_tlv *trigger, | |
124 | const char *fmt, ...) __printf(3, 4); | |
125 | int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 id); | |
2f89a5d7 GBA |
126 | |
127 | #define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ | |
17b809c9 | 128 | void *__dbg_trigger = (fw)->dbg.trigger_tlv[(id)]; \ |
2f89a5d7 GBA |
129 | unlikely(__dbg_trigger); \ |
130 | }) | |
131 | ||
132 | static inline struct iwl_fw_dbg_trigger_tlv* | |
133 | _iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) | |
134 | { | |
17b809c9 | 135 | return fw->dbg.trigger_tlv[id]; |
2f89a5d7 GBA |
136 | } |
137 | ||
138 | #define iwl_fw_dbg_get_trigger(fw, id) ({ \ | |
139 | BUILD_BUG_ON(!__builtin_constant_p(id)); \ | |
140 | BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ | |
141 | _iwl_fw_dbg_get_trigger((fw), (id)); \ | |
142 | }) | |
143 | ||
144 | static inline bool | |
145 | iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, | |
7174beb6 | 146 | struct wireless_dev *wdev) |
2f89a5d7 GBA |
147 | { |
148 | u32 trig_vif = le32_to_cpu(trig->vif_type); | |
149 | ||
36064795 | 150 | return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || |
7174beb6 | 151 | wdev->iftype == trig_vif; |
2f89a5d7 GBA |
152 | } |
153 | ||
154 | static inline bool | |
7174beb6 | 155 | iwl_fw_dbg_trigger_stop_conf_match(struct iwl_fw_runtime *fwrt, |
2f89a5d7 GBA |
156 | struct iwl_fw_dbg_trigger_tlv *trig) |
157 | { | |
158 | return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && | |
7174beb6 JB |
159 | (fwrt->dump.conf == FW_DBG_INVALID || |
160 | (BIT(fwrt->dump.conf) & le32_to_cpu(trig->stop_conf_ids)))); | |
2f89a5d7 GBA |
161 | } |
162 | ||
a977a150 | 163 | static inline bool |
46c7c982 | 164 | iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, u32 id, u32 dis_usec) |
a977a150 | 165 | { |
46c7c982 | 166 | unsigned long wind_jiff = usecs_to_jiffies(dis_usec); |
a977a150 GBA |
167 | |
168 | /* If this is the first event checked, jump to update start ts */ | |
7174beb6 JB |
169 | if (fwrt->dump.non_collect_ts_start[id] && |
170 | (time_after(fwrt->dump.non_collect_ts_start[id] + wind_jiff, | |
a977a150 GBA |
171 | jiffies))) |
172 | return true; | |
173 | ||
7174beb6 | 174 | fwrt->dump.non_collect_ts_start[id] = jiffies; |
a977a150 GBA |
175 | return false; |
176 | } | |
177 | ||
2f89a5d7 | 178 | static inline bool |
7174beb6 JB |
179 | iwl_fw_dbg_trigger_check_stop(struct iwl_fw_runtime *fwrt, |
180 | struct wireless_dev *wdev, | |
2f89a5d7 GBA |
181 | struct iwl_fw_dbg_trigger_tlv *trig) |
182 | { | |
46c7c982 SM |
183 | u32 usec = le16_to_cpu(trig->trig_dis_ms) * USEC_PER_MSEC; |
184 | ||
7174beb6 | 185 | if (wdev && !iwl_fw_dbg_trigger_vif_match(trig, wdev)) |
2f89a5d7 GBA |
186 | return false; |
187 | ||
46c7c982 | 188 | if (iwl_fw_dbg_no_trig_window(fwrt, le32_to_cpu(trig->id), usec)) { |
7174beb6 | 189 | IWL_WARN(fwrt, "Trigger %d occurred while no-collect window.\n", |
a977a150 GBA |
190 | trig->id); |
191 | return false; | |
192 | } | |
193 | ||
7174beb6 | 194 | return iwl_fw_dbg_trigger_stop_conf_match(fwrt, trig); |
2f89a5d7 GBA |
195 | } |
196 | ||
6c042d75 SS |
197 | static inline struct iwl_fw_dbg_trigger_tlv* |
198 | _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt, | |
199 | struct wireless_dev *wdev, | |
200 | const enum iwl_fw_dbg_trigger id) | |
201 | { | |
202 | struct iwl_fw_dbg_trigger_tlv *trig; | |
203 | ||
a1af4c48 | 204 | if (iwl_trans_dbg_ini_valid(fwrt->trans)) |
74374223 SS |
205 | return NULL; |
206 | ||
6c042d75 SS |
207 | if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id)) |
208 | return NULL; | |
209 | ||
210 | trig = _iwl_fw_dbg_get_trigger(fwrt->fw, id); | |
211 | ||
212 | if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trig)) | |
213 | return NULL; | |
214 | ||
215 | return trig; | |
216 | } | |
217 | ||
218 | #define iwl_fw_dbg_trigger_on(fwrt, wdev, id) ({ \ | |
219 | BUILD_BUG_ON(!__builtin_constant_p(id)); \ | |
220 | BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ | |
221 | _iwl_fw_dbg_trigger_on((fwrt), (wdev), (id)); \ | |
222 | }) | |
223 | ||
2f89a5d7 | 224 | static inline void |
7174beb6 JB |
225 | _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt, |
226 | struct wireless_dev *wdev, | |
2f89a5d7 GBA |
227 | struct iwl_fw_dbg_trigger_tlv *trigger) |
228 | { | |
229 | if (!trigger) | |
230 | return; | |
231 | ||
7174beb6 | 232 | if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trigger)) |
2f89a5d7 GBA |
233 | return; |
234 | ||
7174beb6 | 235 | iwl_fw_dbg_collect_trig(fwrt, trigger, NULL); |
2f89a5d7 GBA |
236 | } |
237 | ||
7174beb6 JB |
238 | #define iwl_fw_dbg_trigger_simple_stop(fwrt, wdev, trig) \ |
239 | _iwl_fw_dbg_trigger_simple_stop((fwrt), (wdev), \ | |
240 | iwl_fw_dbg_get_trigger((fwrt)->fw,\ | |
2f89a5d7 | 241 | (trig))) |
57605833 SM |
242 | int iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt, |
243 | struct iwl_fw_dbg_params *params, | |
244 | bool stop); | |
1efc3843 | 245 | |
f7805b33 LC |
246 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
247 | static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt) | |
248 | { | |
029c25f3 SM |
249 | if (fwrt->cur_fw_img == IWL_UCODE_REGULAR && |
250 | (fwrt->fw->dbg.dest_tlv || | |
251 | fwrt->trans->dbg.ini_dest != IWL_FW_INI_LOCATION_INVALID)) | |
91c28b83 | 252 | fwrt->trans->dbg.rec_on = true; |
f7805b33 LC |
253 | } |
254 | #endif | |
255 | ||
7174beb6 JB |
256 | static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) |
257 | { | |
258 | fwrt->dump.conf = FW_DBG_INVALID; | |
259 | } | |
260 | ||
261 | void iwl_fw_error_dump_wk(struct work_struct *work); | |
262 | ||
e488e593 SS |
263 | static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type) |
264 | { | |
1d45a700 | 265 | return (fwrt->fw->dbg.dump_mask & BIT(type)); |
e488e593 SS |
266 | } |
267 | ||
2d8c2615 SM |
268 | static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) |
269 | { | |
270 | return fw_has_capa(&fwrt->fw->ucode_capa, | |
271 | IWL_UCODE_TLV_CAPA_D3_DEBUG) && | |
971377e6 SM |
272 | fwrt->trans->cfg->d3_debug_data_length && fwrt->ops && |
273 | fwrt->ops->d3_debug_enable && | |
274 | fwrt->ops->d3_debug_enable(fwrt->ops_ctx) && | |
e488e593 | 275 | iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); |
2d8c2615 SM |
276 | } |
277 | ||
56b657f7 SS |
278 | static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt) |
279 | { | |
280 | return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) && | |
286ca8eb | 281 | !fwrt->trans->trans_cfg->gen2 && |
9adcc117 | 282 | fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX && |
56b657f7 SS |
283 | fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size && |
284 | fwrt->fw_paging_db[0].fw_paging_block; | |
285 | } | |
286 | ||
2d8c2615 SM |
287 | void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt); |
288 | ||
c7ab138e | 289 | static inline void iwl_fw_flush_dumps(struct iwl_fw_runtime *fwrt) |
7174beb6 | 290 | { |
c7ab138e SM |
291 | int i; |
292 | ||
9b1bcfcc | 293 | iwl_dbg_tlv_del_timers(fwrt->trans); |
3b589d56 | 294 | for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) |
c7ab138e | 295 | flush_delayed_work(&fwrt->dump.wks[i].wk); |
7174beb6 JB |
296 | } |
297 | ||
422b5dd4 HD |
298 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
299 | static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) | |
300 | { | |
301 | fwrt->timestamp.delay = 0; | |
302 | cancel_delayed_work_sync(&fwrt->timestamp.wk); | |
303 | } | |
304 | ||
305 | void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay); | |
306 | ||
307 | static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) | |
308 | { | |
309 | cancel_delayed_work_sync(&fwrt->timestamp.wk); | |
310 | } | |
311 | ||
312 | static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) | |
313 | { | |
314 | if (!fwrt->timestamp.delay) | |
315 | return; | |
316 | ||
317 | schedule_delayed_work(&fwrt->timestamp.wk, | |
318 | round_jiffies_relative(fwrt->timestamp.delay)); | |
319 | } | |
320 | ||
321 | #else | |
322 | ||
323 | static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {} | |
324 | ||
325 | static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, | |
326 | u32 delay) {} | |
327 | ||
328 | static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {} | |
329 | ||
330 | static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} | |
331 | ||
332 | #endif /* CONFIG_IWLWIFI_DEBUGFS */ | |
333 | ||
203c83d3 | 334 | void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt); |
8d534e96 | 335 | |
22463857 SM |
336 | static inline void iwl_fw_lmac1_set_alive_err_table(struct iwl_trans *trans, |
337 | u32 lmac_error_event_table) | |
338 | { | |
91c28b83 | 339 | if (!(trans->dbg.error_event_table_tlv_status & |
22463857 | 340 | IWL_ERROR_EVENT_TABLE_LMAC1) || |
91c28b83 | 341 | WARN_ON(trans->dbg.lmac_error_event_table[0] != |
22463857 | 342 | lmac_error_event_table)) |
91c28b83 | 343 | trans->dbg.lmac_error_event_table[0] = lmac_error_event_table; |
22463857 SM |
344 | } |
345 | ||
346 | static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans, | |
347 | u32 umac_error_event_table) | |
348 | { | |
91c28b83 | 349 | if (!(trans->dbg.error_event_table_tlv_status & |
22463857 | 350 | IWL_ERROR_EVENT_TABLE_UMAC) || |
91c28b83 | 351 | WARN_ON(trans->dbg.umac_error_event_table != |
22463857 | 352 | umac_error_event_table)) |
91c28b83 | 353 | trans->dbg.umac_error_event_table = umac_error_event_table; |
22463857 SM |
354 | } |
355 | ||
4b1831e4 SM |
356 | static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt) |
357 | { | |
3b589d56 SM |
358 | enum iwl_fw_ini_time_point tp_id; |
359 | ||
360 | if (!iwl_trans_dbg_ini_valid(fwrt->trans)) { | |
361 | iwl_fw_dbg_collect_desc(fwrt, &iwl_dump_desc_assert, false, 0); | |
362 | return; | |
363 | } | |
364 | ||
365 | if (fwrt->trans->dbg.hw_error) { | |
366 | tp_id = IWL_FW_INI_TIME_POINT_FW_HW_ERROR; | |
91c28b83 | 367 | fwrt->trans->dbg.hw_error = false; |
4b1831e4 | 368 | } else { |
3b589d56 | 369 | tp_id = IWL_FW_INI_TIME_POINT_FW_ASSERT; |
4b1831e4 | 370 | } |
3b589d56 SM |
371 | |
372 | iwl_dbg_tlv_time_point(fwrt, tp_id, NULL); | |
4b1831e4 SM |
373 | } |
374 | ||
cc5470df | 375 | void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt); |
0a3a3e9e SM |
376 | |
377 | static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, | |
378 | struct iwl_lmac_alive *lmac, | |
379 | struct iwl_umac_alive *umac) | |
380 | { | |
381 | if (lmac) { | |
382 | fwrt->dump.fw_ver.type = lmac->ver_type; | |
383 | fwrt->dump.fw_ver.subtype = lmac->ver_subtype; | |
384 | fwrt->dump.fw_ver.lmac_major = le32_to_cpu(lmac->ucode_major); | |
385 | fwrt->dump.fw_ver.lmac_minor = le32_to_cpu(lmac->ucode_minor); | |
386 | } | |
387 | ||
388 | if (umac) { | |
389 | fwrt->dump.fw_ver.umac_major = le32_to_cpu(umac->umac_major); | |
390 | fwrt->dump.fw_ver.umac_minor = le32_to_cpu(umac->umac_minor); | |
391 | } | |
392 | } | |
7174beb6 | 393 | #endif /* __iwl_fw_dbg_h__ */ |