linux/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
<<
>>
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        Intel Deutschland GmbH
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of version 2 of the GNU General Public License as
  14 * published by the Free Software Foundation.
  15 *
  16 * This program is distributed in the hope that it will be useful, but
  17 * WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  19 * General Public License for more details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with this program;
  23 *
  24 * The full GNU General Public License is included in this distribution
  25 * in the file called COPYING.
  26 *
  27 * Contact Information:
  28 *  Intel Linux Wireless <linuxwifi@intel.com>
  29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  30 *
  31 * BSD LICENSE
  32 *
  33 * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  34 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  35 * Copyright(c) 2015        Intel Deutschland GmbH
  36 * All rights reserved.
  37 *
  38 * Redistribution and use in source and binary forms, with or without
  39 * modification, are permitted provided that the following conditions
  40 * are met:
  41 *
  42 *  * Redistributions of source code must retain the above copyright
  43 *    notice, this list of conditions and the following disclaimer.
  44 *  * Redistributions in binary form must reproduce the above copyright
  45 *    notice, this list of conditions and the following disclaimer in
  46 *    the documentation and/or other materials provided with the
  47 *    distribution.
  48 *  * Neither the name Intel Corporation nor the names of its
  49 *    contributors may be used to endorse or promote products derived
  50 *    from this software without specific prior written permission.
  51 *
  52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  53 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  54 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  55 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  56 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  57 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  58 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  59 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  60 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  61 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  62 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  63 *
  64 *****************************************************************************/
  65#include <linux/devcoredump.h>
  66
  67#include "fw-dbg.h"
  68#include "iwl-io.h"
  69#include "mvm.h"
  70#include "iwl-prph.h"
  71#include "iwl-csr.h"
  72
  73static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
  74                                     const void *data, size_t datalen)
  75{
  76        const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
  77        ssize_t bytes_read;
  78        ssize_t bytes_read_trans;
  79
  80        if (offset < dump_ptrs->op_mode_len) {
  81                bytes_read = min_t(ssize_t, count,
  82                                   dump_ptrs->op_mode_len - offset);
  83                memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
  84                       bytes_read);
  85                offset += bytes_read;
  86                count -= bytes_read;
  87
  88                if (count == 0)
  89                        return bytes_read;
  90        } else {
  91                bytes_read = 0;
  92        }
  93
  94        if (!dump_ptrs->trans_ptr)
  95                return bytes_read;
  96
  97        offset -= dump_ptrs->op_mode_len;
  98        bytes_read_trans = min_t(ssize_t, count,
  99                                 dump_ptrs->trans_ptr->len - offset);
 100        memcpy(buffer + bytes_read,
 101               (u8 *)dump_ptrs->trans_ptr->data + offset,
 102               bytes_read_trans);
 103
 104        return bytes_read + bytes_read_trans;
 105}
 106
 107static void iwl_mvm_free_coredump(const void *data)
 108{
 109        const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
 110
 111        vfree(fw_error_dump->op_mode_ptr);
 112        vfree(fw_error_dump->trans_ptr);
 113        kfree(fw_error_dump);
 114}
 115
 116#define RADIO_REG_MAX_READ 0x2ad
 117static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm,
 118                                   struct iwl_fw_error_dump_data **dump_data)
 119{
 120        u8 *pos = (void *)(*dump_data)->data;
 121        unsigned long flags;
 122        int i;
 123
 124        if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
 125                return;
 126
 127        (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG);
 128        (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ);
 129
 130        for (i = 0; i < RADIO_REG_MAX_READ; i++) {
 131                u32 rd_cmd = RADIO_RSP_RD_CMD;
 132
 133                rd_cmd |= i << RADIO_RSP_ADDR_POS;
 134                iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd);
 135                *pos =  (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT);
 136
 137                pos++;
 138        }
 139
 140        *dump_data = iwl_fw_error_next_data(*dump_data);
 141
 142        iwl_trans_release_nic_access(mvm->trans, &flags);
 143}
 144
 145static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
 146                               struct iwl_fw_error_dump_data **dump_data)
 147{
 148        struct iwl_fw_error_dump_fifo *fifo_hdr;
 149        u32 *fifo_data;
 150        u32 fifo_len;
 151        unsigned long flags;
 152        int i, j;
 153
 154        if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
 155                return;
 156
 157        /* Pull RXF data from all RXFs */
 158        for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
 159                /*
 160                 * Keep aside the additional offset that might be needed for
 161                 * next RXF
 162                 */
 163                u32 offset_diff = RXF_DIFF_FROM_PREV * i;
 164
 165                fifo_hdr = (void *)(*dump_data)->data;
 166                fifo_data = (void *)fifo_hdr->data;
 167                fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
 168
 169                /* No need to try to read the data if the length is 0 */
 170                if (fifo_len == 0)
 171                        continue;
 172
 173                /* Add a TLV for the RXF */
 174                (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
 175                (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
 176
 177                fifo_hdr->fifo_num = cpu_to_le32(i);
 178                fifo_hdr->available_bytes =
 179                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 180                                                        RXF_RD_D_SPACE +
 181                                                        offset_diff));
 182                fifo_hdr->wr_ptr =
 183                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 184                                                        RXF_RD_WR_PTR +
 185                                                        offset_diff));
 186                fifo_hdr->rd_ptr =
 187                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 188                                                        RXF_RD_RD_PTR +
 189                                                        offset_diff));
 190                fifo_hdr->fence_ptr =
 191                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 192                                                        RXF_RD_FENCE_PTR +
 193                                                        offset_diff));
 194                fifo_hdr->fence_mode =
 195                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 196                                                        RXF_SET_FENCE_MODE +
 197                                                        offset_diff));
 198
 199                /* Lock fence */
 200                iwl_trans_write_prph(mvm->trans,
 201                                     RXF_SET_FENCE_MODE + offset_diff, 0x1);
 202                /* Set fence pointer to the same place like WR pointer */
 203                iwl_trans_write_prph(mvm->trans,
 204                                     RXF_LD_WR2FENCE + offset_diff, 0x1);
 205                /* Set fence offset */
 206                iwl_trans_write_prph(mvm->trans,
 207                                     RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
 208                                     0x0);
 209
 210                /* Read FIFO */
 211                fifo_len /= sizeof(u32); /* Size in DWORDS */
 212                for (j = 0; j < fifo_len; j++)
 213                        fifo_data[j] = iwl_trans_read_prph(mvm->trans,
 214                                                         RXF_FIFO_RD_FENCE_INC +
 215                                                         offset_diff);
 216                *dump_data = iwl_fw_error_next_data(*dump_data);
 217        }
 218
 219        /* Pull TXF data from all TXFs */
 220        for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
 221                /* Mark the number of TXF we're pulling now */
 222                iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
 223
 224                fifo_hdr = (void *)(*dump_data)->data;
 225                fifo_data = (void *)fifo_hdr->data;
 226                fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
 227
 228                /* No need to try to read the data if the length is 0 */
 229                if (fifo_len == 0)
 230                        continue;
 231
 232                /* Add a TLV for the FIFO */
 233                (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
 234                (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
 235
 236                fifo_hdr->fifo_num = cpu_to_le32(i);
 237                fifo_hdr->available_bytes =
 238                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 239                                                        TXF_FIFO_ITEM_CNT));
 240                fifo_hdr->wr_ptr =
 241                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 242                                                        TXF_WR_PTR));
 243                fifo_hdr->rd_ptr =
 244                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 245                                                        TXF_RD_PTR));
 246                fifo_hdr->fence_ptr =
 247                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 248                                                        TXF_FENCE_PTR));
 249                fifo_hdr->fence_mode =
 250                        cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 251                                                        TXF_LOCK_FENCE));
 252
 253                /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
 254                iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
 255                                     TXF_WR_PTR);
 256
 257                /* Dummy-read to advance the read pointer to the head */
 258                iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
 259
 260                /* Read FIFO */
 261                fifo_len /= sizeof(u32); /* Size in DWORDS */
 262                for (j = 0; j < fifo_len; j++)
 263                        fifo_data[j] = iwl_trans_read_prph(mvm->trans,
 264                                                          TXF_READ_MODIFY_DATA);
 265                *dump_data = iwl_fw_error_next_data(*dump_data);
 266        }
 267
 268        iwl_trans_release_nic_access(mvm->trans, &flags);
 269}
 270
 271void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
 272{
 273        if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert)
 274                return;
 275
 276        kfree(mvm->fw_dump_desc);
 277        mvm->fw_dump_desc = NULL;
 278}
 279
 280#define IWL8260_ICCM_OFFSET             0x44000 /* Only for B-step */
 281#define IWL8260_ICCM_LEN                0xC000 /* Only for B-step */
 282
 283static const struct {
 284        u32 start, end;
 285} iwl_prph_dump_addr[] = {
 286        { .start = 0x00a00000, .end = 0x00a00000 },
 287        { .start = 0x00a0000c, .end = 0x00a00024 },
 288        { .start = 0x00a0002c, .end = 0x00a0003c },
 289        { .start = 0x00a00410, .end = 0x00a00418 },
 290        { .start = 0x00a00420, .end = 0x00a00420 },
 291        { .start = 0x00a00428, .end = 0x00a00428 },
 292        { .start = 0x00a00430, .end = 0x00a0043c },
 293        { .start = 0x00a00444, .end = 0x00a00444 },
 294        { .start = 0x00a004c0, .end = 0x00a004cc },
 295        { .start = 0x00a004d8, .end = 0x00a004d8 },
 296        { .start = 0x00a004e0, .end = 0x00a004f0 },
 297        { .start = 0x00a00840, .end = 0x00a00840 },
 298        { .start = 0x00a00850, .end = 0x00a00858 },
 299        { .start = 0x00a01004, .end = 0x00a01008 },
 300        { .start = 0x00a01010, .end = 0x00a01010 },
 301        { .start = 0x00a01018, .end = 0x00a01018 },
 302        { .start = 0x00a01024, .end = 0x00a01024 },
 303        { .start = 0x00a0102c, .end = 0x00a01034 },
 304        { .start = 0x00a0103c, .end = 0x00a01040 },
 305        { .start = 0x00a01048, .end = 0x00a01094 },
 306        { .start = 0x00a01c00, .end = 0x00a01c20 },
 307        { .start = 0x00a01c58, .end = 0x00a01c58 },
 308        { .start = 0x00a01c7c, .end = 0x00a01c7c },
 309        { .start = 0x00a01c28, .end = 0x00a01c54 },
 310        { .start = 0x00a01c5c, .end = 0x00a01c5c },
 311        { .start = 0x00a01c60, .end = 0x00a01cdc },
 312        { .start = 0x00a01ce0, .end = 0x00a01d0c },
 313        { .start = 0x00a01d18, .end = 0x00a01d20 },
 314        { .start = 0x00a01d2c, .end = 0x00a01d30 },
 315        { .start = 0x00a01d40, .end = 0x00a01d5c },
 316        { .start = 0x00a01d80, .end = 0x00a01d80 },
 317        { .start = 0x00a01d98, .end = 0x00a01d9c },
 318        { .start = 0x00a01da8, .end = 0x00a01da8 },
 319        { .start = 0x00a01db8, .end = 0x00a01df4 },
 320        { .start = 0x00a01dc0, .end = 0x00a01dfc },
 321        { .start = 0x00a01e00, .end = 0x00a01e2c },
 322        { .start = 0x00a01e40, .end = 0x00a01e60 },
 323        { .start = 0x00a01e68, .end = 0x00a01e6c },
 324        { .start = 0x00a01e74, .end = 0x00a01e74 },
 325        { .start = 0x00a01e84, .end = 0x00a01e90 },
 326        { .start = 0x00a01e9c, .end = 0x00a01ec4 },
 327        { .start = 0x00a01ed0, .end = 0x00a01ee0 },
 328        { .start = 0x00a01f00, .end = 0x00a01f1c },
 329        { .start = 0x00a01f44, .end = 0x00a01ffc },
 330        { .start = 0x00a02000, .end = 0x00a02048 },
 331        { .start = 0x00a02068, .end = 0x00a020f0 },
 332        { .start = 0x00a02100, .end = 0x00a02118 },
 333        { .start = 0x00a02140, .end = 0x00a0214c },
 334        { .start = 0x00a02168, .end = 0x00a0218c },
 335        { .start = 0x00a021c0, .end = 0x00a021c0 },
 336        { .start = 0x00a02400, .end = 0x00a02410 },
 337        { .start = 0x00a02418, .end = 0x00a02420 },
 338        { .start = 0x00a02428, .end = 0x00a0242c },
 339        { .start = 0x00a02434, .end = 0x00a02434 },
 340        { .start = 0x00a02440, .end = 0x00a02460 },
 341        { .start = 0x00a02468, .end = 0x00a024b0 },
 342        { .start = 0x00a024c8, .end = 0x00a024cc },
 343        { .start = 0x00a02500, .end = 0x00a02504 },
 344        { .start = 0x00a0250c, .end = 0x00a02510 },
 345        { .start = 0x00a02540, .end = 0x00a02554 },
 346        { .start = 0x00a02580, .end = 0x00a025f4 },
 347        { .start = 0x00a02600, .end = 0x00a0260c },
 348        { .start = 0x00a02648, .end = 0x00a02650 },
 349        { .start = 0x00a02680, .end = 0x00a02680 },
 350        { .start = 0x00a026c0, .end = 0x00a026d0 },
 351        { .start = 0x00a02700, .end = 0x00a0270c },
 352        { .start = 0x00a02804, .end = 0x00a02804 },
 353        { .start = 0x00a02818, .end = 0x00a0281c },
 354        { .start = 0x00a02c00, .end = 0x00a02db4 },
 355        { .start = 0x00a02df4, .end = 0x00a02fb0 },
 356        { .start = 0x00a03000, .end = 0x00a03014 },
 357        { .start = 0x00a0301c, .end = 0x00a0302c },
 358        { .start = 0x00a03034, .end = 0x00a03038 },
 359        { .start = 0x00a03040, .end = 0x00a03048 },
 360        { .start = 0x00a03060, .end = 0x00a03068 },
 361        { .start = 0x00a03070, .end = 0x00a03074 },
 362        { .start = 0x00a0307c, .end = 0x00a0307c },
 363        { .start = 0x00a03080, .end = 0x00a03084 },
 364        { .start = 0x00a0308c, .end = 0x00a03090 },
 365        { .start = 0x00a03098, .end = 0x00a03098 },
 366        { .start = 0x00a030a0, .end = 0x00a030a0 },
 367        { .start = 0x00a030a8, .end = 0x00a030b4 },
 368        { .start = 0x00a030bc, .end = 0x00a030bc },
 369        { .start = 0x00a030c0, .end = 0x00a0312c },
 370        { .start = 0x00a03c00, .end = 0x00a03c5c },
 371        { .start = 0x00a04400, .end = 0x00a04454 },
 372        { .start = 0x00a04460, .end = 0x00a04474 },
 373        { .start = 0x00a044c0, .end = 0x00a044ec },
 374        { .start = 0x00a04500, .end = 0x00a04504 },
 375        { .start = 0x00a04510, .end = 0x00a04538 },
 376        { .start = 0x00a04540, .end = 0x00a04548 },
 377        { .start = 0x00a04560, .end = 0x00a0457c },
 378        { .start = 0x00a04590, .end = 0x00a04598 },
 379        { .start = 0x00a045c0, .end = 0x00a045f4 },
 380        { .start = 0x00a44000, .end = 0x00a7bf80 },
 381};
 382
 383static u32 iwl_dump_prph(struct iwl_trans *trans,
 384                         struct iwl_fw_error_dump_data **data)
 385{
 386        struct iwl_fw_error_dump_prph *prph;
 387        unsigned long flags;
 388        u32 prph_len = 0, i;
 389
 390        if (!iwl_trans_grab_nic_access(trans, &flags))
 391                return 0;
 392
 393        for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
 394                /* The range includes both boundaries */
 395                int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
 396                         iwl_prph_dump_addr[i].start + 4;
 397                int reg;
 398                __le32 *val;
 399
 400                prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
 401
 402                (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
 403                (*data)->len = cpu_to_le32(sizeof(*prph) +
 404                                        num_bytes_in_chunk);
 405                prph = (void *)(*data)->data;
 406                prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
 407                val = (void *)prph->data;
 408
 409                for (reg = iwl_prph_dump_addr[i].start;
 410                     reg <= iwl_prph_dump_addr[i].end;
 411                     reg += 4)
 412                        *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
 413                                                                   reg));
 414
 415                *data = iwl_fw_error_next_data(*data);
 416        }
 417
 418        iwl_trans_release_nic_access(trans, &flags);
 419
 420        return prph_len;
 421}
 422
 423void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 424{
 425        struct iwl_fw_error_dump_file *dump_file;
 426        struct iwl_fw_error_dump_data *dump_data;
 427        struct iwl_fw_error_dump_info *dump_info;
 428        struct iwl_fw_error_dump_mem *dump_mem;
 429        struct iwl_fw_error_dump_trigger_desc *dump_trig;
 430        struct iwl_mvm_dump_ptrs *fw_error_dump;
 431        u32 sram_len, sram_ofs;
 432        u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0;
 433        u32 smem_len = mvm->cfg->smem_len;
 434        u32 sram2_len = mvm->cfg->dccm2_len;
 435        bool monitor_dump_only = false;
 436        int i;
 437
 438        lockdep_assert_held(&mvm->mutex);
 439
 440        /* there's no point in fw dump if the bus is dead */
 441        if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
 442                IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
 443                goto out;
 444        }
 445
 446        if (mvm->fw_dump_trig &&
 447            mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
 448                monitor_dump_only = true;
 449
 450        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
 451        if (!fw_error_dump)
 452                goto out;
 453
 454        /* SRAM - include stack CCM if driver knows the values for it */
 455        if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
 456                const struct fw_img *img;
 457
 458                img = &mvm->fw->img[mvm->cur_ucode];
 459                sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
 460                sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
 461        } else {
 462                sram_ofs = mvm->cfg->dccm_offset;
 463                sram_len = mvm->cfg->dccm_len;
 464        }
 465
 466        /* reading RXF/TXF sizes */
 467        if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
 468                struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
 469
 470                fifo_data_len = 0;
 471
 472                /* Count RXF size */
 473                for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
 474                        if (!mem_cfg->rxfifo_size[i])
 475                                continue;
 476
 477                        /* Add header info */
 478                        fifo_data_len += mem_cfg->rxfifo_size[i] +
 479                                         sizeof(*dump_data) +
 480                                         sizeof(struct iwl_fw_error_dump_fifo);
 481                }
 482
 483                for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
 484                        if (!mem_cfg->txfifo_size[i])
 485                                continue;
 486
 487                        /* Add header info */
 488                        fifo_data_len += mem_cfg->txfifo_size[i] +
 489                                         sizeof(*dump_data) +
 490                                         sizeof(struct iwl_fw_error_dump_fifo);
 491                }
 492
 493                /* Make room for PRPH registers */
 494                for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
 495                        /* The range includes both boundaries */
 496                        int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
 497                                iwl_prph_dump_addr[i].start + 4;
 498
 499                        prph_len += sizeof(*dump_data) +
 500                                sizeof(struct iwl_fw_error_dump_prph) +
 501                                num_bytes_in_chunk;
 502                }
 503
 504                if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
 505                        radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
 506        }
 507
 508        file_len = sizeof(*dump_file) +
 509                   sizeof(*dump_data) * 2 +
 510                   sram_len + sizeof(*dump_mem) +
 511                   fifo_data_len +
 512                   prph_len +
 513                   radio_len +
 514                   sizeof(*dump_info);
 515
 516        /* Make room for the SMEM, if it exists */
 517        if (smem_len)
 518                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
 519
 520        /* Make room for the secondary SRAM, if it exists */
 521        if (sram2_len)
 522                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
 523
 524        /* Make room for fw's virtual image pages, if it exists */
 525        if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
 526                file_len += mvm->num_of_paging_blk *
 527                        (sizeof(*dump_data) +
 528                         sizeof(struct iwl_fw_error_dump_paging) +
 529                         PAGING_BLOCK_SIZE);
 530
 531        /* If we only want a monitor dump, reset the file length */
 532        if (monitor_dump_only) {
 533                file_len = sizeof(*dump_file) + sizeof(*dump_data) +
 534                           sizeof(*dump_info);
 535        }
 536
 537        /*
 538         * In 8000 HW family B-step include the ICCM (which resides separately)
 539         */
 540        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
 541            CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
 542                file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
 543                            IWL8260_ICCM_LEN;
 544
 545        if (mvm->fw_dump_desc)
 546                file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
 547                            mvm->fw_dump_desc->len;
 548
 549        dump_file = vzalloc(file_len);
 550        if (!dump_file) {
 551                kfree(fw_error_dump);
 552                goto out;
 553        }
 554
 555        fw_error_dump->op_mode_ptr = dump_file;
 556
 557        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
 558        dump_data = (void *)dump_file->data;
 559
 560        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
 561        dump_data->len = cpu_to_le32(sizeof(*dump_info));
 562        dump_info = (void *)dump_data->data;
 563        dump_info->device_family =
 564                mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
 565                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
 566                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
 567        dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
 568        memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
 569               sizeof(dump_info->fw_human_readable));
 570        strncpy(dump_info->dev_human_readable, mvm->cfg->name,
 571                sizeof(dump_info->dev_human_readable));
 572        strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
 573                sizeof(dump_info->bus_human_readable));
 574
 575        dump_data = iwl_fw_error_next_data(dump_data);
 576        /* We only dump the FIFOs if the FW is in error state */
 577        if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
 578                iwl_mvm_dump_fifos(mvm, &dump_data);
 579                if (radio_len)
 580                        iwl_mvm_read_radio_reg(mvm, &dump_data);
 581        }
 582
 583        if (mvm->fw_dump_desc) {
 584                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
 585                dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
 586                                             mvm->fw_dump_desc->len);
 587                dump_trig = (void *)dump_data->data;
 588                memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
 589                       sizeof(*dump_trig) + mvm->fw_dump_desc->len);
 590
 591                dump_data = iwl_fw_error_next_data(dump_data);
 592        }
 593
 594        /* In case we only want monitor dump, skip to dump trasport data */
 595        if (monitor_dump_only)
 596                goto dump_trans_data;
 597
 598        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 599        dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
 600        dump_mem = (void *)dump_data->data;
 601        dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 602        dump_mem->offset = cpu_to_le32(sram_ofs);
 603        iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
 604                                 sram_len);
 605
 606        if (smem_len) {
 607                dump_data = iwl_fw_error_next_data(dump_data);
 608                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 609                dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
 610                dump_mem = (void *)dump_data->data;
 611                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
 612                dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
 613                iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
 614                                         dump_mem->data, smem_len);
 615        }
 616
 617        if (sram2_len) {
 618                dump_data = iwl_fw_error_next_data(dump_data);
 619                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 620                dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
 621                dump_mem = (void *)dump_data->data;
 622                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 623                dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
 624                iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
 625                                         dump_mem->data, sram2_len);
 626        }
 627
 628        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
 629            CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
 630                dump_data = iwl_fw_error_next_data(dump_data);
 631                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 632                dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
 633                                             sizeof(*dump_mem));
 634                dump_mem = (void *)dump_data->data;
 635                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 636                dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
 637                iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
 638                                         dump_mem->data, IWL8260_ICCM_LEN);
 639        }
 640
 641        /* Dump fw's virtual image */
 642        if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
 643                u32 i;
 644
 645                for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
 646                        struct iwl_fw_error_dump_paging *paging;
 647                        struct page *pages =
 648                                mvm->fw_paging_db[i].fw_paging_block;
 649
 650                        dump_data = iwl_fw_error_next_data(dump_data);
 651                        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
 652                        dump_data->len = cpu_to_le32(sizeof(*paging) +
 653                                                     PAGING_BLOCK_SIZE);
 654                        paging = (void *)dump_data->data;
 655                        paging->index = cpu_to_le32(i);
 656                        memcpy(paging->data, page_address(pages),
 657                               PAGING_BLOCK_SIZE);
 658                }
 659        }
 660
 661        dump_data = iwl_fw_error_next_data(dump_data);
 662        if (prph_len)
 663                iwl_dump_prph(mvm->trans, &dump_data);
 664
 665dump_trans_data:
 666        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
 667                                                       mvm->fw_dump_trig);
 668        fw_error_dump->op_mode_len = file_len;
 669        if (fw_error_dump->trans_ptr)
 670                file_len += fw_error_dump->trans_ptr->len;
 671        dump_file->file_len = cpu_to_le32(file_len);
 672
 673        dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
 674                      GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
 675
 676out:
 677        iwl_mvm_free_fw_dump_desc(mvm);
 678        mvm->fw_dump_trig = NULL;
 679        clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
 680}
 681
 682const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
 683        .trig_desc = {
 684                .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
 685        },
 686};
 687
 688int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
 689                                const struct iwl_mvm_dump_desc *desc,
 690                                const struct iwl_fw_dbg_trigger_tlv *trigger)
 691{
 692        unsigned int delay = 0;
 693
 694        if (trigger)
 695                delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
 696
 697        if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
 698                return -EBUSY;
 699
 700        if (WARN_ON(mvm->fw_dump_desc))
 701                iwl_mvm_free_fw_dump_desc(mvm);
 702
 703        IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
 704                 le32_to_cpu(desc->trig_desc.type));
 705
 706        mvm->fw_dump_desc = desc;
 707        mvm->fw_dump_trig = trigger;
 708
 709        queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
 710
 711        return 0;
 712}
 713
 714int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
 715                           const char *str, size_t len,
 716                           const struct iwl_fw_dbg_trigger_tlv *trigger)
 717{
 718        struct iwl_mvm_dump_desc *desc;
 719
 720        desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
 721        if (!desc)
 722                return -ENOMEM;
 723
 724        desc->len = len;
 725        desc->trig_desc.type = cpu_to_le32(trig);
 726        memcpy(desc->trig_desc.data, str, len);
 727
 728        return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
 729}
 730
 731int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
 732                                struct iwl_fw_dbg_trigger_tlv *trigger,
 733                                const char *fmt, ...)
 734{
 735        u16 occurrences = le16_to_cpu(trigger->occurrences);
 736        int ret, len = 0;
 737        char buf[64];
 738
 739        if (!occurrences)
 740                return 0;
 741
 742        if (fmt) {
 743                va_list ap;
 744
 745                buf[sizeof(buf) - 1] = '\0';
 746
 747                va_start(ap, fmt);
 748                vsnprintf(buf, sizeof(buf), fmt, ap);
 749                va_end(ap);
 750
 751                /* check for truncation */
 752                if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
 753                        buf[sizeof(buf) - 1] = '\0';
 754
 755                len = strlen(buf) + 1;
 756        }
 757
 758        ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
 759                                     trigger);
 760
 761        if (ret)
 762                return ret;
 763
 764        trigger->occurrences = cpu_to_le16(occurrences - 1);
 765        return 0;
 766}
 767
 768static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
 769{
 770        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
 771                iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
 772        else
 773                iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
 774}
 775
 776int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
 777{
 778        u8 *ptr;
 779        int ret;
 780        int i;
 781
 782        if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
 783                      "Invalid configuration %d\n", conf_id))
 784                return -EINVAL;
 785
 786        /* EARLY START - firmware's configuration is hard coded */
 787        if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
 788             !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
 789            conf_id == FW_DBG_START_FROM_ALIVE) {
 790                iwl_mvm_restart_early_start(mvm);
 791                return 0;
 792        }
 793
 794        if (!mvm->fw->dbg_conf_tlv[conf_id])
 795                return -EINVAL;
 796
 797        if (mvm->fw_dbg_conf != FW_DBG_INVALID)
 798                IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
 799                         mvm->fw_dbg_conf);
 800
 801        /* Send all HCMDs for configuring the FW debug */
 802        ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
 803        for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
 804                struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
 805
 806                ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
 807                                           le16_to_cpu(cmd->len), cmd->data);
 808                if (ret)
 809                        return ret;
 810
 811                ptr += sizeof(*cmd);
 812                ptr += le16_to_cpu(cmd->len);
 813        }
 814
 815        mvm->fw_dbg_conf = conf_id;
 816        return ret;
 817}
 818