| .. | .. |
|---|
| 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__ */ |
|---|