linux/drivers/net/wireless/intel/iwlwifi/fw/dbg.h
<<
>>
Prefs
   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
  10 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
  11 * Copyright(c) 2018 - 2019 Intel Corporation
  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 *
  22 * The full GNU General Public License is included in this distribution
  23 * in the file called COPYING.
  24 *
  25 * Contact Information:
  26 *  Intel Linux Wireless <linuxwifi@intel.com>
  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
  33 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
  34 * Copyright(c) 2018 - 2019 Intel Corporation
  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
  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"
  70#include "iwl-prph.h"
  71#include "iwl-io.h"
  72#include "file.h"
  73#include "error-dump.h"
  74#include "api/commands.h"
  75#include "api/dbg-tlv.h"
  76#include "api/alive.h"
  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 */
  83struct iwl_fw_dump_desc {
  84        size_t len;
  85        /* must be last */
  86        struct iwl_fw_error_dump_trigger_desc trig_desc;
  87};
  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 */
  94struct iwl_fw_dbg_params {
  95        u32 in_sample;
  96        u32 out_ctrl;
  97};
  98
  99extern const struct iwl_fw_dump_desc iwl_dump_desc_assert;
 100
 101static 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;
 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;
 110}
 111
 112int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
 113                            const struct iwl_fw_dump_desc *desc,
 114                            bool monitor_only, unsigned int delay);
 115int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
 116                             enum iwl_fw_dbg_trigger trig_type);
 117int _iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
 118                            enum iwl_fw_ini_trigger_id id);
 119int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, u32 legacy_trigger_id);
 120int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
 121                       enum iwl_fw_dbg_trigger trig, const char *str,
 122                       size_t len, struct iwl_fw_dbg_trigger_tlv *trigger);
 123int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
 124                            struct iwl_fw_dbg_trigger_tlv *trigger,
 125                            const char *fmt, ...) __printf(3, 4);
 126int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 id);
 127
 128#define iwl_fw_dbg_trigger_enabled(fw, id) ({                   \
 129        void *__dbg_trigger = (fw)->dbg.trigger_tlv[(id)];      \
 130        unlikely(__dbg_trigger);                                \
 131})
 132
 133static inline struct iwl_fw_dbg_trigger_tlv*
 134_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id)
 135{
 136        return fw->dbg.trigger_tlv[id];
 137}
 138
 139#define iwl_fw_dbg_get_trigger(fw, id) ({                       \
 140        BUILD_BUG_ON(!__builtin_constant_p(id));                \
 141        BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX);               \
 142        _iwl_fw_dbg_get_trigger((fw), (id));                    \
 143})
 144
 145static inline bool
 146iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
 147                             struct wireless_dev *wdev)
 148{
 149        u32 trig_vif = le32_to_cpu(trig->vif_type);
 150
 151        return trig_vif == IWL_FW_DBG_CONF_VIF_ANY ||
 152               wdev->iftype == trig_vif;
 153}
 154
 155static inline bool
 156iwl_fw_dbg_trigger_stop_conf_match(struct iwl_fw_runtime *fwrt,
 157                                   struct iwl_fw_dbg_trigger_tlv *trig)
 158{
 159        return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
 160                (fwrt->dump.conf == FW_DBG_INVALID ||
 161                (BIT(fwrt->dump.conf) & le32_to_cpu(trig->stop_conf_ids))));
 162}
 163
 164static inline bool
 165iwl_fw_dbg_no_trig_window(struct iwl_fw_runtime *fwrt, u32 id, u32 dis_usec)
 166{
 167        unsigned long wind_jiff = usecs_to_jiffies(dis_usec);
 168
 169        /* If this is the first event checked, jump to update start ts */
 170        if (fwrt->dump.non_collect_ts_start[id] &&
 171            (time_after(fwrt->dump.non_collect_ts_start[id] + wind_jiff,
 172                        jiffies)))
 173                return true;
 174
 175        fwrt->dump.non_collect_ts_start[id] = jiffies;
 176        return false;
 177}
 178
 179static inline bool
 180iwl_fw_dbg_trigger_check_stop(struct iwl_fw_runtime *fwrt,
 181                              struct wireless_dev *wdev,
 182                              struct iwl_fw_dbg_trigger_tlv *trig)
 183{
 184        u32 usec = le16_to_cpu(trig->trig_dis_ms) * USEC_PER_MSEC;
 185
 186        if (wdev && !iwl_fw_dbg_trigger_vif_match(trig, wdev))
 187                return false;
 188
 189        if (iwl_fw_dbg_no_trig_window(fwrt, le32_to_cpu(trig->id), usec)) {
 190                IWL_WARN(fwrt, "Trigger %d occurred while no-collect window.\n",
 191                         trig->id);
 192                return false;
 193        }
 194
 195        return iwl_fw_dbg_trigger_stop_conf_match(fwrt, trig);
 196}
 197
 198static inline struct iwl_fw_dbg_trigger_tlv*
 199_iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
 200                       struct wireless_dev *wdev,
 201                       const enum iwl_fw_dbg_trigger id)
 202{
 203        struct iwl_fw_dbg_trigger_tlv *trig;
 204
 205        if (iwl_trans_dbg_ini_valid(fwrt->trans))
 206                return NULL;
 207
 208        if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id))
 209                return NULL;
 210
 211        trig = _iwl_fw_dbg_get_trigger(fwrt->fw, id);
 212
 213        if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trig))
 214                return NULL;
 215
 216        return trig;
 217}
 218
 219#define iwl_fw_dbg_trigger_on(fwrt, wdev, id) ({                \
 220        BUILD_BUG_ON(!__builtin_constant_p(id));                \
 221        BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX);               \
 222        _iwl_fw_dbg_trigger_on((fwrt), (wdev), (id));           \
 223})
 224
 225static inline bool
 226iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt,
 227                      enum iwl_fw_ini_trigger_id id)
 228{
 229        struct iwl_fw_ini_trigger *trig;
 230        u32 usec;
 231
 232        if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||
 233            id == IWL_FW_TRIGGER_ID_INVALID || id >= IWL_FW_TRIGGER_ID_NUM ||
 234            !fwrt->dump.active_trigs[id].active)
 235                return false;
 236
 237        trig = fwrt->dump.active_trigs[id].trig;
 238        usec = le32_to_cpu(trig->ignore_consec);
 239
 240        if (iwl_fw_dbg_no_trig_window(fwrt, id, usec)) {
 241                IWL_WARN(fwrt, "Trigger %d fired in no-collect window\n", id);
 242                return false;
 243        }
 244
 245        return true;
 246}
 247
 248static inline void
 249_iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt,
 250                                struct wireless_dev *wdev,
 251                                struct iwl_fw_dbg_trigger_tlv *trigger)
 252{
 253        if (!trigger)
 254                return;
 255
 256        if (!iwl_fw_dbg_trigger_check_stop(fwrt, wdev, trigger))
 257                return;
 258
 259        iwl_fw_dbg_collect_trig(fwrt, trigger, NULL);
 260}
 261
 262#define iwl_fw_dbg_trigger_simple_stop(fwrt, wdev, trig)        \
 263        _iwl_fw_dbg_trigger_simple_stop((fwrt), (wdev),         \
 264                                        iwl_fw_dbg_get_trigger((fwrt)->fw,\
 265                                                               (trig)))
 266int iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt,
 267                                      struct iwl_fw_dbg_params *params,
 268                                      bool stop);
 269
 270#ifdef CONFIG_IWLWIFI_DEBUGFS
 271static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt)
 272{
 273        if (fwrt->cur_fw_img == IWL_UCODE_REGULAR &&
 274            (fwrt->fw->dbg.dest_tlv ||
 275             fwrt->trans->dbg.ini_dest != IWL_FW_INI_LOCATION_INVALID))
 276                fwrt->trans->dbg.rec_on = true;
 277}
 278#endif
 279
 280static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
 281{
 282        fwrt->dump.conf = FW_DBG_INVALID;
 283}
 284
 285void iwl_fw_error_dump_wk(struct work_struct *work);
 286
 287static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type)
 288{
 289        return (fwrt->fw->dbg.dump_mask & BIT(type));
 290}
 291
 292static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
 293{
 294        return fw_has_capa(&fwrt->fw->ucode_capa,
 295                           IWL_UCODE_TLV_CAPA_D3_DEBUG) &&
 296                fwrt->trans->cfg->d3_debug_data_length && fwrt->ops &&
 297                fwrt->ops->d3_debug_enable &&
 298                fwrt->ops->d3_debug_enable(fwrt->ops_ctx) &&
 299                iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
 300}
 301
 302static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt)
 303{
 304        return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) &&
 305                !fwrt->trans->trans_cfg->gen2 &&
 306                fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX &&
 307                fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
 308                fwrt->fw_paging_db[0].fw_paging_block;
 309}
 310
 311void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt);
 312
 313static inline void iwl_fw_flush_dumps(struct iwl_fw_runtime *fwrt)
 314{
 315        int i;
 316
 317        iwl_dbg_tlv_del_timers(fwrt->trans);
 318        for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) {
 319                flush_delayed_work(&fwrt->dump.wks[i].wk);
 320                fwrt->dump.wks[i].ini_trig_id = IWL_FW_TRIGGER_ID_INVALID;
 321        }
 322}
 323
 324#ifdef CONFIG_IWLWIFI_DEBUGFS
 325static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt)
 326{
 327        fwrt->timestamp.delay = 0;
 328        cancel_delayed_work_sync(&fwrt->timestamp.wk);
 329}
 330
 331void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay);
 332
 333static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt)
 334{
 335        cancel_delayed_work_sync(&fwrt->timestamp.wk);
 336}
 337
 338static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt)
 339{
 340        if (!fwrt->timestamp.delay)
 341                return;
 342
 343        schedule_delayed_work(&fwrt->timestamp.wk,
 344                              round_jiffies_relative(fwrt->timestamp.delay));
 345}
 346
 347#else
 348
 349static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {}
 350
 351static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt,
 352                                            u32 delay) {}
 353
 354static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {}
 355
 356static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
 357
 358#endif /* CONFIG_IWLWIFI_DEBUGFS */
 359
 360void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt);
 361
 362static inline void iwl_fw_lmac1_set_alive_err_table(struct iwl_trans *trans,
 363                                                    u32 lmac_error_event_table)
 364{
 365        if (!(trans->dbg.error_event_table_tlv_status &
 366              IWL_ERROR_EVENT_TABLE_LMAC1) ||
 367            WARN_ON(trans->dbg.lmac_error_event_table[0] !=
 368                    lmac_error_event_table))
 369                trans->dbg.lmac_error_event_table[0] = lmac_error_event_table;
 370}
 371
 372static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans,
 373                                                   u32 umac_error_event_table)
 374{
 375        if (!(trans->dbg.error_event_table_tlv_status &
 376              IWL_ERROR_EVENT_TABLE_UMAC) ||
 377            WARN_ON(trans->dbg.umac_error_event_table !=
 378                    umac_error_event_table))
 379                trans->dbg.umac_error_event_table = umac_error_event_table;
 380}
 381
 382static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt)
 383{
 384        if (iwl_trans_dbg_ini_valid(fwrt->trans) && fwrt->trans->dbg.hw_error) {
 385                _iwl_fw_dbg_ini_collect(fwrt, IWL_FW_TRIGGER_ID_FW_HW_ERROR);
 386                fwrt->trans->dbg.hw_error = false;
 387        } else {
 388                iwl_fw_dbg_collect_desc(fwrt, &iwl_dump_desc_assert, false, 0);
 389        }
 390}
 391
 392void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt);
 393
 394static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt,
 395                                               struct iwl_lmac_alive *lmac,
 396                                               struct iwl_umac_alive *umac)
 397{
 398        if (lmac) {
 399                fwrt->dump.fw_ver.type = lmac->ver_type;
 400                fwrt->dump.fw_ver.subtype = lmac->ver_subtype;
 401                fwrt->dump.fw_ver.lmac_major = le32_to_cpu(lmac->ucode_major);
 402                fwrt->dump.fw_ver.lmac_minor = le32_to_cpu(lmac->ucode_minor);
 403        }
 404
 405        if (umac) {
 406                fwrt->dump.fw_ver.umac_major = le32_to_cpu(umac->umac_major);
 407                fwrt->dump.fw_ver.umac_minor = le32_to_cpu(umac->umac_minor);
 408        }
 409}
 410#endif  /* __iwl_fw_dbg_h__ */
 411