Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
0002 /*
0003  * Copyright (C) 2005-2014, 2018-2019, 2021-2022 Intel Corporation
0004  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
0005  * Copyright (C) 2015-2017 Intel Deutschland GmbH
0006  */
0007 #ifndef __iwl_fw_dbg_h__
0008 #define __iwl_fw_dbg_h__
0009 #include <linux/workqueue.h>
0010 #include <net/cfg80211.h>
0011 #include "runtime.h"
0012 #include "iwl-prph.h"
0013 #include "iwl-io.h"
0014 #include "file.h"
0015 #include "error-dump.h"
0016 #include "api/commands.h"
0017 #include "api/dbg-tlv.h"
0018 #include "api/alive.h"
0019 
0020 /**
0021  * struct iwl_fw_dump_desc - describes the dump
0022  * @len: length of trig_desc->data
0023  * @trig_desc: the description of the dump
0024  */
0025 struct iwl_fw_dump_desc {
0026     size_t len;
0027     /* must be last */
0028     struct iwl_fw_error_dump_trigger_desc trig_desc;
0029 };
0030 
0031 /**
0032  * struct iwl_fw_dbg_params - register values to restore
0033  * @in_sample: DBGC_IN_SAMPLE value
0034  * @out_ctrl: DBGC_OUT_CTRL value
0035  */
0036 struct iwl_fw_dbg_params {
0037     u32 in_sample;
0038     u32 out_ctrl;
0039 };
0040 
0041 extern const struct iwl_fw_dump_desc iwl_dump_desc_assert;
0042 
0043 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
0044                 const struct iwl_fw_dump_desc *desc,
0045                 bool monitor_only, unsigned int delay);
0046 int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
0047                  enum iwl_fw_dbg_trigger trig_type);
0048 int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
0049                struct iwl_fwrt_dump_data *dump_data,
0050                bool sync);
0051 int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
0052                enum iwl_fw_dbg_trigger trig, const char *str,
0053                size_t len, struct iwl_fw_dbg_trigger_tlv *trigger);
0054 int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
0055                 struct iwl_fw_dbg_trigger_tlv *trigger,
0056                 const char *fmt, ...) __printf(3, 4);
0057 int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 id);
0058 
0059 #define iwl_fw_dbg_trigger_enabled(fw, id) ({           \
0060     void *__dbg_trigger = (fw)->dbg.trigger_tlv[(id)];  \
0061     unlikely(__dbg_trigger);                \
0062 })
0063 
0064 static inline struct iwl_fw_dbg_trigger_tlv*
0065 _iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id)
0066 {
0067     return fw->dbg.trigger_tlv[id];
0068 }
0069 
0070 #define iwl_fw_dbg_get_trigger(fw, id) ({           \
0071     BUILD_BUG_ON(!__builtin_constant_p(id));        \
0072     BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX);       \
0073     _iwl_fw_dbg_get_trigger((fw), (id));            \
0074 })
0075 
0076 static inline bool
0077 iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
0078                  struct wireless_dev *wdev)
0079 {
0080     u32 trig_vif = le32_to_cpu(trig->vif_type);
0081 
0082     return trig_vif == IWL_FW_DBG_CONF_VIF_ANY ||
0083            wdev->iftype == trig_vif;
0084 }
0085 
0086 static inline bool
0087 iwl_fw_dbg_trigger_stop_conf_match(struct iwl_fw_runtime *fwrt,
0088                    struct iwl_fw_dbg_trigger_tlv *trig)
0089 {
0090     return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
0091         (fwrt->dump.conf == FW_DBG_INVALID ||
0092         (BIT(fwrt->dump.conf) & le32_to_cpu(trig->stop_conf_ids))));
0093 }
0094 
0095 static inline bool
0096 iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, u32 id, u32 dis_usec)
0097 {
0098     unsigned long wind_jiff = usecs_to_jiffies(dis_usec);
0099 
0100     /* If this is the first event checked, jump to update start ts */
0101     if (fwrt->dump.non_collect_ts_start[id] &&
0102         (time_after(fwrt->dump.non_collect_ts_start[id] + wind_jiff,
0103             jiffies)))
0104         return true;
0105 
0106     fwrt->dump.non_collect_ts_start[id] = jiffies;
0107     return false;
0108 }
0109 
0110 static inline bool
0111 iwl_fw_dbg_trigger_check_stop(struct iwl_fw_runtime *fwrt,
0112                   struct wireless_dev *wdev,
0113                   struct iwl_fw_dbg_trigger_tlv *trig)
0114 {
0115     u32 usec = le16_to_cpu(trig->trig_dis_ms) * USEC_PER_MSEC;
0116 
0117     if (wdev && !iwl_fw_dbg_trigger_vif_match(trig, wdev))
0118         return false;
0119 
0120     if (iwl_fw_dbg_no_trig_window(fwrt, le32_to_cpu(trig->id), usec)) {
0121         IWL_WARN(fwrt, "Trigger %d occurred while no-collect window.\n",
0122              trig->id);
0123         return false;
0124     }
0125 
0126     return iwl_fw_dbg_trigger_stop_conf_match(fwrt, trig);
0127 }
0128 
0129 static inline struct iwl_fw_dbg_trigger_tlv*
0130 _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
0131                struct wireless_dev *wdev,
0132                const enum iwl_fw_dbg_trigger id)
0133 {
0134     struct iwl_fw_dbg_trigger_tlv *trig;
0135 
0136     if (iwl_trans_dbg_ini_valid(fwrt->trans))
0137         return NULL;
0138 
0139     if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id))
0140         return NULL;
0141 
0142     trig = _iwl_fw_dbg_get_trigger(fwrt->fw, id);
0143 
0144     if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trig))
0145         return NULL;
0146 
0147     return trig;
0148 }
0149 
0150 #define iwl_fw_dbg_trigger_on(fwrt, wdev, id) ({        \
0151     BUILD_BUG_ON(!__builtin_constant_p(id));        \
0152     BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX);       \
0153     _iwl_fw_dbg_trigger_on((fwrt), (wdev), (id));       \
0154 })
0155 
0156 static inline void
0157 _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt,
0158                 struct wireless_dev *wdev,
0159                 struct iwl_fw_dbg_trigger_tlv *trigger)
0160 {
0161     if (!trigger)
0162         return;
0163 
0164     if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trigger))
0165         return;
0166 
0167     iwl_fw_dbg_collect_trig(fwrt, trigger, NULL);
0168 }
0169 
0170 #define iwl_fw_dbg_trigger_simple_stop(fwrt, wdev, trig)    \
0171     _iwl_fw_dbg_trigger_simple_stop((fwrt), (wdev),     \
0172                     iwl_fw_dbg_get_trigger((fwrt)->fw,\
0173                                    (trig)))
0174 void iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt,
0175                        struct iwl_fw_dbg_params *params,
0176                        bool stop);
0177 
0178 #ifdef CONFIG_IWLWIFI_DEBUGFS
0179 static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt)
0180 {
0181     if (fwrt->cur_fw_img == IWL_UCODE_REGULAR &&
0182         (fwrt->fw->dbg.dest_tlv ||
0183          fwrt->trans->dbg.ini_dest != IWL_FW_INI_LOCATION_INVALID))
0184         fwrt->trans->dbg.rec_on = true;
0185 }
0186 #endif
0187 
0188 static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
0189 {
0190     fwrt->dump.conf = FW_DBG_INVALID;
0191 }
0192 
0193 void iwl_fw_error_dump_wk(struct work_struct *work);
0194 
0195 static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type)
0196 {
0197     return (fwrt->fw->dbg.dump_mask & BIT(type));
0198 }
0199 
0200 static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
0201 {
0202     return fw_has_capa(&fwrt->fw->ucode_capa,
0203                IWL_UCODE_TLV_CAPA_D3_DEBUG) &&
0204         fwrt->trans->cfg->d3_debug_data_length && fwrt->ops &&
0205         fwrt->ops->d3_debug_enable &&
0206         fwrt->ops->d3_debug_enable(fwrt->ops_ctx) &&
0207         iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
0208 }
0209 
0210 static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt)
0211 {
0212     return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) &&
0213         !fwrt->trans->trans_cfg->gen2 &&
0214         fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX &&
0215         fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
0216         fwrt->fw_paging_db[0].fw_paging_block;
0217 }
0218 
0219 void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt);
0220 
0221 static inline void iwl_fw_flush_dumps(struct iwl_fw_runtime *fwrt)
0222 {
0223     int i;
0224 
0225     iwl_dbg_tlv_del_timers(fwrt->trans);
0226     for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++)
0227         flush_delayed_work(&fwrt->dump.wks[i].wk);
0228 }
0229 
0230 #ifdef CONFIG_IWLWIFI_DEBUGFS
0231 static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt)
0232 {
0233     fwrt->timestamp.delay = 0;
0234     cancel_delayed_work_sync(&fwrt->timestamp.wk);
0235 }
0236 
0237 void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay);
0238 
0239 static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt)
0240 {
0241     cancel_delayed_work_sync(&fwrt->timestamp.wk);
0242 }
0243 
0244 static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt)
0245 {
0246     if (!fwrt->timestamp.delay)
0247         return;
0248 
0249     schedule_delayed_work(&fwrt->timestamp.wk,
0250                   round_jiffies_relative(fwrt->timestamp.delay));
0251 }
0252 
0253 #else
0254 
0255 static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {}
0256 
0257 static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt,
0258                         u32 delay) {}
0259 
0260 static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {}
0261 
0262 static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
0263 
0264 #endif /* CONFIG_IWLWIFI_DEBUGFS */
0265 
0266 void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt);
0267 
0268 static inline void iwl_fw_lmac1_set_alive_err_table(struct iwl_trans *trans,
0269                             u32 lmac_error_event_table)
0270 {
0271     if (!(trans->dbg.error_event_table_tlv_status &
0272           IWL_ERROR_EVENT_TABLE_LMAC1) ||
0273         WARN_ON(trans->dbg.lmac_error_event_table[0] !=
0274             lmac_error_event_table))
0275         trans->dbg.lmac_error_event_table[0] = lmac_error_event_table;
0276 }
0277 
0278 static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans,
0279                            u32 umac_error_event_table)
0280 {
0281     if (!(trans->dbg.error_event_table_tlv_status &
0282           IWL_ERROR_EVENT_TABLE_UMAC) ||
0283         WARN_ON(trans->dbg.umac_error_event_table !=
0284             umac_error_event_table))
0285         trans->dbg.umac_error_event_table = umac_error_event_table;
0286 }
0287 
0288 static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync)
0289 {
0290     enum iwl_fw_ini_time_point tp_id;
0291 
0292     if (!iwl_trans_dbg_ini_valid(fwrt->trans)) {
0293         iwl_fw_dbg_collect_desc(fwrt, &iwl_dump_desc_assert, false, 0);
0294         return;
0295     }
0296 
0297     if (fwrt->trans->dbg.hw_error) {
0298         tp_id = IWL_FW_INI_TIME_POINT_FW_HW_ERROR;
0299         fwrt->trans->dbg.hw_error = false;
0300     } else {
0301         tp_id = IWL_FW_INI_TIME_POINT_FW_ASSERT;
0302     }
0303 
0304     _iwl_dbg_tlv_time_point(fwrt, tp_id, NULL, sync);
0305 }
0306 
0307 void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt);
0308 
0309 static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt,
0310                            struct iwl_lmac_alive *lmac,
0311                            struct iwl_umac_alive *umac)
0312 {
0313     if (lmac) {
0314         fwrt->dump.fw_ver.type = lmac->ver_type;
0315         fwrt->dump.fw_ver.subtype = lmac->ver_subtype;
0316         fwrt->dump.fw_ver.lmac_major = le32_to_cpu(lmac->ucode_major);
0317         fwrt->dump.fw_ver.lmac_minor = le32_to_cpu(lmac->ucode_minor);
0318     }
0319 
0320     if (umac) {
0321         fwrt->dump.fw_ver.umac_major = le32_to_cpu(umac->umac_major);
0322         fwrt->dump.fw_ver.umac_minor = le32_to_cpu(umac->umac_minor);
0323     }
0324 }
0325 
0326 void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt);
0327 void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt,
0328                     u32 timepoint,
0329                     u32 timepoint_data);
0330 #endif  /* __iwl_fw_dbg_h__ */