Commit 559afaa2 authored by Kalle Valo's avatar Kalle Valo

Merge tag 'iwlwifi-next-for-kalle-2018-11-23' of...

Merge tag 'iwlwifi-next-for-kalle-2018-11-23' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next

second batch of iwlwifi patches intended for v4.21

* New FW debugging infrastructure;
* Some more work on 802.11ax;
* Improve support for multiple RF modules with 22000 devices;
* Remove an unused FW parameter;
* Other debugging improvements;
parents 12d56175 520229e4
...@@ -11,6 +11,7 @@ iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-gen3.o ...@@ -11,6 +11,7 @@ iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-gen3.o
iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o
iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
iwlwifi-objs += iwl-dbg-tlv.o
iwlwifi-objs += iwl-trans.o iwlwifi-objs += iwl-trans.o
iwlwifi-objs += fw/notif-wait.o iwlwifi-objs += fw/notif-wait.o
iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o
......
...@@ -323,7 +323,6 @@ MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); ...@@ -323,7 +323,6 @@ MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
......
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __iwl_fw_dbg_tlv_h__
#define __iwl_fw_dbg_tlv_h__
#include <linux/bitops.h>
/*
* struct iwl_fw_ini_header: Common Header for all debug group TLV's structures
* @tlv_version: version info
* @apply_point: &enum iwl_fw_ini_apply_point
* @data: TLV data followed
**/
struct iwl_fw_ini_header {
__le32 tlv_version;
__le32 apply_point;
u8 data[];
} __packed; /* FW_INI_HEADER_TLV_S */
/**
* struct iwl_fw_ini_allocation_tlv - (IWL_FW_INI_TLV_TYPE_BUFFER_ALLOCATION)
* buffer allocation TLV - for debug
*
* @iwl_fw_ini_header: header
* @allocation_id: &enum iwl_fw_ini_allocation_id - to bind allocation and hcmd
* if needed (DBGC1/DBGC2/SDFX/...)
* @buffer_location: type of iwl_fw_ini_buffer_location
* @size: size in bytes
* @max_fragments: the maximum allowed fragmentation in the desired memory
* allocation above
* @min_frag_size: the minimum allowed fragmentation size in bytes
*/
struct iwl_fw_ini_allocation_tlv {
struct iwl_fw_ini_header header;
__le32 allocation_id;
__le32 buffer_location;
__le32 size;
__le32 max_fragments;
__le32 min_frag_size;
} __packed; /* FW_INI_BUFFER_ALLOCATION_TLV_S_VER_1 */
/**
* struct iwl_fw_ini_hcmd (IWL_FW_INI_TLV_TYPE_HCMD)
* Generic Host command pass through TLV
*
* @id: the debug configuration command type for instance: 0xf6 / 0xf5 / DHC
* @group: the desired cmd group
* @padding: all zeros for dword alignment
* @data: all of the relevant command (0xf6/0xf5) to be sent
*/
struct iwl_fw_ini_hcmd {
u8 id;
u8 group;
__le16 padding;
u8 data[0];
} __packed; /* FW_INI_HCMD_S */
/**
* struct iwl_fw_ini_hcmd_tlv
* @header: header
* @hcmd: a variable length host-command to be sent to apply the configuration.
*/
struct iwl_fw_ini_hcmd_tlv {
struct iwl_fw_ini_header header;
struct iwl_fw_ini_hcmd hcmd;
} __packed; /* FW_INI_HCMD_TLV_S_VER_1 */
/*
* struct iwl_fw_ini_debug_flow_tlv (IWL_FW_INI_TLV_TYPE_DEBUG_FLOW)
*
* @header: header
* @debug_flow_cfg: &enum iwl_fw_ini_debug_flow
*/
struct iwl_fw_ini_debug_flow_tlv {
struct iwl_fw_ini_header header;
__le32 debug_flow_cfg;
} __packed; /* FW_INI_DEBUG_FLOW_TLV_S_VER_1 */
#define IWL_FW_INI_MAX_REGION_ID 20
#define IWL_FW_INI_MAX_NAME 32
/**
* struct iwl_fw_ini_region_cfg
* @region_id: ID of this dump configuration
* @region_type: &enum iwl_fw_ini_region_type
* @num_regions: amount of regions in the address array.
* @allocation_id: For DRAM type field substitutes for allocation_id.
* @name_len: name length
* @name: file name to use for this region
* @size: size of the data, in bytes.(unused for IWL_FW_INI_REGION_DRAM_BUFFER)
* @start_addr: array of addresses. (unused for IWL_FW_INI_REGION_DRAM_BUFFER)
*/
struct iwl_fw_ini_region_cfg {
__le32 region_id;
__le32 region_type;
__le32 name_len;
u8 name[IWL_FW_INI_MAX_NAME];
union {
__le32 num_regions;
__le32 allocation_id;
};
__le32 size;
__le32 start_addr[];
} __packed; /* FW_INI_REGION_CONFIG_S */
/**
* struct iwl_fw_ini_region_tlv - (IWL_FW_INI_TLV_TYPE_REGION_CFG)
* DUMP sections define IDs and triggers that use those IDs TLV
* @header: header
* @num_regions: how many different region section and IDs are coming next
* @iwl_fw_ini_dump dump_config: list of dump configurations
*/
struct iwl_fw_ini_region_tlv {
struct iwl_fw_ini_header header;
__le32 num_regions;
struct iwl_fw_ini_region_cfg region_config[];
} __packed; /* FW_INI_REGION_CFG_S */
/**
* struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG)
* Region sections define IDs and triggers that use those IDs TLV
*
* @trigger_id: enum &iwl_fw_ini_tigger_id
* @ignore_default: override FW TLV with binary TLV
* @dump_delay: delay from trigger fire to dump, in usec
* @occurrences: max amount of times to be fired
* @ignore_consec: ignore consecutive triggers, in usec
* @force_restart: force FW restart
* @multi_dut: initiate debug dump data on several DUTs
* @trigger_data: generic data to be utilized per trigger
* @num_regions: number of dump regions defined for this trigger
* @data: region IDs
*/
struct iwl_fw_ini_trigger {
__le32 trigger_id;
__le32 ignore_default;
__le32 dump_delay;
__le32 occurrences;
__le32 ignore_consec;
__le32 force_restart;
__le32 multi_dut;
__le32 trigger_data;
__le32 num_regions;
__le32 data[];
} __packed; /* FW_INI_TRIGGER_CONFIG_S */
/**
* struct iwl_fw_ini_trigger_tlv - (IWL_FW_INI_TLV_TYPE_TRIGGERS_CFG)
* DUMP sections define IDs and triggers that use those IDs TLV
*
* @header: header
* @num_triggers: how many different triggers section and IDs are coming next
* @trigger_config: list of trigger configurations
*/
struct iwl_fw_ini_trigger_tlv {
struct iwl_fw_ini_header header;
__le32 num_triggers;
struct iwl_fw_ini_trigger trigger_config[];
} __packed; /* FW_INI_TRIGGER_CFG_S */
/**
* enum iwl_fw_ini_trigger_id
* @IWL_FW_TRIGGER_ID_FW_ASSERT: FW assert
* @IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG: TFD queue hang
* @IWL_FW_TRIGGER_ID_FW_HW_ERROR: HW assert
* @IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR: FW error notification
* @IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING: FW warning notification
* @IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO: FW info notification
* @IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG: FW debug notification
* @IWL_FW_TRIGGER_ID_USER_TRIGGER: User trigger
* @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY: peer inactivity
* @FW_DEBUG_TLV_TRIGGER_ID_HOST_DID_INITIATED_EVENT: undefined
* @IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED: TX latency
* threshold was crossed
* @IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED: TX failed
* @IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER: Deauth initiated by host
* @IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST: stop GO request
* @IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST: start GO request
* @IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST: join P2P group request
* @IWL_FW_TRIGGER_ID_HOST_SCAN_START: scan started event
* @IWL_FW_TRIGGER_ID_HOST_SCAN_SUBMITTED: undefined
* @IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS: undefined
* @IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG: undefined
* @IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED: BAR frame was received
* @IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED: agg TX failed
* @IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED: EAPOL TX failed
* @IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED: suspicious TX response
* @IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT: received suspicious auth
* @IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE: roaming was completed
* @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED: fast assoc failed
* @IWL_FW_TRIGGER_ID_HOST_D3_START: D3 start
* @IWL_FW_TRIGGER_ID_HOST_D3_END: D3 end
* @IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS: missed beacon events
* @IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS: P2P missed beacon events
* @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES: undefined
* @IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED: undefined
* @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED: authentication / association
* failed
* @IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE: scan complete event
* @IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT: scan abort complete
* @IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE: nic alive message was received
* @IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE: CSA was completed
* @IWL_FW_TRIGGER_ID_NUM: number of trigger IDs
*/
enum iwl_fw_ini_trigger_id {
/* Errors triggers */
IWL_FW_TRIGGER_ID_FW_ASSERT = 1,
IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG = 2,
IWL_FW_TRIGGER_ID_FW_HW_ERROR = 3,
/* Generic triggers */
IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR = 4,
IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING = 5,
IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO = 6,
IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG = 7,
/* User Trigger */
IWL_FW_TRIGGER_ID_USER_TRIGGER = 8,
/* Host triggers */
IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY = 9,
IWL_FW_TRIGGER_ID_HOST_DID_INITIATED_EVENT = 10,
IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED = 11,
IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED = 12,
IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER = 13,
IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST = 14,
IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST = 15,
IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST = 16,
IWL_FW_TRIGGER_ID_HOST_SCAN_START = 17,
IWL_FW_TRIGGER_ID_HOST_SCAN_SUBITTED = 18,
IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS = 19,
IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG = 20,
IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED = 21,
IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED = 22,
IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED = 23,
IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED = 24,
IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT = 25,
IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE = 26,
IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED = 27,
IWL_FW_TRIGGER_ID_HOST_D3_START = 28,
IWL_FW_TRIGGER_ID_HOST_D3_END = 29,
IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS = 30,
IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS = 31,
IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES = 32,
IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED = 33,
IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED = 34,
IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE = 35,
IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT = 36,
IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE = 37,
IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE = 38,
IWL_FW_TRIGGER_ID_NUM,
}; /* FW_INI_TRIGGER_ID_E_VER_1 */
/**
* enum iwl_fw_ini_apply_point
* @IWL_FW_INI_APPLY_INVALID: invalid
* @IWL_FW_INI_APPLY_EARLY: pre loading FW
* @IWL_FW_INI_APPLY_AFTER_ALIVE: first cmd from host after alive
* @IWL_FW_INI_APPLY_POST_INIT: last cmd in initialization sequence
* @IWL_FW_INI_APPLY_MISSED_BEACONS: missed beacons notification
* @IWL_FW_INI_APPLY_SCAN_COMPLETE: scan completed
* @IWL_FW_INI_APPLY_NUM: number of apply points
*/
enum iwl_fw_ini_apply_point {
IWL_FW_INI_APPLY_INVALID,
IWL_FW_INI_APPLY_EARLY,
IWL_FW_INI_APPLY_AFTER_ALIVE,
IWL_FW_INI_APPLY_POST_INIT,
IWL_FW_INI_APPLY_MISSED_BEACONS,
IWL_FW_INI_APPLY_SCAN_COMPLETE,
IWL_FW_INI_APPLY_NUM,
}; /* FW_INI_APPLY_POINT_E_VER_1 */
/**
* enum iwl_fw_ini_allocation_id
* @IWL_FW_INI_ALLOCATION_INVALID: invalid
* @IWL_FW_INI_ALLOCATION_ID_DBGC1: allocation meant for DBGC1 configuration
* @IWL_FW_INI_ALLOCATION_ID_DBGC2: allocation meant for DBGC2 configuration
* @IWL_FW_INI_ALLOCATION_ID_DBGC3: allocation meant for DBGC3 configuration
* @IWL_FW_INI_ALLOCATION_ID_SDFX: for SDFX module
* @IWL_FW_INI_ALLOCATION_ID_FW_DUMP: used for crash and runtime dumps
* @IWL_FW_INI_ALLOCATION_ID_USER_DEFINED: for future user scenarios
*/
enum iwl_fw_ini_allocation_id {
IWL_FW_INI_ALLOCATION_INVALID,
IWL_FW_INI_ALLOCATION_ID_DBGC1,
IWL_FW_INI_ALLOCATION_ID_DBGC2,
IWL_FW_INI_ALLOCATION_ID_DBGC3,
IWL_FW_INI_ALLOCATION_ID_SDFX,
IWL_FW_INI_ALLOCATION_ID_FW_DUMP,
IWL_FW_INI_ALLOCATION_ID_USER_DEFINED,
}; /* FW_INI_ALLOCATION_ID_E_VER_1 */
/**
* enum iwl_fw_ini_buffer_location
* @IWL_FW_INI_LOCATION_INVALID: invalid
* @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location
* @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location
*/
enum iwl_fw_ini_buffer_location {
IWL_FW_INI_LOCATION_SRAM_INVALID,
IWL_FW_INI_LOCATION_SRAM_PATH,
IWL_FW_INI_LOCATION_DRAM_PATH,
}; /* FW_INI_BUFFER_LOCATION_E_VER_1 */
/**
* enum iwl_fw_ini_debug_flow
* @IWL_FW_INI_DEBUG_INVALID: invalid
* @IWL_FW_INI_DEBUG_DBTR_FLOW: undefined
* @IWL_FW_INI_DEBUG_TB2DTF_FLOW: undefined
*/
enum iwl_fw_ini_debug_flow {
IWL_FW_INI_DEBUG_INVALID,
IWL_FW_INI_DEBUG_DBTR_FLOW,
IWL_FW_INI_DEBUG_TB2DTF_FLOW,
}; /* FW_INI_DEBUG_FLOW_E_VER_1 */
/**
* enum iwl_fw_ini_region_type
* @IWL_FW_INI_REGION_INVALID: invalid
* @IWL_FW_INI_REGION_DEVICE_MEMORY: device internal memory
* @IWL_FW_INI_REGION_PERIPHERY_MAC: periphery registers of MAC
* @IWL_FW_INI_REGION_PERIPHERY_PHY: periphery registers of PHY
* @IWL_FW_INI_REGION_PERIPHERY_AUX: periphery registers of AUX
* @IWL_FW_INI_REGION_DRAM_BUFFER: DRAM buffer
* @IWL_FW_INI_REGION_DRAM_IMR: IMR memory
* @IWL_FW_INI_REGION_INTERNAL_BUFFER: undefined
* @IWL_FW_INI_REGION_TXF: TX fifos
* @IWL_FW_INI_REGION_RXF: RX fifo
* @IWL_FW_INI_REGION_PAGING: paging memory
* @IWL_FW_INI_REGION_CSR: CSR registers
* @IWL_FW_INI_REGION_NUM: number of region types
*/
enum iwl_fw_ini_region_type {
IWL_FW_INI_REGION_INVALID,
IWL_FW_INI_REGION_DEVICE_MEMORY,
IWL_FW_INI_REGION_PERIPHERY_MAC,
IWL_FW_INI_REGION_PERIPHERY_PHY,
IWL_FW_INI_REGION_PERIPHERY_AUX,
IWL_FW_INI_REGION_DRAM_BUFFER,
IWL_FW_INI_REGION_DRAM_IMR,
IWL_FW_INI_REGION_INTERNAL_BUFFER,
IWL_FW_INI_REGION_TXF,
IWL_FW_INI_REGION_RXF,
IWL_FW_INI_REGION_PAGING,
IWL_FW_INI_REGION_CSR,
IWL_FW_INI_REGION_NUM
}; /* FW_INI_REGION_TYPE_E_VER_1*/
#endif
...@@ -151,9 +151,9 @@ enum iwl_tsf_id { ...@@ -151,9 +151,9 @@ enum iwl_tsf_id {
* @beacon_time: beacon transmit time in system time * @beacon_time: beacon transmit time in system time
* @beacon_tsf: beacon transmit time in TSF * @beacon_tsf: beacon transmit time in TSF
* @bi: beacon interval in TU * @bi: beacon interval in TU
* @bi_reciprocal: 2^32 / bi * @reserved1: reserved
* @dtim_interval: dtim transmit time in TU * @dtim_interval: dtim transmit time in TU
* @dtim_reciprocal: 2^32 / dtim_interval * @reserved2: reserved
* @mcast_qid: queue ID for multicast traffic. * @mcast_qid: queue ID for multicast traffic.
* NOTE: obsolete from VER2 and on * NOTE: obsolete from VER2 and on
* @beacon_template: beacon template ID * @beacon_template: beacon template ID
...@@ -162,9 +162,9 @@ struct iwl_mac_data_ap { ...@@ -162,9 +162,9 @@ struct iwl_mac_data_ap {
__le32 beacon_time; __le32 beacon_time;
__le64 beacon_tsf; __le64 beacon_tsf;
__le32 bi; __le32 bi;
__le32 bi_reciprocal; __le32 reserved1;
__le32 dtim_interval; __le32 dtim_interval;
__le32 dtim_reciprocal; __le32 reserved2;
__le32 mcast_qid; __le32 mcast_qid;
__le32 beacon_template; __le32 beacon_template;
} __packed; /* AP_MAC_DATA_API_S_VER_2 */ } __packed; /* AP_MAC_DATA_API_S_VER_2 */
...@@ -174,26 +174,34 @@ struct iwl_mac_data_ap { ...@@ -174,26 +174,34 @@ struct iwl_mac_data_ap {
* @beacon_time: beacon transmit time in system time * @beacon_time: beacon transmit time in system time
* @beacon_tsf: beacon transmit time in TSF * @beacon_tsf: beacon transmit time in TSF
* @bi: beacon interval in TU * @bi: beacon interval in TU
* @bi_reciprocal: 2^32 / bi * @reserved: reserved
* @beacon_template: beacon template ID * @beacon_template: beacon template ID
*/ */
struct iwl_mac_data_ibss { struct iwl_mac_data_ibss {
__le32 beacon_time; __le32 beacon_time;
__le64 beacon_tsf; __le64 beacon_tsf;
__le32 bi; __le32 bi;
__le32 bi_reciprocal; __le32 reserved;
__le32 beacon_template; __le32 beacon_template;
} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
/**
* enum iwl_mac_data_policy - policy of the data path for this MAC
* @TWT_SUPPORTED: twt is supported
*/
enum iwl_mac_data_policy {
TWT_SUPPORTED = BIT(0),
};
/** /**
* struct iwl_mac_data_sta - configuration data for station MAC context * struct iwl_mac_data_sta - configuration data for station MAC context
* @is_assoc: 1 for associated state, 0 otherwise * @is_assoc: 1 for associated state, 0 otherwise
* @dtim_time: DTIM arrival time in system time * @dtim_time: DTIM arrival time in system time
* @dtim_tsf: DTIM arrival time in TSF * @dtim_tsf: DTIM arrival time in TSF
* @bi: beacon interval in TU, applicable only when associated * @bi: beacon interval in TU, applicable only when associated
* @bi_reciprocal: 2^32 / bi , applicable only when associated * @reserved1: reserved
* @dtim_interval: DTIM interval in TU, applicable only when associated * @dtim_interval: DTIM interval in TU, applicable only when associated
* @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated * @data_policy: see &enum iwl_mac_data_policy
* @listen_interval: in beacon intervals, applicable only when associated * @listen_interval: in beacon intervals, applicable only when associated
* @assoc_id: unique ID assigned by the AP during association * @assoc_id: unique ID assigned by the AP during association
* @assoc_beacon_arrive_time: TSF of first beacon after association * @assoc_beacon_arrive_time: TSF of first beacon after association
...@@ -203,13 +211,13 @@ struct iwl_mac_data_sta { ...@@ -203,13 +211,13 @@ struct iwl_mac_data_sta {
__le32 dtim_time; __le32 dtim_time;
__le64 dtim_tsf; __le64 dtim_tsf;
__le32 bi; __le32 bi;
__le32 bi_reciprocal; __le32 reserved1;
__le32 dtim_interval; __le32 dtim_interval;
__le32 dtim_reciprocal; __le32 data_policy;
__le32 listen_interval; __le32 listen_interval;
__le32 assoc_id; __le32 assoc_id;
__le32 assoc_beacon_arrive_time; __le32 assoc_beacon_arrive_time;
} __packed; /* STA_MAC_DATA_API_S_VER_1 */ } __packed; /* STA_MAC_DATA_API_S_VER_2 */
/** /**
* struct iwl_mac_data_go - configuration data for P2P GO MAC context * struct iwl_mac_data_go - configuration data for P2P GO MAC context
...@@ -233,7 +241,7 @@ struct iwl_mac_data_go { ...@@ -233,7 +241,7 @@ struct iwl_mac_data_go {
struct iwl_mac_data_p2p_sta { struct iwl_mac_data_p2p_sta {
struct iwl_mac_data_sta sta; struct iwl_mac_data_sta sta;
__le32 ctwin; __le32 ctwin;
} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ } __packed; /* P2P_STA_MAC_DATA_API_S_VER_2 */
/** /**
* struct iwl_mac_data_pibss - Pseudo IBSS config data * struct iwl_mac_data_pibss - Pseudo IBSS config data
...@@ -378,13 +386,6 @@ struct iwl_mac_ctx_cmd { ...@@ -378,13 +386,6 @@ struct iwl_mac_ctx_cmd {
}; };
} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ } __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
static inline u32 iwl_mvm_reciprocal(u32 v)
{
if (!v)
return 0;
return 0xFFFFFFFF / v;
}
#define IWL_NONQOS_SEQ_GET 0x1 #define IWL_NONQOS_SEQ_GET 0x1
#define IWL_NONQOS_SEQ_SET 0x2 #define IWL_NONQOS_SEQ_SET 0x2
struct iwl_nonqos_seq_query_cmd { struct iwl_nonqos_seq_query_cmd {
......
...@@ -225,17 +225,13 @@ static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt, ...@@ -225,17 +225,13 @@ static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt,
*dump_data = iwl_fw_error_next_data(*dump_data); *dump_data = iwl_fw_error_next_data(*dump_data);
} }
static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt, static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data) struct iwl_fw_error_dump_data **dump_data)
{ {
struct iwl_fw_error_dump_fifo *fifo_hdr;
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
u32 *fifo_data;
u32 fifo_len;
unsigned long flags; unsigned long flags;
int i, j;
IWL_DEBUG_INFO(fwrt, "WRT FIFO dump\n"); IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n");
if (!iwl_trans_grab_nic_access(fwrt->trans, &flags)) if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
return; return;
...@@ -254,6 +250,24 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt, ...@@ -254,6 +250,24 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
LMAC2_PRPH_OFFSET, 2); LMAC2_PRPH_OFFSET, 2);
} }
iwl_trans_release_nic_access(fwrt->trans, &flags);
}
static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data)
{
struct iwl_fw_error_dump_fifo *fifo_hdr;
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
u32 *fifo_data;
u32 fifo_len;
unsigned long flags;
int i, j;
IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n");
if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
return;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) { if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) {
/* Pull TXF data from LMAC1 */ /* Pull TXF data from LMAC1 */
for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) {
...@@ -595,8 +609,8 @@ static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, ...@@ -595,8 +609,8 @@ static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
do {size_t item = item_len; len += (!!item) * const_len + item; } \ do {size_t item = item_len; len += (!!item) * const_len + item; } \
while (0) while (0)
static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt, static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
struct iwl_fwrt_shared_mem_cfg *mem_cfg) struct iwl_fwrt_shared_mem_cfg *mem_cfg)
{ {
size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
sizeof(struct iwl_fw_error_dump_fifo); sizeof(struct iwl_fw_error_dump_fifo);
...@@ -604,7 +618,7 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt, ...@@ -604,7 +618,7 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
int i; int i;
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF))
goto dump_txf; return 0;
/* Count RXF2 size */ /* Count RXF2 size */
ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len); ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
...@@ -613,7 +627,17 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt, ...@@ -613,7 +627,17 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
for (i = 0; i < mem_cfg->num_lmacs; i++) for (i = 0; i < mem_cfg->num_lmacs; i++)
ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len); ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
dump_txf: return fifo_len;
}
static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
struct iwl_fwrt_shared_mem_cfg *mem_cfg)
{
size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
sizeof(struct iwl_fw_error_dump_fifo);
u32 fifo_len = 0;
int i;
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF))
goto dump_internal_txf; goto dump_internal_txf;
...@@ -697,7 +721,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, ...@@ -697,7 +721,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
/* reading RXF/TXF sizes */ /* reading RXF/TXF sizes */
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
fifo_len = iwl_fw_fifo_len(fwrt, mem_cfg); fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
/* Make room for PRPH registers */ /* Make room for PRPH registers */
if (!fwrt->trans->cfg->gen2 && if (!fwrt->trans->cfg->gen2 &&
...@@ -817,7 +842,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, ...@@ -817,7 +842,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
/* We only dump the FIFOs if the FW is in error state */ /* We only dump the FIFOs if the FW is in error state */
if (fifo_len) { if (fifo_len) {
iwl_fw_dump_fifos(fwrt, &dump_data); iwl_fw_dump_rxf(fwrt, &dump_data);
iwl_fw_dump_txf(fwrt, &dump_data);
if (radio_len) if (radio_len)
iwl_read_radio_regs(fwrt, &dump_data); iwl_read_radio_regs(fwrt, &dump_data);
} }
...@@ -908,6 +934,7 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) ...@@ -908,6 +934,7 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_file *dump_file;
struct scatterlist *sg_dump_data; struct scatterlist *sg_dump_data;
u32 file_len; u32 file_len;
u32 dump_mask = fwrt->fw->dbg.dump_mask;
IWL_DEBUG_INFO(fwrt, "WRT dump start\n"); IWL_DEBUG_INFO(fwrt, "WRT dump start\n");
...@@ -927,8 +954,10 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) ...@@ -927,8 +954,10 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
goto out; goto out;
} }
fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans, if (fwrt->dump.monitor_only)
fwrt->dump.monitor_only); dump_mask &= IWL_FW_ERROR_DUMP_FW_MONITOR;
fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask);
file_len = le32_to_cpu(dump_file->file_len); file_len = le32_to_cpu(dump_file->file_len);
fw_error_dump->fwrt_len = file_len; fw_error_dump->fwrt_len = file_len;
if (fw_error_dump->trans_ptr) { if (fw_error_dump->trans_ptr) {
...@@ -969,6 +998,14 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = { ...@@ -969,6 +998,14 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = {
}; };
IWL_EXPORT_SYMBOL(iwl_dump_desc_assert); IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt)
{
IWL_INFO(fwrt, "error dump due to fw assert\n");
fwrt->dump.desc = &iwl_dump_desc_assert;
iwl_fw_error_dump(fwrt);
}
IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump);
void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt) void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt)
{ {
struct iwl_fw_dump_desc *iwl_dump_desc_no_alive = struct iwl_fw_dump_desc *iwl_dump_desc_no_alive =
...@@ -1080,6 +1117,9 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, ...@@ -1080,6 +1117,9 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
int ret, len = 0; int ret, len = 0;
char buf[64]; char buf[64];
if (fwrt->trans->ini_valid)
return 0;
if (fmt) { if (fmt) {
va_list ap; va_list ap;
...@@ -1223,3 +1263,210 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) ...@@ -1223,3 +1263,210 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
cfg->d3_debug_data_length); cfg->d3_debug_data_length);
} }
IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data);
static void
iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_allocation_tlv *alloc)
{
struct iwl_trans *trans = fwrt->trans;
struct iwl_continuous_record_cmd cont_rec = {};
struct iwl_buffer_allocation_cmd *cmd = (void *)&cont_rec.pad[0];
struct iwl_host_cmd hcmd = {
.id = LDBG_CONFIG_CMD,
.flags = CMD_ASYNC,
.data[0] = &cont_rec,
.len[0] = sizeof(cont_rec),
};
void *virtual_addr = NULL;
u32 size = le32_to_cpu(alloc->size);
dma_addr_t phys_addr;
cont_rec.record_mode.enable_recording = cpu_to_le16(BUFFER_ALLOCATION);
if (!trans->num_blocks &&
le32_to_cpu(alloc->buffer_location) !=
IWL_FW_INI_LOCATION_DRAM_PATH)
return;
virtual_addr = dma_alloc_coherent(fwrt->trans->dev, size,
&phys_addr, GFP_KERNEL);
/* TODO: alloc fragments if needed */
if (!virtual_addr)
IWL_ERR(fwrt, "Failed to allocate debug memory\n");
if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon)))
return;
trans->fw_mon[trans->num_blocks].block = virtual_addr;
trans->fw_mon[trans->num_blocks].physical = phys_addr;
trans->fw_mon[trans->num_blocks].size = size;
trans->num_blocks++;
IWL_DEBUG_FW(trans, "Allocated debug block of size %d\n", size);
/* First block is assigned via registers / context info */
if (trans->num_blocks == 1)
return;
cmd->num_frags = cpu_to_le32(1);
cmd->fragments[0].address = cpu_to_le64(phys_addr);
cmd->fragments[0].size = alloc->size;
cmd->allocation_id = alloc->allocation_id;
cmd->buffer_location = alloc->buffer_location;
iwl_trans_send_cmd(trans, &hcmd);
}
static void iwl_fw_dbg_send_hcmd(struct iwl_fw_runtime *fwrt,
struct iwl_ucode_tlv *tlv)
{
struct iwl_fw_ini_hcmd_tlv *hcmd_tlv = (void *)&tlv->data[0];
struct iwl_fw_ini_hcmd *data = &hcmd_tlv->hcmd;
u16 len = le32_to_cpu(tlv->length) - sizeof(*hcmd_tlv);
struct iwl_host_cmd hcmd = {
.id = WIDE_ID(data->group, data->id),
.len = { len, },
.data = { data->data, },
};
iwl_trans_send_cmd(fwrt->trans, &hcmd);
}
static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_region_tlv *tlv,
bool ext, enum iwl_fw_ini_apply_point pnt)
{
void *iter = (void *)tlv->region_config;
int i, size = le32_to_cpu(tlv->num_regions);
for (i = 0; i < size; i++) {
struct iwl_fw_ini_region_cfg *reg = iter;
int id = le32_to_cpu(reg->region_id);
struct iwl_fw_ini_active_regs *active;
if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_regs)))
break;
active = &fwrt->dump.active_regs[id];
if (ext && active->apply_point == pnt)
IWL_WARN(fwrt->trans,
"External region TLV overrides FW default %x\n",
id);
IWL_DEBUG_FW(fwrt,
"%s: apply point %d, activating region ID %d\n",
__func__, pnt, id);
active->reg = reg;
active->apply_point = pnt;
if (le32_to_cpu(reg->region_type) !=
IWL_FW_INI_REGION_DRAM_BUFFER)
iter += le32_to_cpu(reg->num_regions) * sizeof(__le32);
iter += sizeof(*reg);
}
}
static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_trigger_tlv *tlv,
bool ext,
enum iwl_fw_ini_apply_point apply_point)
{
int i, size = le32_to_cpu(tlv->num_triggers);
void *iter = (void *)tlv->trigger_config;
for (i = 0; i < size; i++) {
struct iwl_fw_ini_trigger *trig = iter;
struct iwl_fw_ini_active_triggers *active;
int id = le32_to_cpu(trig->trigger_id);
u32 num;
if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)))
break;
active = &fwrt->dump.active_trigs[id];
if (active->apply_point != apply_point) {
active->conf = NULL;
active->conf_ext = NULL;
}
num = le32_to_cpu(trig->num_regions);
if (ext && active->apply_point == apply_point) {
num += le32_to_cpu(active->conf->num_regions);
if (trig->ignore_default) {
active->conf_ext = active->conf;
active->conf = trig;
} else {
active->conf_ext = trig;
}
} else {
active->conf = trig;
}
iter += sizeof(*trig) +
le32_to_cpu(trig->num_regions) * sizeof(__le32);
active->active = num;
active->apply_point = apply_point;
}
}
static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
struct iwl_apply_point_data *data,
enum iwl_fw_ini_apply_point pnt,
bool ext)
{
void *iter = data->data;
while (iter && iter < data->data + data->size) {
struct iwl_ucode_tlv *tlv = iter;
void *ini_tlv = (void *)tlv->data;
u32 type = le32_to_cpu(tlv->type);
switch (type) {
case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
iwl_fw_dbg_buffer_allocation(fwrt, ini_tlv);
break;
case IWL_UCODE_TLV_TYPE_HCMD:
if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) {
IWL_ERR(fwrt,
"Invalid apply point %x for host command\n",
pnt);
goto next;
}
iwl_fw_dbg_send_hcmd(fwrt, tlv);
break;
case IWL_UCODE_TLV_TYPE_REGIONS:
iwl_fw_dbg_update_regions(fwrt, ini_tlv, ext, pnt);
break;
case IWL_UCODE_TLV_TYPE_TRIGGERS:
iwl_fw_dbg_update_triggers(fwrt, ini_tlv, ext, pnt);
break;
case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
break;
default:
WARN_ONCE(1, "Invalid TLV %x for apply point\n", type);
break;
}
next:
iter += sizeof(*tlv) + le32_to_cpu(tlv->length);
}
}
void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
enum iwl_fw_ini_apply_point apply_point)
{
void *data = &fwrt->trans->apply_points[apply_point];
_iwl_fw_dbg_apply_point(fwrt, data, apply_point, false);
data = &fwrt->trans->apply_points_ext[apply_point];
_iwl_fw_dbg_apply_point(fwrt, data, apply_point, true);
}
IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point);
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#include "file.h" #include "file.h"
#include "error-dump.h" #include "error-dump.h"
#include "api/commands.h" #include "api/commands.h"
#include "api/dbg-tlv.h"
/** /**
* struct iwl_fw_dump_desc - describes the dump * struct iwl_fw_dump_desc - describes the dump
...@@ -192,6 +193,9 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt, ...@@ -192,6 +193,9 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
{ {
struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_tlv *trig;
if (fwrt->trans->ini_valid)
return NULL;
if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id)) if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id))
return NULL; return NULL;
...@@ -262,6 +266,9 @@ _iwl_fw_dbg_stop_recording(struct iwl_trans *trans, ...@@ -262,6 +266,9 @@ _iwl_fw_dbg_stop_recording(struct iwl_trans *trans,
iwl_write_prph(trans, DBGC_IN_SAMPLE, 0); iwl_write_prph(trans, DBGC_IN_SAMPLE, 0);
udelay(100); udelay(100);
iwl_write_prph(trans, DBGC_OUT_CTRL, 0); iwl_write_prph(trans, DBGC_OUT_CTRL, 0);
#ifdef CONFIG_IWLWIFI_DEBUGFS
trans->dbg_rec_on = false;
#endif
} }
static inline void static inline void
...@@ -292,6 +299,14 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans, ...@@ -292,6 +299,14 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans,
} }
} }
#ifdef CONFIG_IWLWIFI_DEBUGFS
static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt)
{
if (fwrt->fw->dbg.dest_tlv && fwrt->cur_fw_img == IWL_UCODE_REGULAR)
fwrt->trans->dbg_rec_on = true;
}
#endif
static inline void static inline void
iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt, iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
struct iwl_fw_dbg_params *params) struct iwl_fw_dbg_params *params)
...@@ -300,6 +315,9 @@ iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt, ...@@ -300,6 +315,9 @@ iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
_iwl_fw_dbg_restart_recording(fwrt->trans, params); _iwl_fw_dbg_restart_recording(fwrt->trans, params);
else else
iwl_fw_dbg_start_stop_hcmd(fwrt, true); iwl_fw_dbg_start_stop_hcmd(fwrt, true);
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_fw_set_dbg_rec_on(fwrt);
#endif
} }
static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt) static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
...@@ -378,6 +396,10 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} ...@@ -378,6 +396,10 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
#endif /* CONFIG_IWLWIFI_DEBUGFS */ #endif /* CONFIG_IWLWIFI_DEBUGFS */
void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt);
void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt); void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt);
void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt);
void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
enum iwl_fw_ini_apply_point apply_point);
#endif /* __iwl_fw_dbg_h__ */ #endif /* __iwl_fw_dbg_h__ */
...@@ -91,6 +91,8 @@ struct iwl_ucode_header { ...@@ -91,6 +91,8 @@ struct iwl_ucode_header {
} u; } u;
}; };
#define IWL_UCODE_INI_TLV_GROUP BIT(24)
/* /*
* new TLV uCode file layout * new TLV uCode file layout
* *
...@@ -141,6 +143,11 @@ enum iwl_ucode_tlv_type { ...@@ -141,6 +143,11 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_FW_GSCAN_CAPA = 50, IWL_UCODE_TLV_FW_GSCAN_CAPA = 50,
IWL_UCODE_TLV_FW_MEM_SEG = 51, IWL_UCODE_TLV_FW_MEM_SEG = 51,
IWL_UCODE_TLV_IML = 52, IWL_UCODE_TLV_IML = 52,
IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP | 0x1,
IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP | 0x2,
IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP | 0x3,
IWL_UCODE_TLV_TYPE_TRIGGERS = IWL_UCODE_INI_TLV_GROUP | 0x4,
IWL_UCODE_TLV_TYPE_DEBUG_FLOW = IWL_UCODE_INI_TLV_GROUP | 0x5,
/* TLVs 0x1000-0x2000 are for internal driver usage */ /* TLVs 0x1000-0x2000 are for internal driver usage */
IWL_UCODE_TLV_FW_DBG_DUMP_LST = 0x1000, IWL_UCODE_TLV_FW_DBG_DUMP_LST = 0x1000,
......
...@@ -65,6 +65,8 @@ ...@@ -65,6 +65,8 @@
#define __iwl_fw_img_h__ #define __iwl_fw_img_h__
#include <linux/types.h> #include <linux/types.h>
#include "api/dbg-tlv.h"
#include "file.h" #include "file.h"
#include "error-dump.h" #include "error-dump.h"
...@@ -220,6 +222,30 @@ struct iwl_fw_dbg { ...@@ -220,6 +222,30 @@ struct iwl_fw_dbg {
u32 dump_mask; u32 dump_mask;
}; };
/**
* struct iwl_fw_ini_active_triggers
* @active: is this trigger active
* @apply_point: last apply point that updated this trigger
* @conf: active trigger
* @conf_ext: second trigger, contains extra regions to dump
*/
struct iwl_fw_ini_active_triggers {
bool active;
enum iwl_fw_ini_apply_point apply_point;
struct iwl_fw_ini_trigger *conf;
struct iwl_fw_ini_trigger *conf_ext;
};
/**
* struct iwl_fw_ini_active_regs
* @reg: active region from TLV
* @apply_point: apply point where it became active
*/
struct iwl_fw_ini_active_regs {
struct iwl_fw_ini_region_cfg *reg;
enum iwl_fw_ini_apply_point apply_point;
};
/** /**
* struct iwl_fw - variables associated with the firmware * struct iwl_fw - variables associated with the firmware
* *
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#include "iwl-trans.h" #include "iwl-trans.h"
#include "img.h" #include "img.h"
#include "fw/api/debug.h" #include "fw/api/debug.h"
#include "fw/api/dbg-tlv.h"
#include "fw/api/paging.h" #include "fw/api/paging.h"
#include "iwl-eeprom-parse.h" #include "iwl-eeprom-parse.h"
...@@ -139,6 +140,8 @@ struct iwl_fw_runtime { ...@@ -139,6 +140,8 @@ struct iwl_fw_runtime {
/* ts of the beginning of a non-collect fw dbg data period */ /* ts of the beginning of a non-collect fw dbg data period */
unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1];
u32 *d3_debug_data; u32 *d3_debug_data;
struct iwl_fw_ini_active_regs active_regs[IWL_FW_INI_MAX_REGION_ID];
struct iwl_fw_ini_active_triggers active_trigs[IWL_FW_TRIGGER_ID_NUM];
} dump; } dump;
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
struct { struct {
......
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include <linux/firmware.h>
#include "iwl-trans.h"
#include "iwl-dbg-tlv.h"
void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
bool ext)
{
struct iwl_apply_point_data *data;
struct iwl_fw_ini_header *header = (void *)&tlv->data[0];
u32 apply_point = le32_to_cpu(header->apply_point);
int copy_size = le32_to_cpu(tlv->length) + sizeof(*tlv);
if (WARN_ONCE(apply_point >= IWL_FW_INI_APPLY_NUM,
"Invalid apply point id %d\n", apply_point))
return;
if (ext)
data = &trans->apply_points_ext[apply_point];
else
data = &trans->apply_points[apply_point];
/*
* Make sure we still have room to copy this TLV. Offset points to the
* location the last copy ended.
*/
if (WARN_ONCE(data->offset + copy_size > data->size,
"Not enough memory for apply point %d\n",
apply_point))
return;
memcpy(data->data + data->offset, (void *)tlv, copy_size);
data->offset += copy_size;
}
void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
bool ext)
{
struct iwl_ucode_tlv *tlv;
u32 size[IWL_FW_INI_APPLY_NUM] = {0};
int i;
while (len >= sizeof(*tlv)) {
u32 tlv_len, tlv_type, apply;
struct iwl_fw_ini_header *hdr;
len -= sizeof(*tlv);
tlv = (void *)data;
tlv_len = le32_to_cpu(tlv->length);
tlv_type = le32_to_cpu(tlv->type);
if (len < tlv_len)
return;
len -= ALIGN(tlv_len, 4);
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
if (!(tlv_type & IWL_UCODE_INI_TLV_GROUP))
continue;
hdr = (void *)&tlv->data[0];
apply = le32_to_cpu(hdr->apply_point);
IWL_DEBUG_FW(trans, "Read TLV %x, apply point %d\n",
le32_to_cpu(tlv->type), apply);
if (WARN_ON(apply >= IWL_FW_INI_APPLY_NUM))
continue;
size[apply] += sizeof(*tlv) + tlv_len;
}
for (i = 0; i < ARRAY_SIZE(size); i++) {
void *mem;
if (!size[i])
continue;
mem = kzalloc(size[i], GFP_KERNEL);
if (!mem) {
IWL_ERR(trans, "No memory for apply point %d\n", i);
return;
}
if (ext) {
trans->apply_points_ext[i].data = mem;
trans->apply_points_ext[i].size = size[i];
} else {
trans->apply_points[i].data = mem;
trans->apply_points[i].size = size[i];
}
trans->ini_valid = true;
}
}
void iwl_fw_dbg_free(struct iwl_trans *trans)
{
int i;
for (i = 0; i < ARRAY_SIZE(trans->apply_points); i++) {
kfree(trans->apply_points[i].data);
trans->apply_points[i].size = 0;
trans->apply_points[i].offset = 0;
kfree(trans->apply_points_ext[i].data);
trans->apply_points_ext[i].size = 0;
trans->apply_points_ext[i].offset = 0;
}
}
static int iwl_parse_fw_dbg_tlv(struct iwl_trans *trans, const u8 *data,
size_t len)
{
struct iwl_ucode_tlv *tlv;
enum iwl_ucode_tlv_type tlv_type;
u32 tlv_len;
while (len >= sizeof(*tlv)) {
len -= sizeof(*tlv);
tlv = (void *)data;
tlv_len = le32_to_cpu(tlv->length);
tlv_type = le32_to_cpu(tlv->type);
if (len < tlv_len) {
IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
len, tlv_len);
return -EINVAL;
}
len -= ALIGN(tlv_len, 4);
data += sizeof(*tlv) + ALIGN(tlv_len, 4);
switch (tlv_type) {
case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
case IWL_UCODE_TLV_TYPE_HCMD:
case IWL_UCODE_TLV_TYPE_REGIONS:
case IWL_UCODE_TLV_TYPE_TRIGGERS:
case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
iwl_fw_dbg_copy_tlv(trans, tlv, true);
default:
WARN_ONCE(1, "Invalid TLV %x\n", tlv_type);
break;
}
}
return 0;
}
void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans)
{
const struct firmware *fw;
int res;
if (trans->external_ini_loaded || !iwlwifi_mod_params.enable_ini)
return;
res = request_firmware(&fw, "iwl-dbg-tlv.ini", dev);
if (res)
return;
iwl_alloc_dbg_tlv(trans, fw->size, fw->data, true);
iwl_parse_fw_dbg_tlv(trans, fw->data, fw->size);
trans->external_ini_loaded = true;
release_firmware(fw);
}
/******************************************************************************
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*
* The full GNU General Public License is included in this distribution
* in the file called COPYING.
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#ifndef __iwl_dbg_tlv_h__
#define __iwl_dbg_tlv_h__
#include <linux/device.h>
#include <linux/types.h>
/**
* struct iwl_apply_point_data
* @data: start address of this apply point data
* @size total size of the data
* @offset: current offset of the copied data
*/
struct iwl_apply_point_data {
void *data;
int size;
int offset;
};
struct iwl_trans;
void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans);
void iwl_fw_dbg_free(struct iwl_trans *trans);
void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
bool ext);
void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
bool ext);
#endif /* __iwl_dbg_tlv_h__*/
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#include "iwl-op-mode.h" #include "iwl-op-mode.h"
#include "iwl-agn-hw.h" #include "iwl-agn-hw.h"
#include "fw/img.h" #include "fw/img.h"
#include "iwl-dbg-tlv.h"
#include "iwl-config.h" #include "iwl-config.h"
#include "iwl-modparams.h" #include "iwl-modparams.h"
...@@ -645,6 +646,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, ...@@ -645,6 +646,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
len -= sizeof(*ucode); len -= sizeof(*ucode);
if (iwlwifi_mod_params.enable_ini)
iwl_alloc_dbg_tlv(drv->trans, len, data, false);
while (len >= sizeof(*tlv)) { while (len >= sizeof(*tlv)) {
len -= sizeof(*tlv); len -= sizeof(*tlv);
tlv = (void *)data; tlv = (void *)data;
...@@ -1086,6 +1090,13 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, ...@@ -1086,6 +1090,13 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
return -ENOMEM; return -ENOMEM;
break; break;
} }
case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
case IWL_UCODE_TLV_TYPE_HCMD:
case IWL_UCODE_TLV_TYPE_REGIONS:
case IWL_UCODE_TLV_TYPE_TRIGGERS:
case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
if (iwlwifi_mod_params.enable_ini)
iwl_fw_dbg_copy_tlv(drv->trans, tlv, false);
default: default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break; break;
...@@ -1565,7 +1576,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) ...@@ -1565,7 +1576,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
if (!drv->dbgfs_drv) { if (!drv->dbgfs_drv) {
IWL_ERR(drv, "failed to create debugfs directory\n"); IWL_ERR(drv, "failed to create debugfs directory\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_free_drv; goto err_free_tlv;
} }
/* Create transport layer debugfs dir */ /* Create transport layer debugfs dir */
...@@ -1590,7 +1601,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) ...@@ -1590,7 +1601,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
err_free_dbgfs: err_free_dbgfs:
debugfs_remove_recursive(drv->dbgfs_drv); debugfs_remove_recursive(drv->dbgfs_drv);
err_free_drv: err_free_tlv:
iwl_fw_dbg_free(drv->trans);
#endif #endif
kfree(drv); kfree(drv);
err: err:
...@@ -1616,9 +1628,13 @@ void iwl_drv_stop(struct iwl_drv *drv) ...@@ -1616,9 +1628,13 @@ void iwl_drv_stop(struct iwl_drv *drv)
mutex_unlock(&iwlwifi_opmode_table_mtx); mutex_unlock(&iwlwifi_opmode_table_mtx);
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
drv->trans->ops->debugfs_cleanup(drv->trans);
debugfs_remove_recursive(drv->dbgfs_drv); debugfs_remove_recursive(drv->dbgfs_drv);
#endif #endif
iwl_fw_dbg_free(drv->trans);
kfree(drv); kfree(drv);
} }
...@@ -1749,6 +1765,10 @@ MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)"); ...@@ -1749,6 +1765,10 @@ MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644); module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644);
MODULE_PARM_DESC(uapsd_disable, MODULE_PARM_DESC(uapsd_disable,
"disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)"); "disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)");
module_param_named(enable_ini, iwlwifi_mod_params.enable_ini,
bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(enable_ini,
"Enable debug INI TLV FW debug infrastructure (default: 0");
/* /*
* set bt_coex_active to true, uCode will do kill/defer * set bt_coex_active to true, uCode will do kill/defer
......
...@@ -122,6 +122,7 @@ enum iwl_uapsd_disable { ...@@ -122,6 +122,7 @@ enum iwl_uapsd_disable {
* @fw_monitor: allow to use firmware monitor * @fw_monitor: allow to use firmware monitor
* @disable_11ac: disable VHT capabilities, default = false. * @disable_11ac: disable VHT capabilities, default = false.
* @remove_when_gone: remove an inaccessible device from the PCIe bus. * @remove_when_gone: remove an inaccessible device from the PCIe bus.
* @enable_ini: enable new FW debug infratructure (INI TLVs)
*/ */
struct iwl_mod_params { struct iwl_mod_params {
int swcrypto; int swcrypto;
...@@ -148,6 +149,7 @@ struct iwl_mod_params { ...@@ -148,6 +149,7 @@ struct iwl_mod_params {
*/ */
bool disable_11ax; bool disable_11ax;
bool remove_when_gone; bool remove_when_gone;
bool enable_ini;
}; };
#endif /* #__iwl_modparams_h__ */ #endif /* #__iwl_modparams_h__ */
...@@ -73,6 +73,8 @@ ...@@ -73,6 +73,8 @@
#include "iwl-op-mode.h" #include "iwl-op-mode.h"
#include "fw/api/cmdhdr.h" #include "fw/api/cmdhdr.h"
#include "fw/api/txq.h" #include "fw/api/txq.h"
#include "fw/api/dbg-tlv.h"
#include "iwl-dbg-tlv.h"
/** /**
* DOC: Transport layer - what is it ? * DOC: Transport layer - what is it ?
...@@ -534,6 +536,8 @@ struct iwl_trans_rxq_dma_data { ...@@ -534,6 +536,8 @@ struct iwl_trans_rxq_dma_data {
* @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
* TX'ed commands and similar. The buffer will be vfree'd by the caller. * TX'ed commands and similar. The buffer will be vfree'd by the caller.
* Note that the transport must fill in the proper file headers. * Note that the transport must fill in the proper file headers.
* @debugfs_cleanup: used in the driver unload flow to make a proper cleanup
* of the trans debugfs
*/ */
struct iwl_trans_ops { struct iwl_trans_ops {
...@@ -602,7 +606,8 @@ struct iwl_trans_ops { ...@@ -602,7 +606,8 @@ struct iwl_trans_ops {
void (*resume)(struct iwl_trans *trans); void (*resume)(struct iwl_trans *trans);
struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
bool monitor_only); u32 dump_mask);
void (*debugfs_cleanup)(struct iwl_trans *trans);
}; };
/** /**
...@@ -678,7 +683,6 @@ enum iwl_plat_pm_mode { ...@@ -678,7 +683,6 @@ enum iwl_plat_pm_mode {
* enter/exit (in msecs). * enter/exit (in msecs).
*/ */
#define IWL_TRANS_IDLE_TIMEOUT 2000 #define IWL_TRANS_IDLE_TIMEOUT 2000
#define IWL_MAX_DEBUG_ALLOCATIONS 1
/** /**
* struct iwl_dram_data * struct iwl_dram_data
...@@ -733,6 +737,7 @@ struct iwl_dram_data { ...@@ -733,6 +737,7 @@ struct iwl_dram_data {
* @runtime_pm_mode: the runtime power management mode in use. This * @runtime_pm_mode: the runtime power management mode in use. This
* mode is set during the initialization phase and is not * mode is set during the initialization phase and is not
* supposed to change during runtime. * supposed to change during runtime.
* @dbg_rec_on: true iff there is a fw debug recording currently active
*/ */
struct iwl_trans { struct iwl_trans {
const struct iwl_trans_ops *ops; const struct iwl_trans_ops *ops;
...@@ -773,17 +778,23 @@ struct iwl_trans { ...@@ -773,17 +778,23 @@ struct iwl_trans {
struct lockdep_map sync_cmd_lockdep_map; struct lockdep_map sync_cmd_lockdep_map;
#endif #endif
struct iwl_apply_point_data apply_points[IWL_FW_INI_APPLY_NUM];
struct iwl_apply_point_data apply_points_ext[IWL_FW_INI_APPLY_NUM];
bool external_ini_loaded;
bool ini_valid;
const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv; const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv;
const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv; struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
u32 dbg_dump_mask;
u8 dbg_n_dest_reg; u8 dbg_n_dest_reg;
int num_blocks; int num_blocks;
struct iwl_dram_data fw_mon[IWL_MAX_DEBUG_ALLOCATIONS]; struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM];
enum iwl_plat_pm_mode system_pm_mode; enum iwl_plat_pm_mode system_pm_mode;
enum iwl_plat_pm_mode runtime_pm_mode; enum iwl_plat_pm_mode runtime_pm_mode;
bool suspending; bool suspending;
bool dbg_rec_on;
/* pointer to trans specific struct */ /* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */ /*Ensure that this pointer will always be aligned to sizeof pointer */
...@@ -896,11 +907,11 @@ static inline void iwl_trans_resume(struct iwl_trans *trans) ...@@ -896,11 +907,11 @@ static inline void iwl_trans_resume(struct iwl_trans *trans)
} }
static inline struct iwl_trans_dump_data * static inline struct iwl_trans_dump_data *
iwl_trans_dump_data(struct iwl_trans *trans, bool monitor_only) iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask)
{ {
if (!trans->ops->dump_data) if (!trans->ops->dump_data)
return NULL; return NULL;
return trans->ops->dump_data(trans, monitor_only); return trans->ops->dump_data(trans, dump_mask);
} }
static inline struct iwl_device_cmd * static inline struct iwl_device_cmd *
......
...@@ -377,6 +377,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, ...@@ -377,6 +377,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
atomic_set(&mvm->mac80211_queue_stop_count[i], 0); atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_fw_set_dbg_rec_on(&mvm->fwrt);
#endif
clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status); clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status);
return 0; return 0;
...@@ -407,6 +410,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ...@@ -407,6 +410,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
if (ret) { if (ret) {
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
iwl_fw_assert_error_dump(&mvm->fwrt);
goto error; goto error;
} }
...@@ -1024,6 +1028,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ...@@ -1024,6 +1028,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
ret = iwl_mvm_load_rt_fw(mvm); ret = iwl_mvm_load_rt_fw(mvm);
if (ret) { if (ret) {
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
iwl_fw_assert_error_dump(&mvm->fwrt);
goto error; goto error;
} }
......
...@@ -767,13 +767,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, ...@@ -767,13 +767,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
} }
ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
ctxt_sta->bi_reciprocal =
cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
vif->bss_conf.dtim_period); vif->bss_conf.dtim_period);
ctxt_sta->dtim_reciprocal =
cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
vif->bss_conf.dtim_period));
ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
...@@ -782,8 +777,30 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, ...@@ -782,8 +777,30 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
if (vif->bss_conf.assoc && vif->bss_conf.he_support && if (vif->bss_conf.assoc && vif->bss_conf.he_support &&
!iwlwifi_mod_params.disable_11ax) !iwlwifi_mod_params.disable_11ax) {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u8 sta_id = mvmvif->ap_sta_id;
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
if (sta_id != IWL_MVM_INVALID_STA) {
struct ieee80211_sta *sta;
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
lockdep_is_held(&mvm->mutex));
/*
* TODO: we should check the ext cap IE but it is
* unclear why the spec requires two bits (one in HE
* cap IE, and one in the ext cap IE). In the meantime
* rely on the HE cap IE only.
*/
if (sta && (sta->he_cap.he_cap_elem.mac_cap_info[0] &
IEEE80211_HE_MAC_CAP0_TWT_RES))
ctxt_sta->data_policy |=
cpu_to_le32(TWT_SUPPORTED);
}
}
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
} }
...@@ -832,8 +849,6 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, ...@@ -832,8 +849,6 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
/* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
cmd.ibss.bi_reciprocal =
cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
/* TODO: Assumes that the beacon id == mac context id */ /* TODO: Assumes that the beacon id == mac context id */
cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
...@@ -1179,14 +1194,12 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, ...@@ -1179,14 +1194,12 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
} }
if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
ctxt_ap->bi_reciprocal =
cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
vif->bss_conf.dtim_period); vif->bss_conf.dtim_period);
ctxt_ap->dtim_reciprocal =
cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
vif->bss_conf.dtim_period));
if (!fw_has_api(&mvm->fw->ucode_capa, if (!fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_STA_TYPE)) IWL_UCODE_TLV_API_STA_TYPE))
......
...@@ -419,6 +419,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ...@@ -419,6 +419,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP); ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
if (iwl_mvm_has_tlc_offload(mvm)) { if (iwl_mvm_has_tlc_offload(mvm)) {
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
...@@ -2968,6 +2969,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ...@@ -2968,6 +2969,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_AP) { if (vif->type == NL80211_IFTYPE_AP) {
mvmvif->ap_assoc_sta_count++; mvmvif->ap_assoc_sta_count++;
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
if (vif->bss_conf.he_support &&
!iwlwifi_mod_params.disable_11ax)
iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
} }
iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
......
...@@ -769,7 +769,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -769,7 +769,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
memcpy(trans->dbg_conf_tlv, mvm->fw->dbg.conf_tlv, memcpy(trans->dbg_conf_tlv, mvm->fw->dbg.conf_tlv,
sizeof(trans->dbg_conf_tlv)); sizeof(trans->dbg_conf_tlv));
trans->dbg_trigger_tlv = mvm->fw->dbg.trigger_tlv; trans->dbg_trigger_tlv = mvm->fw->dbg.trigger_tlv;
trans->dbg_dump_mask = mvm->fw->dbg.dump_mask;
trans->iml = mvm->fw->iml; trans->iml = mvm->fw->iml;
trans->iml_len = mvm->fw->iml_len; trans->iml_len = mvm->fw->iml_len;
......
...@@ -882,7 +882,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { ...@@ -882,7 +882,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22000_2ax_cfg_hr)}, {IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)}, {IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22000_2ax_cfg_hr)}, {IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ac_cfg_jf)}, {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ax_cfg_hr)},
{IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22560_2ax_cfg_su_cdb)}, {IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22560_2ax_cfg_su_cdb)},
{IWL_PCI_DEVICE(0x40C0, 0x0010, iwl22560_2ax_cfg_su_cdb)}, {IWL_PCI_DEVICE(0x40C0, 0x0010, iwl22560_2ax_cfg_su_cdb)},
{IWL_PCI_DEVICE(0x40c0, 0x0090, iwl22560_2ax_cfg_su_cdb)}, {IWL_PCI_DEVICE(0x40c0, 0x0090, iwl22560_2ax_cfg_su_cdb)},
......
...@@ -378,6 +378,23 @@ struct iwl_tso_hdr_page { ...@@ -378,6 +378,23 @@ struct iwl_tso_hdr_page {
u8 *pos; u8 *pos;
}; };
#ifdef CONFIG_IWLWIFI_DEBUGFS
/**
* enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data
* debugfs file
*
* @IWL_FW_MON_DBGFS_STATE_CLOSED: the file is closed.
* @IWL_FW_MON_DBGFS_STATE_OPEN: the file is open.
* @IWL_FW_MON_DBGFS_STATE_DISABLED: the file is disabled, once this state is
* set the file can no longer be used.
*/
enum iwl_fw_mon_dbgfs_state {
IWL_FW_MON_DBGFS_STATE_CLOSED,
IWL_FW_MON_DBGFS_STATE_OPEN,
IWL_FW_MON_DBGFS_STATE_DISABLED,
};
#endif
/** /**
* enum iwl_shared_irq_flags - level of sharing for irq * enum iwl_shared_irq_flags - level of sharing for irq
* @IWL_SHARED_IRQ_NON_RX: interrupt vector serves non rx causes. * @IWL_SHARED_IRQ_NON_RX: interrupt vector serves non rx causes.
...@@ -414,6 +431,26 @@ struct iwl_self_init_dram { ...@@ -414,6 +431,26 @@ struct iwl_self_init_dram {
int paging_cnt; int paging_cnt;
}; };
/**
* struct cont_rec: continuous recording data structure
* @prev_wr_ptr: the last address that was read in monitor_data
* debugfs file
* @prev_wrap_cnt: the wrap count that was used during the last read in
* monitor_data debugfs file
* @state: the state of monitor_data debugfs file as described
* in &iwl_fw_mon_dbgfs_state enum
* @mutex: locked while reading from monitor_data debugfs file
*/
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct cont_rec {
u32 prev_wr_ptr;
u32 prev_wrap_cnt;
u8 state;
/* Used to sync monitor_data debugfs file with driver unload flow */
struct mutex mutex;
};
#endif
/** /**
* struct iwl_trans_pcie - PCIe transport specific data * struct iwl_trans_pcie - PCIe transport specific data
* @rxq: all the RX queue data * @rxq: all the RX queue data
...@@ -451,6 +488,9 @@ struct iwl_self_init_dram { ...@@ -451,6 +488,9 @@ struct iwl_self_init_dram {
* @reg_lock: protect hw register access * @reg_lock: protect hw register access
* @mutex: to protect stop_device / start_fw / start_hw * @mutex: to protect stop_device / start_fw / start_hw
* @cmd_in_flight: true when we have a host command in flight * @cmd_in_flight: true when we have a host command in flight
#ifdef CONFIG_IWLWIFI_DEBUGFS
* @fw_mon_data: fw continuous recording data
#endif
* @msix_entries: array of MSI-X entries * @msix_entries: array of MSI-X entries
* @msix_enabled: true if managed to enable MSI-X * @msix_enabled: true if managed to enable MSI-X
* @shared_vec_mask: the type of causes the shared vector handles * @shared_vec_mask: the type of causes the shared vector handles
...@@ -538,6 +578,10 @@ struct iwl_trans_pcie { ...@@ -538,6 +578,10 @@ struct iwl_trans_pcie {
bool cmd_hold_nic_awake; bool cmd_hold_nic_awake;
bool ref_cmd_in_flight; bool ref_cmd_in_flight;
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct cont_rec fw_mon_data;
#endif
struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES]; struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES];
bool msix_enabled; bool msix_enabled;
u8 shared_vec_mask; u8 shared_vec_mask;
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/wait.h>
#include "iwl-drv.h" #include "iwl-drv.h"
#include "iwl-trans.h" #include "iwl-trans.h"
...@@ -2709,6 +2710,137 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file, ...@@ -2709,6 +2710,137 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
return count; return count;
} }
static int iwl_dbgfs_monitor_data_open(struct inode *inode,
struct file *file)
{
struct iwl_trans *trans = inode->i_private;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
if (!trans->dbg_dest_tlv ||
trans->dbg_dest_tlv->monitor_mode != EXTERNAL_MODE) {
IWL_ERR(trans, "Debug destination is not set to DRAM\n");
return -ENOENT;
}
if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED)
return -EBUSY;
trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN;
return simple_open(inode, file);
}
static int iwl_dbgfs_monitor_data_release(struct inode *inode,
struct file *file)
{
struct iwl_trans_pcie *trans_pcie =
IWL_TRANS_GET_PCIE_TRANS(inode->i_private);
if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN)
trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
return 0;
}
static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,
void *buf, ssize_t *size,
ssize_t *bytes_copied)
{
int buf_size_left = count - *bytes_copied;
buf_size_left = buf_size_left - (buf_size_left % sizeof(u32));
if (*size > buf_size_left)
*size = buf_size_left;
*size -= copy_to_user(user_buf, buf, *size);
*bytes_copied += *size;
if (buf_size_left == *size)
return true;
return false;
}
static ssize_t iwl_dbgfs_monitor_data_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_trans *trans = file->private_data;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
void *cpu_addr = (void *)trans->fw_mon[0].block, *curr_buf;
struct cont_rec *data = &trans_pcie->fw_mon_data;
u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt;
ssize_t size, bytes_copied = 0;
bool b_full;
if (trans->dbg_dest_tlv) {
write_ptr_addr =
le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
wrap_cnt_addr = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
} else {
write_ptr_addr = MON_BUFF_WRPTR;
wrap_cnt_addr = MON_BUFF_CYCLE_CNT;
}
if (unlikely(!trans->dbg_rec_on))
return 0;
mutex_lock(&data->mutex);
if (data->state ==
IWL_FW_MON_DBGFS_STATE_DISABLED) {
mutex_unlock(&data->mutex);
return 0;
}
/* write_ptr position in bytes rather then DW */
write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32);
wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr);
if (data->prev_wrap_cnt == wrap_cnt) {
size = write_ptr - data->prev_wr_ptr;
curr_buf = cpu_addr + data->prev_wr_ptr;
b_full = iwl_write_to_user_buf(user_buf, count,
curr_buf, &size,
&bytes_copied);
data->prev_wr_ptr += size;
} else if (data->prev_wrap_cnt == wrap_cnt - 1 &&
write_ptr < data->prev_wr_ptr) {
size = trans->fw_mon[0].size - data->prev_wr_ptr;
curr_buf = cpu_addr + data->prev_wr_ptr;
b_full = iwl_write_to_user_buf(user_buf, count,
curr_buf, &size,
&bytes_copied);
data->prev_wr_ptr += size;
if (!b_full) {
size = write_ptr;
b_full = iwl_write_to_user_buf(user_buf, count,
cpu_addr, &size,
&bytes_copied);
data->prev_wr_ptr = size;
data->prev_wrap_cnt++;
}
} else {
if (data->prev_wrap_cnt == wrap_cnt - 1 &&
write_ptr > data->prev_wr_ptr)
IWL_WARN(trans,
"write pointer passed previous write pointer, start copying from the beginning\n");
else if (!unlikely(data->prev_wrap_cnt == 0 &&
data->prev_wr_ptr == 0))
IWL_WARN(trans,
"monitor data is out of sync, start copying from the beginning\n");
size = write_ptr;
b_full = iwl_write_to_user_buf(user_buf, count,
cpu_addr, &size,
&bytes_copied);
data->prev_wr_ptr = size;
data->prev_wrap_cnt = wrap_cnt;
}
mutex_unlock(&data->mutex);
return bytes_copied;
}
DEBUGFS_READ_WRITE_FILE_OPS(interrupt); DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
DEBUGFS_READ_FILE_OPS(fh_reg); DEBUGFS_READ_FILE_OPS(fh_reg);
DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(rx_queue);
...@@ -2716,6 +2848,12 @@ DEBUGFS_READ_FILE_OPS(tx_queue); ...@@ -2716,6 +2848,12 @@ DEBUGFS_READ_FILE_OPS(tx_queue);
DEBUGFS_WRITE_FILE_OPS(csr); DEBUGFS_WRITE_FILE_OPS(csr);
DEBUGFS_READ_WRITE_FILE_OPS(rfkill); DEBUGFS_READ_WRITE_FILE_OPS(rfkill);
static const struct file_operations iwl_dbgfs_monitor_data_ops = {
.read = iwl_dbgfs_monitor_data_read,
.open = iwl_dbgfs_monitor_data_open,
.release = iwl_dbgfs_monitor_data_release,
};
/* Create the debugfs files and directories */ /* Create the debugfs files and directories */
int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
{ {
...@@ -2727,12 +2865,23 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) ...@@ -2727,12 +2865,23 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
DEBUGFS_ADD_FILE(csr, dir, 0200); DEBUGFS_ADD_FILE(csr, dir, 0200);
DEBUGFS_ADD_FILE(fh_reg, dir, 0400); DEBUGFS_ADD_FILE(fh_reg, dir, 0400);
DEBUGFS_ADD_FILE(rfkill, dir, 0600); DEBUGFS_ADD_FILE(rfkill, dir, 0600);
DEBUGFS_ADD_FILE(monitor_data, dir, 0400);
return 0; return 0;
err: err:
IWL_ERR(trans, "failed to create the trans debugfs entry\n"); IWL_ERR(trans, "failed to create the trans debugfs entry\n");
return -ENOMEM; return -ENOMEM;
} }
static void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct cont_rec *data = &trans_pcie->fw_mon_data;
mutex_lock(&data->mutex);
data->state = IWL_FW_MON_DBGFS_STATE_DISABLED;
mutex_unlock(&data->mutex);
}
#endif /*CONFIG_IWLWIFI_DEBUGFS */ #endif /*CONFIG_IWLWIFI_DEBUGFS */
static u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd) static u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd)
...@@ -2990,7 +3139,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len) ...@@ -2990,7 +3139,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len)
static struct iwl_trans_dump_data static struct iwl_trans_dump_data
*iwl_trans_pcie_dump_data(struct iwl_trans *trans, *iwl_trans_pcie_dump_data(struct iwl_trans *trans,
bool monitor_only) u32 dump_mask)
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_fw_error_dump_data *data; struct iwl_fw_error_dump_data *data;
...@@ -3002,7 +3151,10 @@ static struct iwl_trans_dump_data ...@@ -3002,7 +3151,10 @@ static struct iwl_trans_dump_data
int i, ptr; int i, ptr;
bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
!trans->cfg->mq_rx_supported && !trans->cfg->mq_rx_supported &&
trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
if (!dump_mask)
return NULL;
/* transport dump header */ /* transport dump header */
len = sizeof(*dump_data); len = sizeof(*dump_data);
...@@ -3014,11 +3166,7 @@ static struct iwl_trans_dump_data ...@@ -3014,11 +3166,7 @@ static struct iwl_trans_dump_data
/* FW monitor */ /* FW monitor */
monitor_len = iwl_trans_get_fw_monitor_len(trans, &len); monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);
if (monitor_only) { if (dump_mask == BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) {
if (!(trans->dbg_dump_mask &
BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)))
return NULL;
dump_data = vzalloc(len); dump_data = vzalloc(len);
if (!dump_data) if (!dump_data)
return NULL; return NULL;
...@@ -3031,11 +3179,11 @@ static struct iwl_trans_dump_data ...@@ -3031,11 +3179,11 @@ static struct iwl_trans_dump_data
} }
/* CSR registers */ /* CSR registers */
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
len += sizeof(*data) + IWL_CSR_TO_DUMP; len += sizeof(*data) + IWL_CSR_TO_DUMP;
/* FH registers */ /* FH registers */
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) { if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
if (trans->cfg->gen2) if (trans->cfg->gen2)
len += sizeof(*data) + len += sizeof(*data) +
(FH_MEM_UPPER_BOUND_GEN2 - (FH_MEM_UPPER_BOUND_GEN2 -
...@@ -3060,8 +3208,7 @@ static struct iwl_trans_dump_data ...@@ -3060,8 +3208,7 @@ static struct iwl_trans_dump_data
} }
/* Paged memory for gen2 HW */ /* Paged memory for gen2 HW */
if (trans->cfg->gen2 && if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++)
len += sizeof(*data) + len += sizeof(*data) +
sizeof(struct iwl_fw_error_dump_paging) + sizeof(struct iwl_fw_error_dump_paging) +
...@@ -3074,7 +3221,7 @@ static struct iwl_trans_dump_data ...@@ -3074,7 +3221,7 @@ static struct iwl_trans_dump_data
len = 0; len = 0;
data = (void *)dump_data->data; data = (void *)dump_data->data;
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) { if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) {
u16 tfd_size = trans_pcie->tfd_size; u16 tfd_size = trans_pcie->tfd_size;
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
...@@ -3108,16 +3255,15 @@ static struct iwl_trans_dump_data ...@@ -3108,16 +3255,15 @@ static struct iwl_trans_dump_data
data = iwl_fw_error_next_data(data); data = iwl_fw_error_next_data(data);
} }
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
len += iwl_trans_pcie_dump_csr(trans, &data); len += iwl_trans_pcie_dump_csr(trans, &data);
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS))
len += iwl_trans_pcie_fh_regs_dump(trans, &data); len += iwl_trans_pcie_fh_regs_dump(trans, &data);
if (dump_rbs) if (dump_rbs)
len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
/* Paged memory for gen2 HW */ /* Paged memory for gen2 HW */
if (trans->cfg->gen2 && if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) { for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) {
struct iwl_fw_error_dump_paging *paging; struct iwl_fw_error_dump_paging *paging;
dma_addr_t addr = dma_addr_t addr =
...@@ -3137,7 +3283,7 @@ static struct iwl_trans_dump_data ...@@ -3137,7 +3283,7 @@ static struct iwl_trans_dump_data
len += sizeof(*data) + sizeof(*paging) + page_len; len += sizeof(*data) + sizeof(*paging) + page_len;
} }
} }
if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
dump_data->len = len; dump_data->len = len;
...@@ -3214,6 +3360,9 @@ static const struct iwl_trans_ops trans_ops_pcie = { ...@@ -3214,6 +3360,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
.freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
.block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
#endif
}; };
static const struct iwl_trans_ops trans_ops_pcie_gen2 = { static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
...@@ -3233,6 +3382,9 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { ...@@ -3233,6 +3382,9 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
.txq_free = iwl_trans_pcie_dyn_txq_free, .txq_free = iwl_trans_pcie_dyn_txq_free,
.wait_txq_empty = iwl_trans_pcie_wait_txq_empty, .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
.rxq_dma_data = iwl_trans_pcie_rxq_dma_data, .rxq_dma_data = iwl_trans_pcie_rxq_dma_data,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
#endif
}; };
struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
...@@ -3404,8 +3556,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -3404,8 +3556,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
#if IS_ENABLED(CONFIG_IWLMVM) #if IS_ENABLED(CONFIG_IWLMVM)
trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID); trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) == if (cfg == &iwl22000_2ax_cfg_hr) {
CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) { if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
trans->cfg = &iwl22000_2ax_cfg_hr;
} else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF)) {
trans->cfg = &iwl22000_2ax_cfg_jf;
} else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HRCDB)) {
IWL_ERR(trans, "RF ID HRCDB is not supported\n");
ret = -EINVAL;
goto out_no_pci;
} else {
IWL_ERR(trans, "Unrecognized RF ID 0x%08x\n",
CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id));
ret = -EINVAL;
goto out_no_pci;
}
} else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
u32 hw_status; u32 hw_status;
hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS); hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
...@@ -3466,6 +3636,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, ...@@ -3466,6 +3636,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED; trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ #endif /* CONFIG_IWLWIFI_PCIE_RTPM */
#ifdef CONFIG_IWLWIFI_DEBUGFS
trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
mutex_init(&trans_pcie->fw_mon_data.mutex);
#endif
return trans; return trans;
out_free_ict: out_free_ict:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment