.. | .. |
---|
8 | 8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. |
---|
9 | 9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
---|
10 | 10 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
---|
11 | | - * Copyright(c) 2018 Intel Corporation |
---|
| 11 | + * Copyright(c) 2018 - 2019 Intel Corporation |
---|
12 | 12 | * |
---|
13 | 13 | * This program is free software; you can redistribute it and/or modify |
---|
14 | 14 | * it under the terms of version 2 of the GNU General Public License as |
---|
.. | .. |
---|
18 | 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
19 | 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
20 | 20 | * General Public License for more details. |
---|
21 | | - * |
---|
22 | | - * You should have received a copy of the GNU General Public License |
---|
23 | | - * along with this program; |
---|
24 | 21 | * |
---|
25 | 22 | * The full GNU General Public License is included in this distribution |
---|
26 | 23 | * in the file called COPYING. |
---|
.. | .. |
---|
34 | 31 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. |
---|
35 | 32 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
---|
36 | 33 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
---|
37 | | - * Copyright(c) 2018 Intel Corporation |
---|
| 34 | + * Copyright(c) 2018 - 2019 Intel Corporation |
---|
38 | 35 | * All rights reserved. |
---|
39 | 36 | * |
---|
40 | 37 | * Redistribution and use in source and binary forms, with or without |
---|
.. | .. |
---|
74 | 71 | #include "iwl-io.h" |
---|
75 | 72 | #include "file.h" |
---|
76 | 73 | #include "error-dump.h" |
---|
| 74 | +#include "api/commands.h" |
---|
| 75 | +#include "api/dbg-tlv.h" |
---|
| 76 | +#include "api/alive.h" |
---|
77 | 77 | |
---|
78 | 78 | /** |
---|
79 | 79 | * struct iwl_fw_dump_desc - describes the dump |
---|
.. | .. |
---|
86 | 86 | struct iwl_fw_error_dump_trigger_desc trig_desc; |
---|
87 | 87 | }; |
---|
88 | 88 | |
---|
| 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 | + |
---|
89 | 99 | extern const struct iwl_fw_dump_desc iwl_dump_desc_assert; |
---|
90 | 100 | |
---|
91 | | -static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt) |
---|
92 | | -{ |
---|
93 | | - if (fwrt->dump.desc != &iwl_dump_desc_assert) |
---|
94 | | - kfree(fwrt->dump.desc); |
---|
95 | | - fwrt->dump.desc = NULL; |
---|
96 | | - fwrt->dump.trig = NULL; |
---|
97 | | -} |
---|
98 | | - |
---|
99 | | -void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt); |
---|
100 | 101 | int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, |
---|
101 | 102 | const struct iwl_fw_dump_desc *desc, |
---|
102 | | - const struct iwl_fw_dbg_trigger_tlv *trigger); |
---|
| 103 | + bool monitor_only, unsigned int delay); |
---|
| 104 | +int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, |
---|
| 105 | + enum iwl_fw_dbg_trigger trig_type); |
---|
| 106 | +int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, |
---|
| 107 | + struct iwl_fwrt_dump_data *dump_data); |
---|
103 | 108 | int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, |
---|
104 | | - enum iwl_fw_dbg_trigger trig, |
---|
105 | | - const char *str, size_t len, |
---|
106 | | - const struct iwl_fw_dbg_trigger_tlv *trigger); |
---|
| 109 | + enum iwl_fw_dbg_trigger trig, const char *str, |
---|
| 110 | + size_t len, struct iwl_fw_dbg_trigger_tlv *trigger); |
---|
107 | 111 | int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, |
---|
108 | 112 | struct iwl_fw_dbg_trigger_tlv *trigger, |
---|
109 | 113 | const char *fmt, ...) __printf(3, 4); |
---|
110 | 114 | int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 id); |
---|
111 | 115 | |
---|
112 | 116 | #define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ |
---|
113 | | - void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ |
---|
| 117 | + void *__dbg_trigger = (fw)->dbg.trigger_tlv[(id)]; \ |
---|
114 | 118 | unlikely(__dbg_trigger); \ |
---|
115 | 119 | }) |
---|
116 | 120 | |
---|
117 | 121 | static inline struct iwl_fw_dbg_trigger_tlv* |
---|
118 | 122 | _iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) |
---|
119 | 123 | { |
---|
120 | | - return fw->dbg_trigger_tlv[id]; |
---|
| 124 | + return fw->dbg.trigger_tlv[id]; |
---|
121 | 125 | } |
---|
122 | 126 | |
---|
123 | 127 | #define iwl_fw_dbg_get_trigger(fw, id) ({ \ |
---|
.. | .. |
---|
146 | 150 | } |
---|
147 | 151 | |
---|
148 | 152 | static inline bool |
---|
149 | | -iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, |
---|
150 | | - struct iwl_fw_dbg_trigger_tlv *trig) |
---|
| 153 | +iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, u32 id, u32 dis_usec) |
---|
151 | 154 | { |
---|
152 | | - unsigned long wind_jiff = |
---|
153 | | - msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms)); |
---|
154 | | - u32 id = le32_to_cpu(trig->id); |
---|
| 155 | + unsigned long wind_jiff = usecs_to_jiffies(dis_usec); |
---|
155 | 156 | |
---|
156 | 157 | /* If this is the first event checked, jump to update start ts */ |
---|
157 | 158 | if (fwrt->dump.non_collect_ts_start[id] && |
---|
.. | .. |
---|
168 | 169 | struct wireless_dev *wdev, |
---|
169 | 170 | struct iwl_fw_dbg_trigger_tlv *trig) |
---|
170 | 171 | { |
---|
| 172 | + u32 usec = le16_to_cpu(trig->trig_dis_ms) * USEC_PER_MSEC; |
---|
| 173 | + |
---|
171 | 174 | if (wdev && !iwl_fw_dbg_trigger_vif_match(trig, wdev)) |
---|
172 | 175 | return false; |
---|
173 | 176 | |
---|
174 | | - if (iwl_fw_dbg_no_trig_window(fwrt, trig)) { |
---|
| 177 | + if (iwl_fw_dbg_no_trig_window(fwrt, le32_to_cpu(trig->id), usec)) { |
---|
175 | 178 | IWL_WARN(fwrt, "Trigger %d occurred while no-collect window.\n", |
---|
176 | 179 | trig->id); |
---|
177 | 180 | return false; |
---|
.. | .. |
---|
179 | 182 | |
---|
180 | 183 | return iwl_fw_dbg_trigger_stop_conf_match(fwrt, trig); |
---|
181 | 184 | } |
---|
| 185 | + |
---|
| 186 | +static inline struct iwl_fw_dbg_trigger_tlv* |
---|
| 187 | +_iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt, |
---|
| 188 | + struct wireless_dev *wdev, |
---|
| 189 | + const enum iwl_fw_dbg_trigger id) |
---|
| 190 | +{ |
---|
| 191 | + struct iwl_fw_dbg_trigger_tlv *trig; |
---|
| 192 | + |
---|
| 193 | + if (iwl_trans_dbg_ini_valid(fwrt->trans)) |
---|
| 194 | + return NULL; |
---|
| 195 | + |
---|
| 196 | + if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id)) |
---|
| 197 | + return NULL; |
---|
| 198 | + |
---|
| 199 | + trig = _iwl_fw_dbg_get_trigger(fwrt->fw, id); |
---|
| 200 | + |
---|
| 201 | + if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trig)) |
---|
| 202 | + return NULL; |
---|
| 203 | + |
---|
| 204 | + return trig; |
---|
| 205 | +} |
---|
| 206 | + |
---|
| 207 | +#define iwl_fw_dbg_trigger_on(fwrt, wdev, id) ({ \ |
---|
| 208 | + BUILD_BUG_ON(!__builtin_constant_p(id)); \ |
---|
| 209 | + BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ |
---|
| 210 | + _iwl_fw_dbg_trigger_on((fwrt), (wdev), (id)); \ |
---|
| 211 | +}) |
---|
182 | 212 | |
---|
183 | 213 | static inline void |
---|
184 | 214 | _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt, |
---|
.. | .. |
---|
198 | 228 | _iwl_fw_dbg_trigger_simple_stop((fwrt), (wdev), \ |
---|
199 | 229 | iwl_fw_dbg_get_trigger((fwrt)->fw,\ |
---|
200 | 230 | (trig))) |
---|
| 231 | +void iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt, |
---|
| 232 | + struct iwl_fw_dbg_params *params, |
---|
| 233 | + bool stop); |
---|
201 | 234 | |
---|
202 | | -static inline void iwl_fw_dbg_stop_recording(struct iwl_fw_runtime *fwrt) |
---|
| 235 | +#ifdef CONFIG_IWLWIFI_DEBUGFS |
---|
| 236 | +static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt) |
---|
203 | 237 | { |
---|
204 | | - if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { |
---|
205 | | - iwl_set_bits_prph(fwrt->trans, MON_BUFF_SAMPLE_CTL, 0x100); |
---|
206 | | - } else { |
---|
207 | | - iwl_write_prph(fwrt->trans, DBGC_IN_SAMPLE, 0); |
---|
208 | | - udelay(100); |
---|
209 | | - iwl_write_prph(fwrt->trans, DBGC_OUT_CTRL, 0); |
---|
210 | | - } |
---|
| 238 | + if (fwrt->cur_fw_img == IWL_UCODE_REGULAR && |
---|
| 239 | + (fwrt->fw->dbg.dest_tlv || |
---|
| 240 | + fwrt->trans->dbg.ini_dest != IWL_FW_INI_LOCATION_INVALID)) |
---|
| 241 | + fwrt->trans->dbg.rec_on = true; |
---|
211 | 242 | } |
---|
| 243 | +#endif |
---|
212 | 244 | |
---|
213 | 245 | static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) |
---|
214 | 246 | { |
---|
.. | .. |
---|
217 | 249 | |
---|
218 | 250 | void iwl_fw_error_dump_wk(struct work_struct *work); |
---|
219 | 251 | |
---|
220 | | -static inline void iwl_fw_flush_dump(struct iwl_fw_runtime *fwrt) |
---|
| 252 | +static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type) |
---|
221 | 253 | { |
---|
222 | | - flush_delayed_work(&fwrt->dump.wk); |
---|
| 254 | + return (fwrt->fw->dbg.dump_mask & BIT(type)); |
---|
223 | 255 | } |
---|
224 | 256 | |
---|
225 | | -static inline void iwl_fw_cancel_dump(struct iwl_fw_runtime *fwrt) |
---|
| 257 | +static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) |
---|
226 | 258 | { |
---|
227 | | - cancel_delayed_work_sync(&fwrt->dump.wk); |
---|
| 259 | + return fw_has_capa(&fwrt->fw->ucode_capa, |
---|
| 260 | + IWL_UCODE_TLV_CAPA_D3_DEBUG) && |
---|
| 261 | + fwrt->trans->cfg->d3_debug_data_length && fwrt->ops && |
---|
| 262 | + fwrt->ops->d3_debug_enable && |
---|
| 263 | + fwrt->ops->d3_debug_enable(fwrt->ops_ctx) && |
---|
| 264 | + iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); |
---|
| 265 | +} |
---|
| 266 | + |
---|
| 267 | +static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt) |
---|
| 268 | +{ |
---|
| 269 | + return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) && |
---|
| 270 | + !fwrt->trans->trans_cfg->gen2 && |
---|
| 271 | + fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX && |
---|
| 272 | + fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size && |
---|
| 273 | + fwrt->fw_paging_db[0].fw_paging_block; |
---|
| 274 | +} |
---|
| 275 | + |
---|
| 276 | +void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt); |
---|
| 277 | + |
---|
| 278 | +static inline void iwl_fw_flush_dumps(struct iwl_fw_runtime *fwrt) |
---|
| 279 | +{ |
---|
| 280 | + int i; |
---|
| 281 | + |
---|
| 282 | + iwl_dbg_tlv_del_timers(fwrt->trans); |
---|
| 283 | + for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) |
---|
| 284 | + flush_delayed_work(&fwrt->dump.wks[i].wk); |
---|
228 | 285 | } |
---|
229 | 286 | |
---|
230 | 287 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
---|
.. | .. |
---|
263 | 320 | |
---|
264 | 321 | #endif /* CONFIG_IWLWIFI_DEBUGFS */ |
---|
265 | 322 | |
---|
| 323 | +void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt); |
---|
| 324 | + |
---|
| 325 | +static inline void iwl_fw_lmac1_set_alive_err_table(struct iwl_trans *trans, |
---|
| 326 | + u32 lmac_error_event_table) |
---|
| 327 | +{ |
---|
| 328 | + if (!(trans->dbg.error_event_table_tlv_status & |
---|
| 329 | + IWL_ERROR_EVENT_TABLE_LMAC1) || |
---|
| 330 | + WARN_ON(trans->dbg.lmac_error_event_table[0] != |
---|
| 331 | + lmac_error_event_table)) |
---|
| 332 | + trans->dbg.lmac_error_event_table[0] = lmac_error_event_table; |
---|
| 333 | +} |
---|
| 334 | + |
---|
| 335 | +static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans, |
---|
| 336 | + u32 umac_error_event_table) |
---|
| 337 | +{ |
---|
| 338 | + if (!(trans->dbg.error_event_table_tlv_status & |
---|
| 339 | + IWL_ERROR_EVENT_TABLE_UMAC) || |
---|
| 340 | + WARN_ON(trans->dbg.umac_error_event_table != |
---|
| 341 | + umac_error_event_table)) |
---|
| 342 | + trans->dbg.umac_error_event_table = umac_error_event_table; |
---|
| 343 | +} |
---|
| 344 | + |
---|
| 345 | +static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt) |
---|
| 346 | +{ |
---|
| 347 | + enum iwl_fw_ini_time_point tp_id; |
---|
| 348 | + |
---|
| 349 | + if (!iwl_trans_dbg_ini_valid(fwrt->trans)) { |
---|
| 350 | + iwl_fw_dbg_collect_desc(fwrt, &iwl_dump_desc_assert, false, 0); |
---|
| 351 | + return; |
---|
| 352 | + } |
---|
| 353 | + |
---|
| 354 | + if (fwrt->trans->dbg.hw_error) { |
---|
| 355 | + tp_id = IWL_FW_INI_TIME_POINT_FW_HW_ERROR; |
---|
| 356 | + fwrt->trans->dbg.hw_error = false; |
---|
| 357 | + } else { |
---|
| 358 | + tp_id = IWL_FW_INI_TIME_POINT_FW_ASSERT; |
---|
| 359 | + } |
---|
| 360 | + |
---|
| 361 | + iwl_dbg_tlv_time_point(fwrt, tp_id, NULL); |
---|
| 362 | +} |
---|
| 363 | + |
---|
| 364 | +void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt); |
---|
| 365 | + |
---|
| 366 | +static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, |
---|
| 367 | + struct iwl_lmac_alive *lmac, |
---|
| 368 | + struct iwl_umac_alive *umac) |
---|
| 369 | +{ |
---|
| 370 | + if (lmac) { |
---|
| 371 | + fwrt->dump.fw_ver.type = lmac->ver_type; |
---|
| 372 | + fwrt->dump.fw_ver.subtype = lmac->ver_subtype; |
---|
| 373 | + fwrt->dump.fw_ver.lmac_major = le32_to_cpu(lmac->ucode_major); |
---|
| 374 | + fwrt->dump.fw_ver.lmac_minor = le32_to_cpu(lmac->ucode_minor); |
---|
| 375 | + } |
---|
| 376 | + |
---|
| 377 | + if (umac) { |
---|
| 378 | + fwrt->dump.fw_ver.umac_major = le32_to_cpu(umac->umac_major); |
---|
| 379 | + fwrt->dump.fw_ver.umac_minor = le32_to_cpu(umac->umac_minor); |
---|
| 380 | + } |
---|
| 381 | +} |
---|
266 | 382 | #endif /* __iwl_fw_dbg_h__ */ |
---|