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 - 2016 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 - 2016 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                                     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(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        if (fw_has_capa(&mvm->fw->ucode_capa,
 269                        IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
 270                /* Pull UMAC internal TXF data from all TXFs */
 271                for (i = 0;
 272                     i < ARRAY_SIZE(mvm->shared_mem_cfg.internal_txfifo_size);
 273                     i++) {
 274                        fifo_hdr = (void *)(*dump_data)->data;
 275                        fifo_data = (void *)fifo_hdr->data;
 276                        fifo_len = mvm->shared_mem_cfg.internal_txfifo_size[i];
 277
 278                        /* No need to try to read the data if the length is 0 */
 279                        if (fifo_len == 0)
 280                                continue;
 281
 282                        /* Add a TLV for the internal FIFOs */
 283                        (*dump_data)->type =
 284                                cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF);
 285                        (*dump_data)->len =
 286                                cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
 287
 288                        fifo_hdr->fifo_num = cpu_to_le32(i);
 289
 290                        /* Mark the number of TXF we're pulling now */
 291                        iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i +
 292                                ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size));
 293
 294                        fifo_hdr->available_bytes =
 295                                cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 296                                                                TXF_CPU2_FIFO_ITEM_CNT));
 297                        fifo_hdr->wr_ptr =
 298                                cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 299                                                                TXF_CPU2_WR_PTR));
 300                        fifo_hdr->rd_ptr =
 301                                cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 302                                                                TXF_CPU2_RD_PTR));
 303                        fifo_hdr->fence_ptr =
 304                                cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 305                                                                TXF_CPU2_FENCE_PTR));
 306                        fifo_hdr->fence_mode =
 307                                cpu_to_le32(iwl_trans_read_prph(mvm->trans,
 308                                                                TXF_CPU2_LOCK_FENCE));
 309
 310                        /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */
 311                        iwl_trans_write_prph(mvm->trans,
 312                                             TXF_CPU2_READ_MODIFY_ADDR,
 313                                             TXF_CPU2_WR_PTR);
 314
 315                        /* Dummy-read to advance the read pointer to head */
 316                        iwl_trans_read_prph(mvm->trans,
 317                                            TXF_CPU2_READ_MODIFY_DATA);
 318
 319                        /* Read FIFO */
 320                        fifo_len /= sizeof(u32); /* Size in DWORDS */
 321                        for (j = 0; j < fifo_len; j++)
 322                                fifo_data[j] =
 323                                        iwl_trans_read_prph(mvm->trans,
 324                                                            TXF_CPU2_READ_MODIFY_DATA);
 325                        *dump_data = iwl_fw_error_next_data(*dump_data);
 326                }
 327        }
 328
 329        iwl_trans_release_nic_access(mvm->trans, &flags);
 330}
 331
 332void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
 333{
 334        if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert)
 335                return;
 336
 337        kfree(mvm->fw_dump_desc);
 338        mvm->fw_dump_desc = NULL;
 339}
 340
 341#define IWL8260_ICCM_OFFSET             0x44000 /* Only for B-step */
 342#define IWL8260_ICCM_LEN                0xC000 /* Only for B-step */
 343
 344struct iwl_prph_range {
 345        u32 start, end;
 346};
 347
 348static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = {
 349        { .start = 0x00a00000, .end = 0x00a00000 },
 350        { .start = 0x00a0000c, .end = 0x00a00024 },
 351        { .start = 0x00a0002c, .end = 0x00a0003c },
 352        { .start = 0x00a00410, .end = 0x00a00418 },
 353        { .start = 0x00a00420, .end = 0x00a00420 },
 354        { .start = 0x00a00428, .end = 0x00a00428 },
 355        { .start = 0x00a00430, .end = 0x00a0043c },
 356        { .start = 0x00a00444, .end = 0x00a00444 },
 357        { .start = 0x00a004c0, .end = 0x00a004cc },
 358        { .start = 0x00a004d8, .end = 0x00a004d8 },
 359        { .start = 0x00a004e0, .end = 0x00a004f0 },
 360        { .start = 0x00a00840, .end = 0x00a00840 },
 361        { .start = 0x00a00850, .end = 0x00a00858 },
 362        { .start = 0x00a01004, .end = 0x00a01008 },
 363        { .start = 0x00a01010, .end = 0x00a01010 },
 364        { .start = 0x00a01018, .end = 0x00a01018 },
 365        { .start = 0x00a01024, .end = 0x00a01024 },
 366        { .start = 0x00a0102c, .end = 0x00a01034 },
 367        { .start = 0x00a0103c, .end = 0x00a01040 },
 368        { .start = 0x00a01048, .end = 0x00a01094 },
 369        { .start = 0x00a01c00, .end = 0x00a01c20 },
 370        { .start = 0x00a01c58, .end = 0x00a01c58 },
 371        { .start = 0x00a01c7c, .end = 0x00a01c7c },
 372        { .start = 0x00a01c28, .end = 0x00a01c54 },
 373        { .start = 0x00a01c5c, .end = 0x00a01c5c },
 374        { .start = 0x00a01c60, .end = 0x00a01cdc },
 375        { .start = 0x00a01ce0, .end = 0x00a01d0c },
 376        { .start = 0x00a01d18, .end = 0x00a01d20 },
 377        { .start = 0x00a01d2c, .end = 0x00a01d30 },
 378        { .start = 0x00a01d40, .end = 0x00a01d5c },
 379        { .start = 0x00a01d80, .end = 0x00a01d80 },
 380        { .start = 0x00a01d98, .end = 0x00a01d9c },
 381        { .start = 0x00a01da8, .end = 0x00a01da8 },
 382        { .start = 0x00a01db8, .end = 0x00a01df4 },
 383        { .start = 0x00a01dc0, .end = 0x00a01dfc },
 384        { .start = 0x00a01e00, .end = 0x00a01e2c },
 385        { .start = 0x00a01e40, .end = 0x00a01e60 },
 386        { .start = 0x00a01e68, .end = 0x00a01e6c },
 387        { .start = 0x00a01e74, .end = 0x00a01e74 },
 388        { .start = 0x00a01e84, .end = 0x00a01e90 },
 389        { .start = 0x00a01e9c, .end = 0x00a01ec4 },
 390        { .start = 0x00a01ed0, .end = 0x00a01ee0 },
 391        { .start = 0x00a01f00, .end = 0x00a01f1c },
 392        { .start = 0x00a01f44, .end = 0x00a01ffc },
 393        { .start = 0x00a02000, .end = 0x00a02048 },
 394        { .start = 0x00a02068, .end = 0x00a020f0 },
 395        { .start = 0x00a02100, .end = 0x00a02118 },
 396        { .start = 0x00a02140, .end = 0x00a0214c },
 397        { .start = 0x00a02168, .end = 0x00a0218c },
 398        { .start = 0x00a021c0, .end = 0x00a021c0 },
 399        { .start = 0x00a02400, .end = 0x00a02410 },
 400        { .start = 0x00a02418, .end = 0x00a02420 },
 401        { .start = 0x00a02428, .end = 0x00a0242c },
 402        { .start = 0x00a02434, .end = 0x00a02434 },
 403        { .start = 0x00a02440, .end = 0x00a02460 },
 404        { .start = 0x00a02468, .end = 0x00a024b0 },
 405        { .start = 0x00a024c8, .end = 0x00a024cc },
 406        { .start = 0x00a02500, .end = 0x00a02504 },
 407        { .start = 0x00a0250c, .end = 0x00a02510 },
 408        { .start = 0x00a02540, .end = 0x00a02554 },
 409        { .start = 0x00a02580, .end = 0x00a025f4 },
 410        { .start = 0x00a02600, .end = 0x00a0260c },
 411        { .start = 0x00a02648, .end = 0x00a02650 },
 412        { .start = 0x00a02680, .end = 0x00a02680 },
 413        { .start = 0x00a026c0, .end = 0x00a026d0 },
 414        { .start = 0x00a02700, .end = 0x00a0270c },
 415        { .start = 0x00a02804, .end = 0x00a02804 },
 416        { .start = 0x00a02818, .end = 0x00a0281c },
 417        { .start = 0x00a02c00, .end = 0x00a02db4 },
 418        { .start = 0x00a02df4, .end = 0x00a02fb0 },
 419        { .start = 0x00a03000, .end = 0x00a03014 },
 420        { .start = 0x00a0301c, .end = 0x00a0302c },
 421        { .start = 0x00a03034, .end = 0x00a03038 },
 422        { .start = 0x00a03040, .end = 0x00a03048 },
 423        { .start = 0x00a03060, .end = 0x00a03068 },
 424        { .start = 0x00a03070, .end = 0x00a03074 },
 425        { .start = 0x00a0307c, .end = 0x00a0307c },
 426        { .start = 0x00a03080, .end = 0x00a03084 },
 427        { .start = 0x00a0308c, .end = 0x00a03090 },
 428        { .start = 0x00a03098, .end = 0x00a03098 },
 429        { .start = 0x00a030a0, .end = 0x00a030a0 },
 430        { .start = 0x00a030a8, .end = 0x00a030b4 },
 431        { .start = 0x00a030bc, .end = 0x00a030bc },
 432        { .start = 0x00a030c0, .end = 0x00a0312c },
 433        { .start = 0x00a03c00, .end = 0x00a03c5c },
 434        { .start = 0x00a04400, .end = 0x00a04454 },
 435        { .start = 0x00a04460, .end = 0x00a04474 },
 436        { .start = 0x00a044c0, .end = 0x00a044ec },
 437        { .start = 0x00a04500, .end = 0x00a04504 },
 438        { .start = 0x00a04510, .end = 0x00a04538 },
 439        { .start = 0x00a04540, .end = 0x00a04548 },
 440        { .start = 0x00a04560, .end = 0x00a0457c },
 441        { .start = 0x00a04590, .end = 0x00a04598 },
 442        { .start = 0x00a045c0, .end = 0x00a045f4 },
 443};
 444
 445static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
 446        { .start = 0x00a05c00, .end = 0x00a05c18 },
 447        { .start = 0x00a05400, .end = 0x00a056e8 },
 448        { .start = 0x00a08000, .end = 0x00a098bc },
 449        { .start = 0x00a02400, .end = 0x00a02758 },
 450};
 451
 452static u32 iwl_dump_prph(struct iwl_trans *trans,
 453                         struct iwl_fw_error_dump_data **data,
 454                         const struct iwl_prph_range *iwl_prph_dump_addr,
 455                         u32 range_len)
 456{
 457        struct iwl_fw_error_dump_prph *prph;
 458        unsigned long flags;
 459        u32 prph_len = 0, i;
 460
 461        if (!iwl_trans_grab_nic_access(trans, &flags))
 462                return 0;
 463
 464        for (i = 0; i < range_len; i++) {
 465                /* The range includes both boundaries */
 466                int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
 467                         iwl_prph_dump_addr[i].start + 4;
 468                int reg;
 469                __le32 *val;
 470
 471                prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
 472
 473                (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
 474                (*data)->len = cpu_to_le32(sizeof(*prph) +
 475                                        num_bytes_in_chunk);
 476                prph = (void *)(*data)->data;
 477                prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
 478                val = (void *)prph->data;
 479
 480                for (reg = iwl_prph_dump_addr[i].start;
 481                     reg <= iwl_prph_dump_addr[i].end;
 482                     reg += 4)
 483                        *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
 484                                                                   reg));
 485
 486                *data = iwl_fw_error_next_data(*data);
 487        }
 488
 489        iwl_trans_release_nic_access(trans, &flags);
 490
 491        return prph_len;
 492}
 493
 494void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 495{
 496        struct iwl_fw_error_dump_file *dump_file;
 497        struct iwl_fw_error_dump_data *dump_data;
 498        struct iwl_fw_error_dump_info *dump_info;
 499        struct iwl_fw_error_dump_mem *dump_mem;
 500        struct iwl_fw_error_dump_trigger_desc *dump_trig;
 501        struct iwl_mvm_dump_ptrs *fw_error_dump;
 502        u32 sram_len, sram_ofs;
 503        struct iwl_fw_dbg_mem_seg_tlv * const *fw_dbg_mem =
 504                mvm->fw->dbg_mem_tlv;
 505        u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0;
 506        u32 smem_len = mvm->fw->dbg_dynamic_mem ? 0 : mvm->cfg->smem_len;
 507        u32 sram2_len = mvm->fw->dbg_dynamic_mem ? 0 : mvm->cfg->dccm2_len;
 508        bool monitor_dump_only = false;
 509        int i;
 510
 511        if (!IWL_MVM_COLLECT_FW_ERR_DUMP &&
 512            !mvm->trans->dbg_dest_tlv)
 513                return;
 514
 515        lockdep_assert_held(&mvm->mutex);
 516
 517        /* there's no point in fw dump if the bus is dead */
 518        if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
 519                IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
 520                goto out;
 521        }
 522
 523        if (mvm->fw_dump_trig &&
 524            mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
 525                monitor_dump_only = true;
 526
 527        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
 528        if (!fw_error_dump)
 529                goto out;
 530
 531        /* SRAM - include stack CCM if driver knows the values for it */
 532        if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
 533                const struct fw_img *img;
 534
 535                img = &mvm->fw->img[mvm->cur_ucode];
 536                sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
 537                sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
 538        } else {
 539                sram_ofs = mvm->cfg->dccm_offset;
 540                sram_len = mvm->cfg->dccm_len;
 541        }
 542
 543        /* reading RXF/TXF sizes */
 544        if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
 545                struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
 546
 547                fifo_data_len = 0;
 548
 549                /* Count RXF size */
 550                for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
 551                        if (!mem_cfg->rxfifo_size[i])
 552                                continue;
 553
 554                        /* Add header info */
 555                        fifo_data_len += mem_cfg->rxfifo_size[i] +
 556                                         sizeof(*dump_data) +
 557                                         sizeof(struct iwl_fw_error_dump_fifo);
 558                }
 559
 560                for (i = 0; i < mem_cfg->num_txfifo_entries; i++) {
 561                        if (!mem_cfg->txfifo_size[i])
 562                                continue;
 563
 564                        /* Add header info */
 565                        fifo_data_len += mem_cfg->txfifo_size[i] +
 566                                         sizeof(*dump_data) +
 567                                         sizeof(struct iwl_fw_error_dump_fifo);
 568                }
 569
 570                if (fw_has_capa(&mvm->fw->ucode_capa,
 571                                IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
 572                        for (i = 0;
 573                             i < ARRAY_SIZE(mem_cfg->internal_txfifo_size);
 574                             i++) {
 575                                if (!mem_cfg->internal_txfifo_size[i])
 576                                        continue;
 577
 578                                /* Add header info */
 579                                fifo_data_len +=
 580                                        mem_cfg->internal_txfifo_size[i] +
 581                                        sizeof(*dump_data) +
 582                                        sizeof(struct iwl_fw_error_dump_fifo);
 583                        }
 584                }
 585
 586                /* Make room for PRPH registers */
 587                for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm); i++) {
 588                        /* The range includes both boundaries */
 589                        int num_bytes_in_chunk =
 590                                iwl_prph_dump_addr_comm[i].end -
 591                                iwl_prph_dump_addr_comm[i].start + 4;
 592
 593                        prph_len += sizeof(*dump_data) +
 594                                sizeof(struct iwl_fw_error_dump_prph) +
 595                                num_bytes_in_chunk;
 596                }
 597
 598                if (mvm->cfg->mq_rx_supported) {
 599                        for (i = 0; i <
 600                                ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) {
 601                                /* The range includes both boundaries */
 602                                int num_bytes_in_chunk =
 603                                        iwl_prph_dump_addr_9000[i].end -
 604                                        iwl_prph_dump_addr_9000[i].start + 4;
 605
 606                                prph_len += sizeof(*dump_data) +
 607                                        sizeof(struct iwl_fw_error_dump_prph) +
 608                                        num_bytes_in_chunk;
 609                        }
 610                }
 611
 612                if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
 613                        radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
 614        }
 615
 616        file_len = sizeof(*dump_file) +
 617                   sizeof(*dump_data) * 2 +
 618                   fifo_data_len +
 619                   prph_len +
 620                   radio_len +
 621                   sizeof(*dump_info);
 622
 623        /* Make room for the SMEM, if it exists */
 624        if (smem_len)
 625                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
 626
 627        /* Make room for the secondary SRAM, if it exists */
 628        if (sram2_len)
 629                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
 630
 631        /* Make room for MEM segments */
 632        for (i = 0; i < ARRAY_SIZE(mvm->fw->dbg_mem_tlv); i++) {
 633                if (fw_dbg_mem[i])
 634                        file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
 635                                le32_to_cpu(fw_dbg_mem[i]->len);
 636        }
 637
 638        /* Make room for fw's virtual image pages, if it exists */
 639        if (mvm->fw->img[mvm->cur_ucode].paging_mem_size &&
 640            mvm->fw_paging_db[0].fw_paging_block)
 641                file_len += mvm->num_of_paging_blk *
 642                        (sizeof(*dump_data) +
 643                         sizeof(struct iwl_fw_error_dump_paging) +
 644                         PAGING_BLOCK_SIZE);
 645
 646        /* If we only want a monitor dump, reset the file length */
 647        if (monitor_dump_only) {
 648                file_len = sizeof(*dump_file) + sizeof(*dump_data) +
 649                           sizeof(*dump_info);
 650        }
 651
 652        /*
 653         * In 8000 HW family B-step include the ICCM (which resides separately)
 654         */
 655        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
 656            CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
 657                file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
 658                            IWL8260_ICCM_LEN;
 659
 660        if (mvm->fw_dump_desc)
 661                file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
 662                            mvm->fw_dump_desc->len;
 663
 664        if (!mvm->fw->dbg_dynamic_mem)
 665                file_len += sram_len + sizeof(*dump_mem);
 666
 667        dump_file = vzalloc(file_len);
 668        if (!dump_file) {
 669                kfree(fw_error_dump);
 670                goto out;
 671        }
 672
 673        fw_error_dump->op_mode_ptr = dump_file;
 674
 675        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
 676        dump_data = (void *)dump_file->data;
 677
 678        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
 679        dump_data->len = cpu_to_le32(sizeof(*dump_info));
 680        dump_info = (void *)dump_data->data;
 681        dump_info->device_family =
 682                mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
 683                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
 684                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
 685        dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
 686        memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
 687               sizeof(dump_info->fw_human_readable));
 688        strncpy(dump_info->dev_human_readable, mvm->cfg->name,
 689                sizeof(dump_info->dev_human_readable));
 690        strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
 691                sizeof(dump_info->bus_human_readable));
 692
 693        dump_data = iwl_fw_error_next_data(dump_data);
 694        /* We only dump the FIFOs if the FW is in error state */
 695        if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
 696                iwl_mvm_dump_fifos(mvm, &dump_data);
 697                if (radio_len)
 698                        iwl_mvm_read_radio_reg(mvm, &dump_data);
 699        }
 700
 701        if (mvm->fw_dump_desc) {
 702                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
 703                dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
 704                                             mvm->fw_dump_desc->len);
 705                dump_trig = (void *)dump_data->data;
 706                memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
 707                       sizeof(*dump_trig) + mvm->fw_dump_desc->len);
 708
 709                dump_data = iwl_fw_error_next_data(dump_data);
 710        }
 711
 712        /* In case we only want monitor dump, skip to dump trasport data */
 713        if (monitor_dump_only)
 714                goto dump_trans_data;
 715
 716        if (!mvm->fw->dbg_dynamic_mem) {
 717                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 718                dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
 719                dump_mem = (void *)dump_data->data;
 720                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 721                dump_mem->offset = cpu_to_le32(sram_ofs);
 722                iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
 723                                         sram_len);
 724                dump_data = iwl_fw_error_next_data(dump_data);
 725        }
 726
 727        for (i = 0; i < ARRAY_SIZE(mvm->fw->dbg_mem_tlv); i++) {
 728                if (fw_dbg_mem[i]) {
 729                        u32 len = le32_to_cpu(fw_dbg_mem[i]->len);
 730                        u32 ofs = le32_to_cpu(fw_dbg_mem[i]->ofs);
 731
 732                        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 733                        dump_data->len = cpu_to_le32(len +
 734                                        sizeof(*dump_mem));
 735                        dump_mem = (void *)dump_data->data;
 736                        dump_mem->type = fw_dbg_mem[i]->data_type;
 737                        dump_mem->offset = cpu_to_le32(ofs);
 738                        iwl_trans_read_mem_bytes(mvm->trans, ofs,
 739                                                 dump_mem->data,
 740                                                 len);
 741                        dump_data = iwl_fw_error_next_data(dump_data);
 742                }
 743        }
 744
 745        if (smem_len) {
 746                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 747                dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
 748                dump_mem = (void *)dump_data->data;
 749                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
 750                dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
 751                iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
 752                                         dump_mem->data, smem_len);
 753                dump_data = iwl_fw_error_next_data(dump_data);
 754        }
 755
 756        if (sram2_len) {
 757                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 758                dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
 759                dump_mem = (void *)dump_data->data;
 760                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 761                dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
 762                iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
 763                                         dump_mem->data, sram2_len);
 764                dump_data = iwl_fw_error_next_data(dump_data);
 765        }
 766
 767        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
 768            CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
 769                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 770                dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
 771                                             sizeof(*dump_mem));
 772                dump_mem = (void *)dump_data->data;
 773                dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 774                dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
 775                iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
 776                                         dump_mem->data, IWL8260_ICCM_LEN);
 777                dump_data = iwl_fw_error_next_data(dump_data);
 778        }
 779
 780        /* Dump fw's virtual image */
 781        if (mvm->fw->img[mvm->cur_ucode].paging_mem_size &&
 782            mvm->fw_paging_db[0].fw_paging_block) {
 783                for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
 784                        struct iwl_fw_error_dump_paging *paging;
 785                        struct page *pages =
 786                                mvm->fw_paging_db[i].fw_paging_block;
 787
 788                        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
 789                        dump_data->len = cpu_to_le32(sizeof(*paging) +
 790                                                     PAGING_BLOCK_SIZE);
 791                        paging = (void *)dump_data->data;
 792                        paging->index = cpu_to_le32(i);
 793                        memcpy(paging->data, page_address(pages),
 794                               PAGING_BLOCK_SIZE);
 795                        dump_data = iwl_fw_error_next_data(dump_data);
 796                }
 797        }
 798
 799        if (prph_len) {
 800                iwl_dump_prph(mvm->trans, &dump_data,
 801                              iwl_prph_dump_addr_comm,
 802                              ARRAY_SIZE(iwl_prph_dump_addr_comm));
 803
 804                if (mvm->cfg->mq_rx_supported)
 805                        iwl_dump_prph(mvm->trans, &dump_data,
 806                                      iwl_prph_dump_addr_9000,
 807                                      ARRAY_SIZE(iwl_prph_dump_addr_9000));
 808        }
 809
 810dump_trans_data:
 811        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
 812                                                       mvm->fw_dump_trig);
 813        fw_error_dump->op_mode_len = file_len;
 814        if (fw_error_dump->trans_ptr)
 815                file_len += fw_error_dump->trans_ptr->len;
 816        dump_file->file_len = cpu_to_le32(file_len);
 817
 818        dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
 819                      GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
 820
 821out:
 822        iwl_mvm_free_fw_dump_desc(mvm);
 823        mvm->fw_dump_trig = NULL;
 824        clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
 825}
 826
 827const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
 828        .trig_desc = {
 829                .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
 830        },
 831};
 832
 833int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
 834                                const struct iwl_mvm_dump_desc *desc,
 835                                const struct iwl_fw_dbg_trigger_tlv *trigger)
 836{
 837        unsigned int delay = 0;
 838
 839        if (trigger)
 840                delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
 841
 842        if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
 843                return -EBUSY;
 844
 845        if (WARN_ON(mvm->fw_dump_desc))
 846                iwl_mvm_free_fw_dump_desc(mvm);
 847
 848        IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
 849                 le32_to_cpu(desc->trig_desc.type));
 850
 851        mvm->fw_dump_desc = desc;
 852        mvm->fw_dump_trig = trigger;
 853
 854        queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
 855
 856        return 0;
 857}
 858
 859int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
 860                           const char *str, size_t len,
 861                           const struct iwl_fw_dbg_trigger_tlv *trigger)
 862{
 863        struct iwl_mvm_dump_desc *desc;
 864
 865        desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
 866        if (!desc)
 867                return -ENOMEM;
 868
 869        desc->len = len;
 870        desc->trig_desc.type = cpu_to_le32(trig);
 871        memcpy(desc->trig_desc.data, str, len);
 872
 873        return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
 874}
 875
 876int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
 877                                struct iwl_fw_dbg_trigger_tlv *trigger,
 878                                const char *fmt, ...)
 879{
 880        u16 occurrences = le16_to_cpu(trigger->occurrences);
 881        int ret, len = 0;
 882        char buf[64];
 883
 884        if (!occurrences)
 885                return 0;
 886
 887        if (fmt) {
 888                va_list ap;
 889
 890                buf[sizeof(buf) - 1] = '\0';
 891
 892                va_start(ap, fmt);
 893                vsnprintf(buf, sizeof(buf), fmt, ap);
 894                va_end(ap);
 895
 896                /* check for truncation */
 897                if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
 898                        buf[sizeof(buf) - 1] = '\0';
 899
 900                len = strlen(buf) + 1;
 901        }
 902
 903        ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
 904                                     trigger);
 905
 906        if (ret)
 907                return ret;
 908
 909        trigger->occurrences = cpu_to_le16(occurrences - 1);
 910        return 0;
 911}
 912
 913static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
 914{
 915        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
 916                iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
 917        else
 918                iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
 919}
 920
 921int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
 922{
 923        u8 *ptr;
 924        int ret;
 925        int i;
 926
 927        if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
 928                      "Invalid configuration %d\n", conf_id))
 929                return -EINVAL;
 930
 931        /* EARLY START - firmware's configuration is hard coded */
 932        if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
 933             !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
 934            conf_id == FW_DBG_START_FROM_ALIVE) {
 935                iwl_mvm_restart_early_start(mvm);
 936                return 0;
 937        }
 938
 939        if (!mvm->fw->dbg_conf_tlv[conf_id])
 940                return -EINVAL;
 941
 942        if (mvm->fw_dbg_conf != FW_DBG_INVALID)
 943                IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
 944                         mvm->fw_dbg_conf);
 945
 946        /* Send all HCMDs for configuring the FW debug */
 947        ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
 948        for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
 949                struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
 950
 951                ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
 952                                           le16_to_cpu(cmd->len), cmd->data);
 953                if (ret)
 954                        return ret;
 955
 956                ptr += sizeof(*cmd);
 957                ptr += le16_to_cpu(cmd->len);
 958        }
 959
 960        mvm->fw_dbg_conf = conf_id;
 961
 962        return 0;
 963}
 964