162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright(c) 2003 - 2014, 2018 - 2022  Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci * Copyright(c) 2015 Intel Deutschland GmbH
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Portions of this file are derived from the ipw3945 project, as well
862306a36Sopenharmony_ci * as portions of the ieee80211 subsystem header files.
962306a36Sopenharmony_ci *****************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/sched.h>
1962306a36Sopenharmony_ci#include <linux/skbuff.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci#include <linux/if_arp.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <net/mac80211.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/div64.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "iwl-eeprom-read.h"
2962306a36Sopenharmony_ci#include "iwl-eeprom-parse.h"
3062306a36Sopenharmony_ci#include "iwl-io.h"
3162306a36Sopenharmony_ci#include "iwl-trans.h"
3262306a36Sopenharmony_ci#include "iwl-op-mode.h"
3362306a36Sopenharmony_ci#include "iwl-drv.h"
3462306a36Sopenharmony_ci#include "iwl-modparams.h"
3562306a36Sopenharmony_ci#include "iwl-prph.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "dev.h"
3862306a36Sopenharmony_ci#include "calib.h"
3962306a36Sopenharmony_ci#include "agn.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/******************************************************************************
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * module boiler plate
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci ******************************************************************************/
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define DRV_DESCRIPTION	"Intel(R) Wireless WiFi Link AGN driver for Linux"
4962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
5062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5162306a36Sopenharmony_ciMODULE_IMPORT_NS(IWLWIFI);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Please keep this array *SORTED* by hex value.
5462306a36Sopenharmony_ci * Access is done through binary search.
5562306a36Sopenharmony_ci * A warning will be triggered on violation.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cistatic const struct iwl_hcmd_names iwl_dvm_cmd_names[] = {
5862306a36Sopenharmony_ci	HCMD_NAME(REPLY_ALIVE),
5962306a36Sopenharmony_ci	HCMD_NAME(REPLY_ERROR),
6062306a36Sopenharmony_ci	HCMD_NAME(REPLY_ECHO),
6162306a36Sopenharmony_ci	HCMD_NAME(REPLY_RXON),
6262306a36Sopenharmony_ci	HCMD_NAME(REPLY_RXON_ASSOC),
6362306a36Sopenharmony_ci	HCMD_NAME(REPLY_QOS_PARAM),
6462306a36Sopenharmony_ci	HCMD_NAME(REPLY_RXON_TIMING),
6562306a36Sopenharmony_ci	HCMD_NAME(REPLY_ADD_STA),
6662306a36Sopenharmony_ci	HCMD_NAME(REPLY_REMOVE_STA),
6762306a36Sopenharmony_ci	HCMD_NAME(REPLY_REMOVE_ALL_STA),
6862306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX),
6962306a36Sopenharmony_ci	HCMD_NAME(REPLY_TXFIFO_FLUSH),
7062306a36Sopenharmony_ci	HCMD_NAME(REPLY_WEPKEY),
7162306a36Sopenharmony_ci	HCMD_NAME(REPLY_LEDS_CMD),
7262306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX_LINK_QUALITY_CMD),
7362306a36Sopenharmony_ci	HCMD_NAME(COEX_PRIORITY_TABLE_CMD),
7462306a36Sopenharmony_ci	HCMD_NAME(COEX_MEDIUM_NOTIFICATION),
7562306a36Sopenharmony_ci	HCMD_NAME(COEX_EVENT_CMD),
7662306a36Sopenharmony_ci	HCMD_NAME(TEMPERATURE_NOTIFICATION),
7762306a36Sopenharmony_ci	HCMD_NAME(CALIBRATION_CFG_CMD),
7862306a36Sopenharmony_ci	HCMD_NAME(CALIBRATION_RES_NOTIFICATION),
7962306a36Sopenharmony_ci	HCMD_NAME(CALIBRATION_COMPLETE_NOTIFICATION),
8062306a36Sopenharmony_ci	HCMD_NAME(REPLY_QUIET_CMD),
8162306a36Sopenharmony_ci	HCMD_NAME(REPLY_CHANNEL_SWITCH),
8262306a36Sopenharmony_ci	HCMD_NAME(CHANNEL_SWITCH_NOTIFICATION),
8362306a36Sopenharmony_ci	HCMD_NAME(REPLY_SPECTRUM_MEASUREMENT_CMD),
8462306a36Sopenharmony_ci	HCMD_NAME(SPECTRUM_MEASURE_NOTIFICATION),
8562306a36Sopenharmony_ci	HCMD_NAME(POWER_TABLE_CMD),
8662306a36Sopenharmony_ci	HCMD_NAME(PM_SLEEP_NOTIFICATION),
8762306a36Sopenharmony_ci	HCMD_NAME(PM_DEBUG_STATISTIC_NOTIFIC),
8862306a36Sopenharmony_ci	HCMD_NAME(REPLY_SCAN_CMD),
8962306a36Sopenharmony_ci	HCMD_NAME(REPLY_SCAN_ABORT_CMD),
9062306a36Sopenharmony_ci	HCMD_NAME(SCAN_START_NOTIFICATION),
9162306a36Sopenharmony_ci	HCMD_NAME(SCAN_RESULTS_NOTIFICATION),
9262306a36Sopenharmony_ci	HCMD_NAME(SCAN_COMPLETE_NOTIFICATION),
9362306a36Sopenharmony_ci	HCMD_NAME(BEACON_NOTIFICATION),
9462306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX_BEACON),
9562306a36Sopenharmony_ci	HCMD_NAME(WHO_IS_AWAKE_NOTIFICATION),
9662306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX_POWER_DBM_CMD),
9762306a36Sopenharmony_ci	HCMD_NAME(QUIET_NOTIFICATION),
9862306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX_PWR_TABLE_CMD),
9962306a36Sopenharmony_ci	HCMD_NAME(REPLY_TX_POWER_DBM_CMD_V1),
10062306a36Sopenharmony_ci	HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
10162306a36Sopenharmony_ci	HCMD_NAME(MEASURE_ABORT_NOTIFICATION),
10262306a36Sopenharmony_ci	HCMD_NAME(REPLY_BT_CONFIG),
10362306a36Sopenharmony_ci	HCMD_NAME(REPLY_STATISTICS_CMD),
10462306a36Sopenharmony_ci	HCMD_NAME(STATISTICS_NOTIFICATION),
10562306a36Sopenharmony_ci	HCMD_NAME(REPLY_CARD_STATE_CMD),
10662306a36Sopenharmony_ci	HCMD_NAME(CARD_STATE_NOTIFICATION),
10762306a36Sopenharmony_ci	HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
10862306a36Sopenharmony_ci	HCMD_NAME(REPLY_CT_KILL_CONFIG_CMD),
10962306a36Sopenharmony_ci	HCMD_NAME(SENSITIVITY_CMD),
11062306a36Sopenharmony_ci	HCMD_NAME(REPLY_PHY_CALIBRATION_CMD),
11162306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_PARAMS),
11262306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_RXON),
11362306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_RXON_TIMING),
11462306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_RXON_ASSOC),
11562306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_QOS_PARAM),
11662306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_WEPKEY),
11762306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_P2P_CHANNEL_SWITCH),
11862306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_NOA_NOTIFICATION),
11962306a36Sopenharmony_ci	HCMD_NAME(REPLY_WIPAN_DEACTIVATION_COMPLETE),
12062306a36Sopenharmony_ci	HCMD_NAME(REPLY_RX_PHY_CMD),
12162306a36Sopenharmony_ci	HCMD_NAME(REPLY_RX_MPDU_CMD),
12262306a36Sopenharmony_ci	HCMD_NAME(REPLY_RX),
12362306a36Sopenharmony_ci	HCMD_NAME(REPLY_COMPRESSED_BA),
12462306a36Sopenharmony_ci	HCMD_NAME(REPLY_BT_COEX_PRIO_TABLE),
12562306a36Sopenharmony_ci	HCMD_NAME(REPLY_BT_COEX_PROT_ENV),
12662306a36Sopenharmony_ci	HCMD_NAME(REPLY_BT_COEX_PROFILE_NOTIF),
12762306a36Sopenharmony_ci	HCMD_NAME(REPLY_D3_CONFIG),
12862306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_PATTERNS),
12962306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_WAKEUP_FILTER),
13062306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_TSC_RSC_PARAMS),
13162306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_TKIP_PARAMS),
13262306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_KEK_KCK_MATERIAL),
13362306a36Sopenharmony_ci	HCMD_NAME(REPLY_WOWLAN_GET_STATUS),
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const struct iwl_hcmd_arr iwl_dvm_groups[] = {
13762306a36Sopenharmony_ci	[0x0] = HCMD_ARR(iwl_dvm_cmd_names),
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic const struct iwl_op_mode_ops iwl_dvm_ops;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid iwl_update_chain_flags(struct iwl_priv *priv)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for_each_context(priv, ctx) {
14762306a36Sopenharmony_ci		iwlagn_set_rxon_chain(priv, ctx);
14862306a36Sopenharmony_ci		if (ctx->active.rx_chain != ctx->staging.rx_chain)
14962306a36Sopenharmony_ci			iwlagn_commit_rxon(priv, ctx);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
15462306a36Sopenharmony_cistatic void iwl_set_beacon_tim(struct iwl_priv *priv,
15562306a36Sopenharmony_ci			       struct iwl_tx_beacon_cmd *tx_beacon_cmd,
15662306a36Sopenharmony_ci			       u8 *beacon, u32 frame_size)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u16 tim_idx;
15962306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * The index is relative to frame start but we start looking at the
16362306a36Sopenharmony_ci	 * variable-length part of the beacon.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	tim_idx = mgmt->u.beacon.variable - beacon;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Parse variable-length elements of beacon to find WLAN_EID_TIM */
16862306a36Sopenharmony_ci	while ((tim_idx < (frame_size - 2)) &&
16962306a36Sopenharmony_ci			(beacon[tim_idx] != WLAN_EID_TIM))
17062306a36Sopenharmony_ci		tim_idx += beacon[tim_idx+1] + 2;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* If TIM field was found, set variables */
17362306a36Sopenharmony_ci	if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
17462306a36Sopenharmony_ci		tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
17562306a36Sopenharmony_ci		tx_beacon_cmd->tim_size = beacon[tim_idx+1];
17662306a36Sopenharmony_ci	} else
17762306a36Sopenharmony_ci		IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint iwlagn_send_beacon_cmd(struct iwl_priv *priv)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct iwl_tx_beacon_cmd *tx_beacon_cmd;
18362306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
18462306a36Sopenharmony_ci		.id = REPLY_TX_BEACON,
18562306a36Sopenharmony_ci	};
18662306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
18762306a36Sopenharmony_ci	u32 frame_size;
18862306a36Sopenharmony_ci	u32 rate_flags;
18962306a36Sopenharmony_ci	u32 rate;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/*
19262306a36Sopenharmony_ci	 * We have to set up the TX command, the TX Beacon command, and the
19362306a36Sopenharmony_ci	 * beacon contents.
19462306a36Sopenharmony_ci	 */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (!priv->beacon_ctx) {
19962306a36Sopenharmony_ci		IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (WARN_ON(!priv->beacon_skb))
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Allocate beacon command */
20762306a36Sopenharmony_ci	if (!priv->beacon_cmd)
20862306a36Sopenharmony_ci		priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL);
20962306a36Sopenharmony_ci	tx_beacon_cmd = priv->beacon_cmd;
21062306a36Sopenharmony_ci	if (!tx_beacon_cmd)
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	frame_size = priv->beacon_skb->len;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Set up TX command fields */
21662306a36Sopenharmony_ci	tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
21762306a36Sopenharmony_ci	tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id;
21862306a36Sopenharmony_ci	tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
21962306a36Sopenharmony_ci	tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
22062306a36Sopenharmony_ci		TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Set up TX beacon command fields */
22362306a36Sopenharmony_ci	iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data,
22462306a36Sopenharmony_ci			   frame_size);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Set up packet rate and flags */
22762306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(priv->beacon_skb);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Let's set up the rate at least somewhat correctly;
23162306a36Sopenharmony_ci	 * it will currently not actually be used by the uCode,
23262306a36Sopenharmony_ci	 * it uses the broadcast station's rate instead.
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci	if (info->control.rates[0].idx < 0 ||
23562306a36Sopenharmony_ci	    info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
23662306a36Sopenharmony_ci		rate = 0;
23762306a36Sopenharmony_ci	else
23862306a36Sopenharmony_ci		rate = info->control.rates[0].idx;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
24162306a36Sopenharmony_ci					      priv->nvm_data->valid_tx_ant);
24262306a36Sopenharmony_ci	rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* In mac80211, rates for 5 GHz start at 0 */
24562306a36Sopenharmony_ci	if (info->band == NL80211_BAND_5GHZ)
24662306a36Sopenharmony_ci		rate += IWL_FIRST_OFDM_RATE;
24762306a36Sopenharmony_ci	else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE)
24862306a36Sopenharmony_ci		rate_flags |= RATE_MCS_CCK_MSK;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	tx_beacon_cmd->tx.rate_n_flags =
25162306a36Sopenharmony_ci			iwl_hw_set_rate_n_flags(rate, rate_flags);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Submit command */
25462306a36Sopenharmony_ci	cmd.len[0] = sizeof(*tx_beacon_cmd);
25562306a36Sopenharmony_ci	cmd.data[0] = tx_beacon_cmd;
25662306a36Sopenharmony_ci	cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
25762306a36Sopenharmony_ci	cmd.len[1] = frame_size;
25862306a36Sopenharmony_ci	cmd.data[1] = priv->beacon_skb->data;
25962306a36Sopenharmony_ci	cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return iwl_dvm_send_cmd(priv, &cmd);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void iwl_bg_beacon_update(struct work_struct *work)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct iwl_priv *priv =
26762306a36Sopenharmony_ci		container_of(work, struct iwl_priv, beacon_update);
26862306a36Sopenharmony_ci	struct sk_buff *beacon;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
27162306a36Sopenharmony_ci	if (!priv->beacon_ctx) {
27262306a36Sopenharmony_ci		IWL_ERR(priv, "updating beacon w/o beacon context!\n");
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) {
27762306a36Sopenharmony_ci		/*
27862306a36Sopenharmony_ci		 * The ucode will send beacon notifications even in
27962306a36Sopenharmony_ci		 * IBSS mode, but we don't want to process them. But
28062306a36Sopenharmony_ci		 * we need to defer the type check to here due to
28162306a36Sopenharmony_ci		 * requiring locking around the beacon_ctx access.
28262306a36Sopenharmony_ci		 */
28362306a36Sopenharmony_ci		goto out;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Pull updated AP beacon from mac80211. will fail if not in AP mode */
28762306a36Sopenharmony_ci	beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif, 0);
28862306a36Sopenharmony_ci	if (!beacon) {
28962306a36Sopenharmony_ci		IWL_ERR(priv, "update beacon failed -- keeping old\n");
29062306a36Sopenharmony_ci		goto out;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* new beacon skb is allocated every time; dispose previous.*/
29462306a36Sopenharmony_ci	dev_kfree_skb(priv->beacon_skb);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	priv->beacon_skb = beacon;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	iwlagn_send_beacon_cmd(priv);
29962306a36Sopenharmony_ci out:
30062306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void iwl_bg_bt_runtime_config(struct work_struct *work)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct iwl_priv *priv =
30662306a36Sopenharmony_ci		container_of(work, struct iwl_priv, bt_runtime_config);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
30962306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
31062306a36Sopenharmony_ci		goto out;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* dont send host command if rf-kill is on */
31362306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
31462306a36Sopenharmony_ci		goto out;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	iwlagn_send_advance_bt_config(priv);
31762306a36Sopenharmony_ciout:
31862306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void iwl_bg_bt_full_concurrency(struct work_struct *work)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct iwl_priv *priv =
32462306a36Sopenharmony_ci		container_of(work, struct iwl_priv, bt_full_concurrency);
32562306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
33062306a36Sopenharmony_ci		goto out;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* dont send host command if rf-kill is on */
33362306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
33462306a36Sopenharmony_ci		goto out;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "BT coex in %s mode\n",
33762306a36Sopenharmony_ci		       priv->bt_full_concurrent ?
33862306a36Sopenharmony_ci		       "full concurrency" : "3-wire");
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * LQ & RXON updated cmds must be sent before BT Config cmd
34262306a36Sopenharmony_ci	 * to avoid 3-wire collisions
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	for_each_context(priv, ctx) {
34562306a36Sopenharmony_ci		iwlagn_set_rxon_chain(priv, ctx);
34662306a36Sopenharmony_ci		iwlagn_commit_rxon(priv, ctx);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	iwlagn_send_advance_bt_config(priv);
35062306a36Sopenharmony_ciout:
35162306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ciint iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct iwl_statistics_cmd statistics_cmd = {
35762306a36Sopenharmony_ci		.configuration_flags =
35862306a36Sopenharmony_ci			clear ? IWL_STATS_CONF_CLEAR_STATS : 0,
35962306a36Sopenharmony_ci	};
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (flags & CMD_ASYNC)
36262306a36Sopenharmony_ci		return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
36362306a36Sopenharmony_ci					CMD_ASYNC,
36462306a36Sopenharmony_ci					sizeof(struct iwl_statistics_cmd),
36562306a36Sopenharmony_ci					&statistics_cmd);
36662306a36Sopenharmony_ci	else
36762306a36Sopenharmony_ci		return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0,
36862306a36Sopenharmony_ci					sizeof(struct iwl_statistics_cmd),
36962306a36Sopenharmony_ci					&statistics_cmd);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * iwl_bg_statistics_periodic - Timer callback to queue statistics
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * This callback is provided in order to send a statistics request.
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * This timer function is continually reset to execute within
37862306a36Sopenharmony_ci * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION
37962306a36Sopenharmony_ci * was received.  We need to ensure we receive the statistics in order
38062306a36Sopenharmony_ci * to update the temperature used for calibrating the TXPOWER.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic void iwl_bg_statistics_periodic(struct timer_list *t)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct iwl_priv *priv = from_timer(priv, t, statistics_periodic);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
38762306a36Sopenharmony_ci		return;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* dont send host command if rf-kill is on */
39062306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	iwl_send_statistics_request(priv, CMD_ASYNC, false);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
39862306a36Sopenharmony_ci					u32 start_idx, u32 num_events,
39962306a36Sopenharmony_ci					u32 capacity, u32 mode)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	u32 i;
40262306a36Sopenharmony_ci	u32 ptr;        /* SRAM byte address of log data */
40362306a36Sopenharmony_ci	u32 ev, time, data; /* event log data */
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (mode == 0)
40662306a36Sopenharmony_ci		ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
40762306a36Sopenharmony_ci	else
40862306a36Sopenharmony_ci		ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Make sure device is powered up for SRAM reads */
41162306a36Sopenharmony_ci	if (!iwl_trans_grab_nic_access(priv->trans))
41262306a36Sopenharmony_ci		return;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* Set starting address; reads will auto-increment */
41562306a36Sopenharmony_ci	iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/*
41862306a36Sopenharmony_ci	 * Refuse to read more than would have fit into the log from
41962306a36Sopenharmony_ci	 * the current start_idx. This used to happen due to the race
42062306a36Sopenharmony_ci	 * described below, but now WARN because the code below should
42162306a36Sopenharmony_ci	 * prevent it from happening here.
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	if (WARN_ON(num_events > capacity - start_idx))
42462306a36Sopenharmony_ci		num_events = capacity - start_idx;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * "time" is actually "data" for mode 0 (no timestamp).
42862306a36Sopenharmony_ci	 * place event id # at far right for easier visual parsing.
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	for (i = 0; i < num_events; i++) {
43162306a36Sopenharmony_ci		ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
43262306a36Sopenharmony_ci		time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
43362306a36Sopenharmony_ci		if (mode == 0) {
43462306a36Sopenharmony_ci			trace_iwlwifi_dev_ucode_cont_event(
43562306a36Sopenharmony_ci					priv->trans->dev, 0, time, ev);
43662306a36Sopenharmony_ci		} else {
43762306a36Sopenharmony_ci			data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
43862306a36Sopenharmony_ci			trace_iwlwifi_dev_ucode_cont_event(
43962306a36Sopenharmony_ci					priv->trans->dev, time, data, ev);
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	/* Allow device to power down */
44362306a36Sopenharmony_ci	iwl_trans_release_nic_access(priv->trans);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic void iwl_continuous_event_trace(struct iwl_priv *priv)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	u32 capacity;   /* event log capacity in # entries */
44962306a36Sopenharmony_ci	struct {
45062306a36Sopenharmony_ci		u32 capacity;
45162306a36Sopenharmony_ci		u32 mode;
45262306a36Sopenharmony_ci		u32 wrap_counter;
45362306a36Sopenharmony_ci		u32 write_counter;
45462306a36Sopenharmony_ci	} __packed read;
45562306a36Sopenharmony_ci	u32 base;       /* SRAM byte address of event log header */
45662306a36Sopenharmony_ci	u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
45762306a36Sopenharmony_ci	u32 num_wraps;  /* # times uCode wrapped to top of log */
45862306a36Sopenharmony_ci	u32 next_entry; /* index of next entry to be written by uCode */
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	base = priv->device_pointers.log_event_table;
46162306a36Sopenharmony_ci	if (iwlagn_hw_valid_rtc_data_addr(base)) {
46262306a36Sopenharmony_ci		iwl_trans_read_mem_bytes(priv->trans, base,
46362306a36Sopenharmony_ci					 &read, sizeof(read));
46462306a36Sopenharmony_ci		capacity = read.capacity;
46562306a36Sopenharmony_ci		mode = read.mode;
46662306a36Sopenharmony_ci		num_wraps = read.wrap_counter;
46762306a36Sopenharmony_ci		next_entry = read.write_counter;
46862306a36Sopenharmony_ci	} else
46962306a36Sopenharmony_ci		return;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/*
47262306a36Sopenharmony_ci	 * Unfortunately, the uCode doesn't use temporary variables.
47362306a36Sopenharmony_ci	 * Therefore, it can happen that we read next_entry == capacity,
47462306a36Sopenharmony_ci	 * which really means next_entry == 0.
47562306a36Sopenharmony_ci	 */
47662306a36Sopenharmony_ci	if (unlikely(next_entry == capacity))
47762306a36Sopenharmony_ci		next_entry = 0;
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * Additionally, the uCode increases the write pointer before
48062306a36Sopenharmony_ci	 * the wraps counter, so if the write pointer is smaller than
48162306a36Sopenharmony_ci	 * the old write pointer (wrap occurred) but we read that no
48262306a36Sopenharmony_ci	 * wrap occurred, we actually read between the next_entry and
48362306a36Sopenharmony_ci	 * num_wraps update (this does happen in practice!!) -- take
48462306a36Sopenharmony_ci	 * that into account by increasing num_wraps.
48562306a36Sopenharmony_ci	 */
48662306a36Sopenharmony_ci	if (unlikely(next_entry < priv->event_log.next_entry &&
48762306a36Sopenharmony_ci		     num_wraps == priv->event_log.num_wraps))
48862306a36Sopenharmony_ci		num_wraps++;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (num_wraps == priv->event_log.num_wraps) {
49162306a36Sopenharmony_ci		iwl_print_cont_event_trace(
49262306a36Sopenharmony_ci			priv, base, priv->event_log.next_entry,
49362306a36Sopenharmony_ci			next_entry - priv->event_log.next_entry,
49462306a36Sopenharmony_ci			capacity, mode);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		priv->event_log.non_wraps_count++;
49762306a36Sopenharmony_ci	} else {
49862306a36Sopenharmony_ci		if (num_wraps - priv->event_log.num_wraps > 1)
49962306a36Sopenharmony_ci			priv->event_log.wraps_more_count++;
50062306a36Sopenharmony_ci		else
50162306a36Sopenharmony_ci			priv->event_log.wraps_once_count++;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev,
50462306a36Sopenharmony_ci				num_wraps - priv->event_log.num_wraps,
50562306a36Sopenharmony_ci				next_entry, priv->event_log.next_entry);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		if (next_entry < priv->event_log.next_entry) {
50862306a36Sopenharmony_ci			iwl_print_cont_event_trace(
50962306a36Sopenharmony_ci				priv, base, priv->event_log.next_entry,
51062306a36Sopenharmony_ci				capacity - priv->event_log.next_entry,
51162306a36Sopenharmony_ci				capacity, mode);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci			iwl_print_cont_event_trace(
51462306a36Sopenharmony_ci				priv, base, 0, next_entry, capacity, mode);
51562306a36Sopenharmony_ci		} else {
51662306a36Sopenharmony_ci			iwl_print_cont_event_trace(
51762306a36Sopenharmony_ci				priv, base, next_entry,
51862306a36Sopenharmony_ci				capacity - next_entry,
51962306a36Sopenharmony_ci				capacity, mode);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci			iwl_print_cont_event_trace(
52262306a36Sopenharmony_ci				priv, base, 0, next_entry, capacity, mode);
52362306a36Sopenharmony_ci		}
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	priv->event_log.num_wraps = num_wraps;
52762306a36Sopenharmony_ci	priv->event_log.next_entry = next_entry;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/*
53162306a36Sopenharmony_ci * iwl_bg_ucode_trace - Timer callback to log ucode event
53262306a36Sopenharmony_ci *
53362306a36Sopenharmony_ci * The timer is continually set to execute every
53462306a36Sopenharmony_ci * UCODE_TRACE_PERIOD milliseconds after the last timer expired
53562306a36Sopenharmony_ci * this function is to perform continuous uCode event logging operation
53662306a36Sopenharmony_ci * if enabled
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic void iwl_bg_ucode_trace(struct timer_list *t)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct iwl_priv *priv = from_timer(priv, t, ucode_trace);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
54362306a36Sopenharmony_ci		return;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (priv->event_log.ucode_trace) {
54662306a36Sopenharmony_ci		iwl_continuous_event_trace(priv);
54762306a36Sopenharmony_ci		/* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
54862306a36Sopenharmony_ci		mod_timer(&priv->ucode_trace,
54962306a36Sopenharmony_ci			 jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic void iwl_bg_tx_flush(struct work_struct *work)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct iwl_priv *priv =
55662306a36Sopenharmony_ci		container_of(work, struct iwl_priv, tx_flush);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
55962306a36Sopenharmony_ci		return;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* do nothing if rf-kill is on */
56262306a36Sopenharmony_ci	if (!iwl_is_ready_rf(priv))
56362306a36Sopenharmony_ci		return;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
56662306a36Sopenharmony_ci	iwlagn_dev_txfifo_flush(priv);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*
57062306a36Sopenharmony_ci * queue/FIFO/AC mapping definitions
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic const u8 iwlagn_bss_ac_to_fifo[] = {
57462306a36Sopenharmony_ci	IWL_TX_FIFO_VO,
57562306a36Sopenharmony_ci	IWL_TX_FIFO_VI,
57662306a36Sopenharmony_ci	IWL_TX_FIFO_BE,
57762306a36Sopenharmony_ci	IWL_TX_FIFO_BK,
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic const u8 iwlagn_bss_ac_to_queue[] = {
58162306a36Sopenharmony_ci	0, 1, 2, 3,
58262306a36Sopenharmony_ci};
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic const u8 iwlagn_pan_ac_to_fifo[] = {
58562306a36Sopenharmony_ci	IWL_TX_FIFO_VO_IPAN,
58662306a36Sopenharmony_ci	IWL_TX_FIFO_VI_IPAN,
58762306a36Sopenharmony_ci	IWL_TX_FIFO_BE_IPAN,
58862306a36Sopenharmony_ci	IWL_TX_FIFO_BK_IPAN,
58962306a36Sopenharmony_ci};
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic const u8 iwlagn_pan_ac_to_queue[] = {
59262306a36Sopenharmony_ci	7, 6, 5, 4,
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	int i;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/*
60062306a36Sopenharmony_ci	 * The default context is always valid,
60162306a36Sopenharmony_ci	 * the PAN context depends on uCode.
60262306a36Sopenharmony_ci	 */
60362306a36Sopenharmony_ci	priv->valid_contexts = BIT(IWL_RXON_CTX_BSS);
60462306a36Sopenharmony_ci	if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
60562306a36Sopenharmony_ci		priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	for (i = 0; i < NUM_IWL_RXON_CTX; i++)
60862306a36Sopenharmony_ci		priv->contexts[i].ctxid = i;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
61162306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
61262306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
61362306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
61462306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
61562306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM;
61662306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID;
61762306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY;
61862306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID;
61962306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes =
62062306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR);
62162306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].interface_modes =
62262306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_STATION);
62362306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP;
62462306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
62562306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
62662306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
62762306a36Sopenharmony_ci	memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue,
62862306a36Sopenharmony_ci	       iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue));
62962306a36Sopenharmony_ci	memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo,
63062306a36Sopenharmony_ci	       iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo));
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
63362306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd =
63462306a36Sopenharmony_ci		REPLY_WIPAN_RXON_TIMING;
63562306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd =
63662306a36Sopenharmony_ci		REPLY_WIPAN_RXON_ASSOC;
63762306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM;
63862306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN;
63962306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY;
64062306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID;
64162306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION;
64262306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
64362306a36Sopenharmony_ci		BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
64662306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
64762306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
64862306a36Sopenharmony_ci	memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue,
64962306a36Sopenharmony_ci	       iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue));
65062306a36Sopenharmony_ci	memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo,
65162306a36Sopenharmony_ci	       iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo));
65262306a36Sopenharmony_ci	priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void iwl_rf_kill_ct_config(struct iwl_priv *priv)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct iwl_ct_kill_config cmd;
66062306a36Sopenharmony_ci	struct iwl_ct_kill_throttling_config adv_cmd;
66162306a36Sopenharmony_ci	int ret = 0;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
66462306a36Sopenharmony_ci		    CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	priv->thermal_throttle.ct_kill_toggle = false;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (priv->lib->support_ct_kill_exit) {
66962306a36Sopenharmony_ci		adv_cmd.critical_temperature_enter =
67062306a36Sopenharmony_ci			cpu_to_le32(priv->hw_params.ct_kill_threshold);
67162306a36Sopenharmony_ci		adv_cmd.critical_temperature_exit =
67262306a36Sopenharmony_ci			cpu_to_le32(priv->hw_params.ct_kill_exit_threshold);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		ret = iwl_dvm_send_cmd_pdu(priv,
67562306a36Sopenharmony_ci				       REPLY_CT_KILL_CONFIG_CMD,
67662306a36Sopenharmony_ci				       0, sizeof(adv_cmd), &adv_cmd);
67762306a36Sopenharmony_ci		if (ret)
67862306a36Sopenharmony_ci			IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
67962306a36Sopenharmony_ci		else
68062306a36Sopenharmony_ci			IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
68162306a36Sopenharmony_ci				"succeeded, critical temperature enter is %d,"
68262306a36Sopenharmony_ci				"exit is %d\n",
68362306a36Sopenharmony_ci				priv->hw_params.ct_kill_threshold,
68462306a36Sopenharmony_ci				priv->hw_params.ct_kill_exit_threshold);
68562306a36Sopenharmony_ci	} else {
68662306a36Sopenharmony_ci		cmd.critical_temperature_R =
68762306a36Sopenharmony_ci			cpu_to_le32(priv->hw_params.ct_kill_threshold);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		ret = iwl_dvm_send_cmd_pdu(priv,
69062306a36Sopenharmony_ci				       REPLY_CT_KILL_CONFIG_CMD,
69162306a36Sopenharmony_ci				       0, sizeof(cmd), &cmd);
69262306a36Sopenharmony_ci		if (ret)
69362306a36Sopenharmony_ci			IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
69462306a36Sopenharmony_ci		else
69562306a36Sopenharmony_ci			IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD "
69662306a36Sopenharmony_ci				"succeeded, "
69762306a36Sopenharmony_ci				"critical temperature is %d\n",
69862306a36Sopenharmony_ci				priv->hw_params.ct_kill_threshold);
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct iwl_calib_cfg_cmd calib_cfg_cmd;
70562306a36Sopenharmony_ci	struct iwl_host_cmd cmd = {
70662306a36Sopenharmony_ci		.id = CALIBRATION_CFG_CMD,
70762306a36Sopenharmony_ci		.len = { sizeof(struct iwl_calib_cfg_cmd), },
70862306a36Sopenharmony_ci		.data = { &calib_cfg_cmd, },
70962306a36Sopenharmony_ci	};
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
71262306a36Sopenharmony_ci	calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL;
71362306a36Sopenharmony_ci	calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return iwl_dvm_send_cmd(priv, &cmd);
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct iwl_tx_ant_config_cmd tx_ant_cmd = {
72262306a36Sopenharmony_ci	  .valid = cpu_to_le32(valid_tx_ant),
72362306a36Sopenharmony_ci	};
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) {
72662306a36Sopenharmony_ci		IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
72762306a36Sopenharmony_ci		return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0,
72862306a36Sopenharmony_ci					sizeof(struct iwl_tx_ant_config_cmd),
72962306a36Sopenharmony_ci					&tx_ant_cmd);
73062306a36Sopenharmony_ci	} else {
73162306a36Sopenharmony_ci		IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
73262306a36Sopenharmony_ci		return -EOPNOTSUPP;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic void iwl_send_bt_config(struct iwl_priv *priv)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct iwl_bt_cmd bt_cmd = {
73962306a36Sopenharmony_ci		.lead_time = BT_LEAD_TIME_DEF,
74062306a36Sopenharmony_ci		.max_kill = BT_MAX_KILL_DEF,
74162306a36Sopenharmony_ci		.kill_ack_mask = 0,
74262306a36Sopenharmony_ci		.kill_cts_mask = 0,
74362306a36Sopenharmony_ci	};
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (!iwlwifi_mod_params.bt_coex_active)
74662306a36Sopenharmony_ci		bt_cmd.flags = BT_COEX_DISABLE;
74762306a36Sopenharmony_ci	else
74862306a36Sopenharmony_ci		bt_cmd.flags = BT_COEX_ENABLE;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	priv->bt_enable_flag = bt_cmd.flags;
75162306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "BT coex %s\n",
75262306a36Sopenharmony_ci		(bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
75562306a36Sopenharmony_ci			     0, sizeof(struct iwl_bt_cmd), &bt_cmd))
75662306a36Sopenharmony_ci		IWL_ERR(priv, "failed to send BT Coex Config\n");
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci/*
76062306a36Sopenharmony_ci * iwl_alive_start - called after REPLY_ALIVE notification received
76162306a36Sopenharmony_ci *                   from protocol/runtime uCode (initialization uCode's
76262306a36Sopenharmony_ci *                   Alive gets handled by iwl_init_alive_start()).
76362306a36Sopenharmony_ci */
76462306a36Sopenharmony_ciint iwl_alive_start(struct iwl_priv *priv)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	int ret = 0;
76762306a36Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* After the ALIVE response, we can send host commands to the uCode */
77262306a36Sopenharmony_ci	set_bit(STATUS_ALIVE, &priv->status);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (iwl_is_rfkill(priv))
77562306a36Sopenharmony_ci		return -ERFKILL;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (priv->event_log.ucode_trace) {
77862306a36Sopenharmony_ci		/* start collecting data now */
77962306a36Sopenharmony_ci		mod_timer(&priv->ucode_trace, jiffies);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* download priority table before any calibration request */
78362306a36Sopenharmony_ci	if (priv->lib->bt_params &&
78462306a36Sopenharmony_ci	    priv->lib->bt_params->advanced_bt_coexist) {
78562306a36Sopenharmony_ci		/* Configure Bluetooth device coexistence support */
78662306a36Sopenharmony_ci		if (priv->lib->bt_params->bt_sco_disable)
78762306a36Sopenharmony_ci			priv->bt_enable_pspoll = false;
78862306a36Sopenharmony_ci		else
78962306a36Sopenharmony_ci			priv->bt_enable_pspoll = true;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
79262306a36Sopenharmony_ci		priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
79362306a36Sopenharmony_ci		priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
79462306a36Sopenharmony_ci		iwlagn_send_advance_bt_config(priv);
79562306a36Sopenharmony_ci		priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
79662306a36Sopenharmony_ci		priv->cur_rssi_ctx = NULL;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		iwl_send_prio_tbl(priv);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		/* FIXME: w/a to force change uCode BT state machine */
80162306a36Sopenharmony_ci		ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
80262306a36Sopenharmony_ci					 BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
80362306a36Sopenharmony_ci		if (ret)
80462306a36Sopenharmony_ci			return ret;
80562306a36Sopenharmony_ci		ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
80662306a36Sopenharmony_ci					 BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
80762306a36Sopenharmony_ci		if (ret)
80862306a36Sopenharmony_ci			return ret;
80962306a36Sopenharmony_ci	} else if (priv->lib->bt_params) {
81062306a36Sopenharmony_ci		/*
81162306a36Sopenharmony_ci		 * default is 2-wire BT coexexistence support
81262306a36Sopenharmony_ci		 */
81362306a36Sopenharmony_ci		iwl_send_bt_config(priv);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/*
81762306a36Sopenharmony_ci	 * Perform runtime calibrations, including DC calibration.
81862306a36Sopenharmony_ci	 */
81962306a36Sopenharmony_ci	iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	ieee80211_wake_queues(priv->hw);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* Configure Tx antenna selection based on H/W config */
82462306a36Sopenharmony_ci	iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (iwl_is_associated_ctx(ctx) && !priv->wowlan) {
82762306a36Sopenharmony_ci		struct iwl_rxon_cmd *active_rxon =
82862306a36Sopenharmony_ci				(struct iwl_rxon_cmd *)&ctx->active;
82962306a36Sopenharmony_ci		/* apply any changes in staging */
83062306a36Sopenharmony_ci		ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
83162306a36Sopenharmony_ci		active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
83262306a36Sopenharmony_ci	} else {
83362306a36Sopenharmony_ci		struct iwl_rxon_context *tmp;
83462306a36Sopenharmony_ci		/* Initialize our rx_config data */
83562306a36Sopenharmony_ci		for_each_context(priv, tmp)
83662306a36Sopenharmony_ci			iwl_connection_init_rx_config(priv, tmp);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		iwlagn_set_rxon_chain(priv, ctx);
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!priv->wowlan) {
84262306a36Sopenharmony_ci		/* WoWLAN ucode will not reply in the same way, skip it */
84362306a36Sopenharmony_ci		iwl_reset_run_time_calib(priv);
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	set_bit(STATUS_READY, &priv->status);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* Configure the adapter for unassociated operation */
84962306a36Sopenharmony_ci	ret = iwlagn_commit_rxon(priv, ctx);
85062306a36Sopenharmony_ci	if (ret)
85162306a36Sopenharmony_ci		return ret;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* At this point, the NIC is initialized and operational */
85462306a36Sopenharmony_ci	iwl_rf_kill_ct_config(priv);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return iwl_power_update_mode(priv, true);
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci/**
86262306a36Sopenharmony_ci * iwl_clear_driver_stations - clear knowledge of all stations from driver
86362306a36Sopenharmony_ci * @priv: iwl priv struct
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * This is called during iwl_down() to make sure that in the case
86662306a36Sopenharmony_ci * we're coming there from a hardware restart mac80211 will be
86762306a36Sopenharmony_ci * able to reconfigure stations -- if we're getting there in the
86862306a36Sopenharmony_ci * normal down flow then the stations will already be cleared.
86962306a36Sopenharmony_ci */
87062306a36Sopenharmony_cistatic void iwl_clear_driver_stations(struct iwl_priv *priv)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct iwl_rxon_context *ctx;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	spin_lock_bh(&priv->sta_lock);
87562306a36Sopenharmony_ci	memset(priv->stations, 0, sizeof(priv->stations));
87662306a36Sopenharmony_ci	priv->num_stations = 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	priv->ucode_key_table = 0;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	for_each_context(priv, ctx) {
88162306a36Sopenharmony_ci		/*
88262306a36Sopenharmony_ci		 * Remove all key information that is not stored as part
88362306a36Sopenharmony_ci		 * of station information since mac80211 may not have had
88462306a36Sopenharmony_ci		 * a chance to remove all the keys. When device is
88562306a36Sopenharmony_ci		 * reconfigured by mac80211 after an error all keys will
88662306a36Sopenharmony_ci		 * be reconfigured.
88762306a36Sopenharmony_ci		 */
88862306a36Sopenharmony_ci		memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys));
88962306a36Sopenharmony_ci		ctx->key_mapping_keys = 0;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	spin_unlock_bh(&priv->sta_lock);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_civoid iwl_down(struct iwl_priv *priv)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	int exit_pending;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	iwl_scan_cancel_timeout(priv, 200);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	exit_pending =
90662306a36Sopenharmony_ci		test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	iwl_clear_ucode_stations(priv, NULL);
90962306a36Sopenharmony_ci	iwl_dealloc_bcast_stations(priv);
91062306a36Sopenharmony_ci	iwl_clear_driver_stations(priv);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/* reset BT coex data */
91362306a36Sopenharmony_ci	priv->bt_status = 0;
91462306a36Sopenharmony_ci	priv->cur_rssi_ctx = NULL;
91562306a36Sopenharmony_ci	priv->bt_is_sco = 0;
91662306a36Sopenharmony_ci	if (priv->lib->bt_params)
91762306a36Sopenharmony_ci		priv->bt_traffic_load =
91862306a36Sopenharmony_ci			 priv->lib->bt_params->bt_init_traffic_load;
91962306a36Sopenharmony_ci	else
92062306a36Sopenharmony_ci		priv->bt_traffic_load = 0;
92162306a36Sopenharmony_ci	priv->bt_full_concurrent = false;
92262306a36Sopenharmony_ci	priv->bt_ci_compliance = 0;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	/* Wipe out the EXIT_PENDING status bit if we are not actually
92562306a36Sopenharmony_ci	 * exiting the module */
92662306a36Sopenharmony_ci	if (!exit_pending)
92762306a36Sopenharmony_ci		clear_bit(STATUS_EXIT_PENDING, &priv->status);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (priv->mac80211_registered)
93062306a36Sopenharmony_ci		ieee80211_stop_queues(priv->hw);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	priv->ucode_loaded = false;
93362306a36Sopenharmony_ci	iwl_trans_stop_device(priv->trans);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* Set num_aux_in_flight must be done after the transport is stopped */
93662306a36Sopenharmony_ci	atomic_set(&priv->num_aux_in_flight, 0);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	/* Clear out all status bits but a few that are stable across reset */
93962306a36Sopenharmony_ci	priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) <<
94062306a36Sopenharmony_ci				STATUS_RF_KILL_HW |
94162306a36Sopenharmony_ci			test_bit(STATUS_FW_ERROR, &priv->status) <<
94262306a36Sopenharmony_ci				STATUS_FW_ERROR |
94362306a36Sopenharmony_ci			test_bit(STATUS_EXIT_PENDING, &priv->status) <<
94462306a36Sopenharmony_ci				STATUS_EXIT_PENDING;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	dev_kfree_skb(priv->beacon_skb);
94762306a36Sopenharmony_ci	priv->beacon_skb = NULL;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci/*****************************************************************************
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * Workqueue callbacks
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci *****************************************************************************/
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void iwl_bg_run_time_calib_work(struct work_struct *work)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct iwl_priv *priv = container_of(work, struct iwl_priv,
95962306a36Sopenharmony_ci			run_time_calib_work);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
96462306a36Sopenharmony_ci	    test_bit(STATUS_SCANNING, &priv->status)) {
96562306a36Sopenharmony_ci		mutex_unlock(&priv->mutex);
96662306a36Sopenharmony_ci		return;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (priv->start_calib) {
97062306a36Sopenharmony_ci		iwl_chain_noise_calibration(priv);
97162306a36Sopenharmony_ci		iwl_sensitivity_calibration(priv);
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_civoid iwlagn_prepare_restart(struct iwl_priv *priv)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	bool bt_full_concurrent;
98062306a36Sopenharmony_ci	u8 bt_ci_compliance;
98162306a36Sopenharmony_ci	u8 bt_load;
98262306a36Sopenharmony_ci	u8 bt_status;
98362306a36Sopenharmony_ci	bool bt_is_sco;
98462306a36Sopenharmony_ci	int i;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	lockdep_assert_held(&priv->mutex);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	priv->is_open = 0;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/*
99162306a36Sopenharmony_ci	 * __iwl_down() will clear the BT status variables,
99262306a36Sopenharmony_ci	 * which is correct, but when we restart we really
99362306a36Sopenharmony_ci	 * want to keep them so restore them afterwards.
99462306a36Sopenharmony_ci	 *
99562306a36Sopenharmony_ci	 * The restart process will later pick them up and
99662306a36Sopenharmony_ci	 * re-configure the hw when we reconfigure the BT
99762306a36Sopenharmony_ci	 * command.
99862306a36Sopenharmony_ci	 */
99962306a36Sopenharmony_ci	bt_full_concurrent = priv->bt_full_concurrent;
100062306a36Sopenharmony_ci	bt_ci_compliance = priv->bt_ci_compliance;
100162306a36Sopenharmony_ci	bt_load = priv->bt_traffic_load;
100262306a36Sopenharmony_ci	bt_status = priv->bt_status;
100362306a36Sopenharmony_ci	bt_is_sco = priv->bt_is_sco;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	iwl_down(priv);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	priv->bt_full_concurrent = bt_full_concurrent;
100862306a36Sopenharmony_ci	priv->bt_ci_compliance = bt_ci_compliance;
100962306a36Sopenharmony_ci	priv->bt_traffic_load = bt_load;
101062306a36Sopenharmony_ci	priv->bt_status = bt_status;
101162306a36Sopenharmony_ci	priv->bt_is_sco = bt_is_sco;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* reset aggregation queues */
101462306a36Sopenharmony_ci	for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++)
101562306a36Sopenharmony_ci		priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
101662306a36Sopenharmony_ci	/* and stop counts */
101762306a36Sopenharmony_ci	for (i = 0; i < IWL_MAX_HW_QUEUES; i++)
101862306a36Sopenharmony_ci		atomic_set(&priv->queue_stop_count[i], 0);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc));
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic void iwl_bg_restart(struct work_struct *data)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
102862306a36Sopenharmony_ci		return;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
103162306a36Sopenharmony_ci		mutex_lock(&priv->mutex);
103262306a36Sopenharmony_ci		iwlagn_prepare_restart(priv);
103362306a36Sopenharmony_ci		mutex_unlock(&priv->mutex);
103462306a36Sopenharmony_ci		iwl_cancel_deferred_work(priv);
103562306a36Sopenharmony_ci		if (priv->mac80211_registered)
103662306a36Sopenharmony_ci			ieee80211_restart_hw(priv->hw);
103762306a36Sopenharmony_ci		else
103862306a36Sopenharmony_ci			IWL_ERR(priv,
103962306a36Sopenharmony_ci				"Cannot request restart before registering with mac80211\n");
104062306a36Sopenharmony_ci	} else {
104162306a36Sopenharmony_ci		WARN_ON(1);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/*****************************************************************************
104662306a36Sopenharmony_ci *
104762306a36Sopenharmony_ci * driver setup and teardown
104862306a36Sopenharmony_ci *
104962306a36Sopenharmony_ci *****************************************************************************/
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic void iwl_setup_deferred_work(struct iwl_priv *priv)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	priv->workqueue = alloc_ordered_workqueue(DRV_NAME, 0);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	INIT_WORK(&priv->restart, iwl_bg_restart);
105662306a36Sopenharmony_ci	INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
105762306a36Sopenharmony_ci	INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
105862306a36Sopenharmony_ci	INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
105962306a36Sopenharmony_ci	INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
106062306a36Sopenharmony_ci	INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	iwl_setup_scan_deferred_work(priv);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (priv->lib->bt_params)
106562306a36Sopenharmony_ci		iwlagn_bt_setup_deferred_work(priv);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	timer_setup(&priv->statistics_periodic, iwl_bg_statistics_periodic, 0);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	timer_setup(&priv->ucode_trace, iwl_bg_ucode_trace, 0);
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_civoid iwl_cancel_deferred_work(struct iwl_priv *priv)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	if (priv->lib->bt_params)
107562306a36Sopenharmony_ci		iwlagn_bt_cancel_deferred_work(priv);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	cancel_work_sync(&priv->run_time_calib_work);
107862306a36Sopenharmony_ci	cancel_work_sync(&priv->beacon_update);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	iwl_cancel_scan_deferred_work(priv);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	cancel_work_sync(&priv->bt_full_concurrency);
108362306a36Sopenharmony_ci	cancel_work_sync(&priv->bt_runtime_config);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	del_timer_sync(&priv->statistics_periodic);
108662306a36Sopenharmony_ci	del_timer_sync(&priv->ucode_trace);
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int iwl_init_drv(struct iwl_priv *priv)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	spin_lock_init(&priv->sta_lock);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	mutex_init(&priv->mutex);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->calib_results);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	priv->band = NL80211_BAND_2GHZ;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	priv->iw_mode = NL80211_IFTYPE_STATION;
110262306a36Sopenharmony_ci	priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
110362306a36Sopenharmony_ci	priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
110462306a36Sopenharmony_ci	priv->agg_tids_count = 0;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	priv->rx_statistics_jiffies = jiffies;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	/* Choose which receivers/antennas to use */
110962306a36Sopenharmony_ci	iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	iwl_init_scan_params(priv);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* init bt coex */
111462306a36Sopenharmony_ci	if (priv->lib->bt_params &&
111562306a36Sopenharmony_ci	    priv->lib->bt_params->advanced_bt_coexist) {
111662306a36Sopenharmony_ci		priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
111762306a36Sopenharmony_ci		priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
111862306a36Sopenharmony_ci		priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
111962306a36Sopenharmony_ci		priv->bt_on_thresh = BT_ON_THRESHOLD_DEF;
112062306a36Sopenharmony_ci		priv->bt_duration = BT_DURATION_LIMIT_DEF;
112162306a36Sopenharmony_ci		priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistatic void iwl_uninit_drv(struct iwl_priv *priv)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	kfree(priv->scan_cmd);
113062306a36Sopenharmony_ci	kfree(priv->beacon_cmd);
113162306a36Sopenharmony_ci	kfree(rcu_dereference_raw(priv->noa_data));
113262306a36Sopenharmony_ci	iwl_calib_free_results(priv);
113362306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
113462306a36Sopenharmony_ci	kfree(priv->wowlan_sram);
113562306a36Sopenharmony_ci#endif
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic void iwl_set_hw_params(struct iwl_priv *priv)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	if (priv->cfg->ht_params)
114162306a36Sopenharmony_ci		priv->hw_params.use_rts_for_aggregation =
114262306a36Sopenharmony_ci			priv->cfg->ht_params->use_rts_for_aggregation;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/* Device-specific setup */
114562306a36Sopenharmony_ci	priv->lib->set_hw_params(priv);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/* show what optional capabilities we have */
115162306a36Sopenharmony_cistatic void iwl_option_config(struct iwl_priv *priv)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG
115462306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n");
115562306a36Sopenharmony_ci#else
115662306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n");
115762306a36Sopenharmony_ci#endif
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
116062306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n");
116162306a36Sopenharmony_ci#else
116262306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n");
116362306a36Sopenharmony_ci#endif
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEVICE_TRACING
116662306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n");
116762306a36Sopenharmony_ci#else
116862306a36Sopenharmony_ci	IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
116962306a36Sopenharmony_ci#endif
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct iwl_nvm_data *data = priv->nvm_data;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (data->sku_cap_11n_enable &&
117762306a36Sopenharmony_ci	    !priv->cfg->ht_params) {
117862306a36Sopenharmony_ci		IWL_ERR(priv, "Invalid 11n configuration\n");
117962306a36Sopenharmony_ci		return -EINVAL;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (!data->sku_cap_11n_enable && !data->sku_cap_band_24ghz_enable &&
118362306a36Sopenharmony_ci	    !data->sku_cap_band_52ghz_enable) {
118462306a36Sopenharmony_ci		IWL_ERR(priv, "Invalid device sku\n");
118562306a36Sopenharmony_ci		return -EINVAL;
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv,
118962306a36Sopenharmony_ci		       "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
119062306a36Sopenharmony_ci		       data->sku_cap_band_24ghz_enable ? "" : "NOT", "enabled",
119162306a36Sopenharmony_ci		       data->sku_cap_band_52ghz_enable ? "" : "NOT", "enabled",
119262306a36Sopenharmony_ci		       data->sku_cap_11n_enable ? "" : "NOT", "enabled");
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	priv->hw_params.tx_chains_num =
119562306a36Sopenharmony_ci		num_of_ant(data->valid_tx_ant);
119662306a36Sopenharmony_ci	if (priv->cfg->rx_with_siso_diversity)
119762306a36Sopenharmony_ci		priv->hw_params.rx_chains_num = 1;
119862306a36Sopenharmony_ci	else
119962306a36Sopenharmony_ci		priv->hw_params.rx_chains_num =
120062306a36Sopenharmony_ci			num_of_ant(data->valid_rx_ant);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n",
120362306a36Sopenharmony_ci		       data->valid_tx_ant,
120462306a36Sopenharmony_ci		       data->valid_rx_ant);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return 0;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic int iwl_nvm_check_version(struct iwl_nvm_data *data,
121062306a36Sopenharmony_ci				 struct iwl_trans *trans)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	if (data->nvm_version >= trans->cfg->nvm_ver ||
121362306a36Sopenharmony_ci	    data->calib_version >= trans->cfg->nvm_calib_ver) {
121462306a36Sopenharmony_ci		IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n",
121562306a36Sopenharmony_ci			       data->nvm_version, data->calib_version);
121662306a36Sopenharmony_ci		return 0;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	IWL_ERR(trans,
122062306a36Sopenharmony_ci		"Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
122162306a36Sopenharmony_ci		data->nvm_version, trans->cfg->nvm_ver,
122262306a36Sopenharmony_ci		data->calib_version,  trans->cfg->nvm_calib_ver);
122362306a36Sopenharmony_ci	return -EINVAL;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
122762306a36Sopenharmony_ci						 const struct iwl_cfg *cfg,
122862306a36Sopenharmony_ci						 const struct iwl_fw *fw,
122962306a36Sopenharmony_ci						 struct dentry *dbgfs_dir)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct iwl_priv *priv;
123262306a36Sopenharmony_ci	struct ieee80211_hw *hw;
123362306a36Sopenharmony_ci	struct iwl_op_mode *op_mode;
123462306a36Sopenharmony_ci	u16 num_mac;
123562306a36Sopenharmony_ci	u32 ucode_flags;
123662306a36Sopenharmony_ci	struct iwl_trans_config trans_cfg = {};
123762306a36Sopenharmony_ci	static const u8 no_reclaim_cmds[] = {
123862306a36Sopenharmony_ci		REPLY_RX_PHY_CMD,
123962306a36Sopenharmony_ci		REPLY_RX_MPDU_CMD,
124062306a36Sopenharmony_ci		REPLY_COMPRESSED_BA,
124162306a36Sopenharmony_ci		STATISTICS_NOTIFICATION,
124262306a36Sopenharmony_ci		REPLY_TX,
124362306a36Sopenharmony_ci	};
124462306a36Sopenharmony_ci	int i;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/************************
124762306a36Sopenharmony_ci	 * 1. Allocating HW data
124862306a36Sopenharmony_ci	 ************************/
124962306a36Sopenharmony_ci	hw = iwl_alloc_all();
125062306a36Sopenharmony_ci	if (!hw) {
125162306a36Sopenharmony_ci		pr_err("%s: Cannot allocate network device\n", trans->name);
125262306a36Sopenharmony_ci		goto out;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	op_mode = hw->priv;
125662306a36Sopenharmony_ci	op_mode->ops = &iwl_dvm_ops;
125762306a36Sopenharmony_ci	priv = IWL_OP_MODE_GET_DVM(op_mode);
125862306a36Sopenharmony_ci	priv->trans = trans;
125962306a36Sopenharmony_ci	priv->dev = trans->dev;
126062306a36Sopenharmony_ci	priv->cfg = cfg;
126162306a36Sopenharmony_ci	priv->fw = fw;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	switch (priv->trans->trans_cfg->device_family) {
126462306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_1000:
126562306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_100:
126662306a36Sopenharmony_ci		priv->lib = &iwl_dvm_1000_cfg;
126762306a36Sopenharmony_ci		break;
126862306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_2000:
126962306a36Sopenharmony_ci		priv->lib = &iwl_dvm_2000_cfg;
127062306a36Sopenharmony_ci		break;
127162306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_105:
127262306a36Sopenharmony_ci		priv->lib = &iwl_dvm_105_cfg;
127362306a36Sopenharmony_ci		break;
127462306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_2030:
127562306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_135:
127662306a36Sopenharmony_ci		priv->lib = &iwl_dvm_2030_cfg;
127762306a36Sopenharmony_ci		break;
127862306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_5000:
127962306a36Sopenharmony_ci		priv->lib = &iwl_dvm_5000_cfg;
128062306a36Sopenharmony_ci		break;
128162306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_5150:
128262306a36Sopenharmony_ci		priv->lib = &iwl_dvm_5150_cfg;
128362306a36Sopenharmony_ci		break;
128462306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6000:
128562306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6000i:
128662306a36Sopenharmony_ci		priv->lib = &iwl_dvm_6000_cfg;
128762306a36Sopenharmony_ci		break;
128862306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6005:
128962306a36Sopenharmony_ci		priv->lib = &iwl_dvm_6005_cfg;
129062306a36Sopenharmony_ci		break;
129162306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6050:
129262306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6150:
129362306a36Sopenharmony_ci		priv->lib = &iwl_dvm_6050_cfg;
129462306a36Sopenharmony_ci		break;
129562306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_6030:
129662306a36Sopenharmony_ci		priv->lib = &iwl_dvm_6030_cfg;
129762306a36Sopenharmony_ci		break;
129862306a36Sopenharmony_ci	default:
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (WARN_ON(!priv->lib))
130362306a36Sopenharmony_ci		goto out_free_hw;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	/*
130662306a36Sopenharmony_ci	 * Populate the state variables that the transport layer needs
130762306a36Sopenharmony_ci	 * to know about.
130862306a36Sopenharmony_ci	 */
130962306a36Sopenharmony_ci	trans_cfg.op_mode = op_mode;
131062306a36Sopenharmony_ci	trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
131162306a36Sopenharmony_ci	trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	switch (iwlwifi_mod_params.amsdu_size) {
131462306a36Sopenharmony_ci	case IWL_AMSDU_DEF:
131562306a36Sopenharmony_ci	case IWL_AMSDU_4K:
131662306a36Sopenharmony_ci		trans_cfg.rx_buf_size = IWL_AMSDU_4K;
131762306a36Sopenharmony_ci		break;
131862306a36Sopenharmony_ci	case IWL_AMSDU_8K:
131962306a36Sopenharmony_ci		trans_cfg.rx_buf_size = IWL_AMSDU_8K;
132062306a36Sopenharmony_ci		break;
132162306a36Sopenharmony_ci	case IWL_AMSDU_12K:
132262306a36Sopenharmony_ci	default:
132362306a36Sopenharmony_ci		trans_cfg.rx_buf_size = IWL_AMSDU_4K;
132462306a36Sopenharmony_ci		pr_err("Unsupported amsdu_size: %d\n",
132562306a36Sopenharmony_ci		       iwlwifi_mod_params.amsdu_size);
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	trans_cfg.command_groups = iwl_dvm_groups;
133162306a36Sopenharmony_ci	trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
133462306a36Sopenharmony_ci	trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info,
133562306a36Sopenharmony_ci					  driver_data[2]);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE <
133862306a36Sopenharmony_ci		priv->trans->trans_cfg->base_params->num_of_queues);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	ucode_flags = fw->ucode_capa.flags;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
134362306a36Sopenharmony_ci		priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
134462306a36Sopenharmony_ci		trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
134562306a36Sopenharmony_ci	} else {
134662306a36Sopenharmony_ci		priv->sta_key_max_num = STA_KEY_MAX_NUM;
134762306a36Sopenharmony_ci		trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* Configure transport layer */
135162306a36Sopenharmony_ci	iwl_trans_configure(priv->trans, &trans_cfg);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
135462306a36Sopenharmony_ci	trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
135562306a36Sopenharmony_ci	trans->command_groups = trans_cfg.command_groups;
135662306a36Sopenharmony_ci	trans->command_groups_size = trans_cfg.command_groups_size;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/* At this point both hw and priv are allocated. */
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	SET_IEEE80211_DEV(priv->hw, priv->trans->dev);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	iwl_option_config(priv);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* bt channel inhibition enabled*/
136762306a36Sopenharmony_ci	priv->bt_ch_announce = true;
136862306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n",
136962306a36Sopenharmony_ci		       (priv->bt_ch_announce) ? "On" : "Off");
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* these spin locks will be used in apm_ops.init and EEPROM access
137262306a36Sopenharmony_ci	 * we should init now
137362306a36Sopenharmony_ci	 */
137462306a36Sopenharmony_ci	spin_lock_init(&priv->statistics.lock);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	/***********************
137762306a36Sopenharmony_ci	 * 2. Read REV register
137862306a36Sopenharmony_ci	 ***********************/
137962306a36Sopenharmony_ci	IWL_INFO(priv, "Detected %s, REV=0x%X\n",
138062306a36Sopenharmony_ci		priv->trans->name, priv->trans->hw_rev);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if (iwl_trans_start_hw(priv->trans))
138362306a36Sopenharmony_ci		goto out_free_hw;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	/* Read the EEPROM */
138662306a36Sopenharmony_ci	if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob,
138762306a36Sopenharmony_ci			    &priv->eeprom_blob_size)) {
138862306a36Sopenharmony_ci		IWL_ERR(priv, "Unable to init EEPROM\n");
138962306a36Sopenharmony_ci		goto out_free_hw;
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* Reset chip to save power until we load uCode during "up". */
139362306a36Sopenharmony_ci	iwl_trans_stop_device(priv->trans);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	priv->nvm_data = iwl_parse_eeprom_data(priv->trans, priv->cfg,
139662306a36Sopenharmony_ci					       priv->eeprom_blob,
139762306a36Sopenharmony_ci					       priv->eeprom_blob_size);
139862306a36Sopenharmony_ci	if (!priv->nvm_data)
139962306a36Sopenharmony_ci		goto out_free_eeprom_blob;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (iwl_nvm_check_version(priv->nvm_data, priv->trans))
140262306a36Sopenharmony_ci		goto out_free_eeprom;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (iwl_eeprom_init_hw_params(priv))
140562306a36Sopenharmony_ci		goto out_free_eeprom;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* extract MAC Address */
140862306a36Sopenharmony_ci	memcpy(priv->addresses[0].addr, priv->nvm_data->hw_addr, ETH_ALEN);
140962306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr);
141062306a36Sopenharmony_ci	priv->hw->wiphy->addresses = priv->addresses;
141162306a36Sopenharmony_ci	priv->hw->wiphy->n_addresses = 1;
141262306a36Sopenharmony_ci	num_mac = priv->nvm_data->n_hw_addrs;
141362306a36Sopenharmony_ci	if (num_mac > 1) {
141462306a36Sopenharmony_ci		memcpy(priv->addresses[1].addr, priv->addresses[0].addr,
141562306a36Sopenharmony_ci		       ETH_ALEN);
141662306a36Sopenharmony_ci		priv->addresses[1].addr[5]++;
141762306a36Sopenharmony_ci		priv->hw->wiphy->n_addresses++;
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/************************
142162306a36Sopenharmony_ci	 * 4. Setup HW constants
142262306a36Sopenharmony_ci	 ************************/
142362306a36Sopenharmony_ci	iwl_set_hw_params(priv);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (!(priv->nvm_data->sku_cap_ipan_enable)) {
142662306a36Sopenharmony_ci		IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
142762306a36Sopenharmony_ci		ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
142862306a36Sopenharmony_ci		/*
142962306a36Sopenharmony_ci		 * if not PAN, then don't support P2P -- might be a uCode
143062306a36Sopenharmony_ci		 * packaging bug or due to the eeprom check above
143162306a36Sopenharmony_ci		 */
143262306a36Sopenharmony_ci		priv->sta_key_max_num = STA_KEY_MAX_NUM;
143362306a36Sopenharmony_ci		trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci		/* Configure transport layer again*/
143662306a36Sopenharmony_ci		iwl_trans_configure(priv->trans, &trans_cfg);
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/*******************
144062306a36Sopenharmony_ci	 * 5. Setup priv
144162306a36Sopenharmony_ci	 *******************/
144262306a36Sopenharmony_ci	for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
144362306a36Sopenharmony_ci		priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
144462306a36Sopenharmony_ci		if (i < IWLAGN_FIRST_AMPDU_QUEUE &&
144562306a36Sopenharmony_ci		    i != IWL_DEFAULT_CMD_QUEUE_NUM &&
144662306a36Sopenharmony_ci		    i != IWL_IPAN_CMD_QUEUE_NUM)
144762306a36Sopenharmony_ci			priv->queue_to_mac80211[i] = i;
144862306a36Sopenharmony_ci		atomic_set(&priv->queue_stop_count[i], 0);
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (iwl_init_drv(priv))
145262306a36Sopenharmony_ci		goto out_free_eeprom;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/* At this point both hw and priv are initialized. */
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/********************
145762306a36Sopenharmony_ci	 * 6. Setup services
145862306a36Sopenharmony_ci	 ********************/
145962306a36Sopenharmony_ci	iwl_setup_deferred_work(priv);
146062306a36Sopenharmony_ci	iwl_setup_rx_handlers(priv);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	iwl_power_initialize(priv);
146362306a36Sopenharmony_ci	iwl_tt_initialize(priv);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	snprintf(priv->hw->wiphy->fw_version,
146662306a36Sopenharmony_ci		 sizeof(priv->hw->wiphy->fw_version),
146762306a36Sopenharmony_ci		 "%s", fw->fw_version);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	priv->new_scan_threshold_behaviour =
147062306a36Sopenharmony_ci		!!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	priv->phy_calib_chain_noise_reset_cmd =
147362306a36Sopenharmony_ci		fw->ucode_capa.standard_phy_calibration_size;
147462306a36Sopenharmony_ci	priv->phy_calib_chain_noise_gain_cmd =
147562306a36Sopenharmony_ci		fw->ucode_capa.standard_phy_calibration_size + 1;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	/* initialize all valid contexts */
147862306a36Sopenharmony_ci	iwl_init_context(priv, ucode_flags);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/**************************************************
148162306a36Sopenharmony_ci	 * This is still part of probe() in a sense...
148262306a36Sopenharmony_ci	 *
148362306a36Sopenharmony_ci	 * 7. Setup and register with mac80211 and debugfs
148462306a36Sopenharmony_ci	 **************************************************/
148562306a36Sopenharmony_ci	if (iwlagn_mac_setup_register(priv, &fw->ucode_capa))
148662306a36Sopenharmony_ci		goto out_destroy_workqueue;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	iwl_dbgfs_register(priv, dbgfs_dir);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	return op_mode;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ciout_destroy_workqueue:
149362306a36Sopenharmony_ci	iwl_tt_exit(priv);
149462306a36Sopenharmony_ci	iwl_cancel_deferred_work(priv);
149562306a36Sopenharmony_ci	destroy_workqueue(priv->workqueue);
149662306a36Sopenharmony_ci	priv->workqueue = NULL;
149762306a36Sopenharmony_ci	iwl_uninit_drv(priv);
149862306a36Sopenharmony_ciout_free_eeprom_blob:
149962306a36Sopenharmony_ci	kfree(priv->eeprom_blob);
150062306a36Sopenharmony_ciout_free_eeprom:
150162306a36Sopenharmony_ci	kfree(priv->nvm_data);
150262306a36Sopenharmony_ciout_free_hw:
150362306a36Sopenharmony_ci	ieee80211_free_hw(priv->hw);
150462306a36Sopenharmony_ciout:
150562306a36Sopenharmony_ci	op_mode = NULL;
150662306a36Sopenharmony_ci	return op_mode;
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_cistatic void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
151062306a36Sopenharmony_ci{
151162306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n");
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	iwlagn_mac_unregister(priv);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	iwl_tt_exit(priv);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	kfree(priv->eeprom_blob);
152062306a36Sopenharmony_ci	kfree(priv->nvm_data);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/*netif_stop_queue(dev); */
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes
152562306a36Sopenharmony_ci	 * priv->workqueue... so we can't take down the workqueue
152662306a36Sopenharmony_ci	 * until now... */
152762306a36Sopenharmony_ci	destroy_workqueue(priv->workqueue);
152862306a36Sopenharmony_ci	priv->workqueue = NULL;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	iwl_uninit_drv(priv);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	dev_kfree_skb(priv->beacon_skb);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	iwl_trans_op_mode_leave(priv->trans);
153562306a36Sopenharmony_ci	ieee80211_free_hw(priv->hw);
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic const char * const desc_lookup_text[] = {
153962306a36Sopenharmony_ci	"OK",
154062306a36Sopenharmony_ci	"FAIL",
154162306a36Sopenharmony_ci	"BAD_PARAM",
154262306a36Sopenharmony_ci	"BAD_CHECKSUM",
154362306a36Sopenharmony_ci	"NMI_INTERRUPT_WDG",
154462306a36Sopenharmony_ci	"SYSASSERT",
154562306a36Sopenharmony_ci	"FATAL_ERROR",
154662306a36Sopenharmony_ci	"BAD_COMMAND",
154762306a36Sopenharmony_ci	"HW_ERROR_TUNE_LOCK",
154862306a36Sopenharmony_ci	"HW_ERROR_TEMPERATURE",
154962306a36Sopenharmony_ci	"ILLEGAL_CHAN_FREQ",
155062306a36Sopenharmony_ci	"VCC_NOT_STABLE",
155162306a36Sopenharmony_ci	"FH_ERROR",
155262306a36Sopenharmony_ci	"NMI_INTERRUPT_HOST",
155362306a36Sopenharmony_ci	"NMI_INTERRUPT_ACTION_PT",
155462306a36Sopenharmony_ci	"NMI_INTERRUPT_UNKNOWN",
155562306a36Sopenharmony_ci	"UCODE_VERSION_MISMATCH",
155662306a36Sopenharmony_ci	"HW_ERROR_ABS_LOCK",
155762306a36Sopenharmony_ci	"HW_ERROR_CAL_LOCK_FAIL",
155862306a36Sopenharmony_ci	"NMI_INTERRUPT_INST_ACTION_PT",
155962306a36Sopenharmony_ci	"NMI_INTERRUPT_DATA_ACTION_PT",
156062306a36Sopenharmony_ci	"NMI_TRM_HW_ER",
156162306a36Sopenharmony_ci	"NMI_INTERRUPT_TRM",
156262306a36Sopenharmony_ci	"NMI_INTERRUPT_BREAK_POINT",
156362306a36Sopenharmony_ci	"DEBUG_0",
156462306a36Sopenharmony_ci	"DEBUG_1",
156562306a36Sopenharmony_ci	"DEBUG_2",
156662306a36Sopenharmony_ci	"DEBUG_3",
156762306a36Sopenharmony_ci};
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic struct { char *name; u8 num; } advanced_lookup[] = {
157062306a36Sopenharmony_ci	{ "NMI_INTERRUPT_WDG", 0x34 },
157162306a36Sopenharmony_ci	{ "SYSASSERT", 0x35 },
157262306a36Sopenharmony_ci	{ "UCODE_VERSION_MISMATCH", 0x37 },
157362306a36Sopenharmony_ci	{ "BAD_COMMAND", 0x38 },
157462306a36Sopenharmony_ci	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
157562306a36Sopenharmony_ci	{ "FATAL_ERROR", 0x3D },
157662306a36Sopenharmony_ci	{ "NMI_TRM_HW_ERR", 0x46 },
157762306a36Sopenharmony_ci	{ "NMI_INTERRUPT_TRM", 0x4C },
157862306a36Sopenharmony_ci	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
157962306a36Sopenharmony_ci	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
158062306a36Sopenharmony_ci	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
158162306a36Sopenharmony_ci	{ "NMI_INTERRUPT_HOST", 0x66 },
158262306a36Sopenharmony_ci	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
158362306a36Sopenharmony_ci	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
158462306a36Sopenharmony_ci	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
158562306a36Sopenharmony_ci	{ "ADVANCED_SYSASSERT", 0 },
158662306a36Sopenharmony_ci};
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic const char *desc_lookup(u32 num)
158962306a36Sopenharmony_ci{
159062306a36Sopenharmony_ci	int i;
159162306a36Sopenharmony_ci	int max = ARRAY_SIZE(desc_lookup_text);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	if (num < max)
159462306a36Sopenharmony_ci		return desc_lookup_text[num];
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	max = ARRAY_SIZE(advanced_lookup) - 1;
159762306a36Sopenharmony_ci	for (i = 0; i < max; i++) {
159862306a36Sopenharmony_ci		if (advanced_lookup[i].num == num)
159962306a36Sopenharmony_ci			break;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci	return advanced_lookup[i].name;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci#define ERROR_START_OFFSET  (1 * sizeof(u32))
160562306a36Sopenharmony_ci#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic void iwl_dump_nic_error_log(struct iwl_priv *priv)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct iwl_trans *trans = priv->trans;
161062306a36Sopenharmony_ci	u32 base;
161162306a36Sopenharmony_ci	struct iwl_error_event_table table;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	base = priv->device_pointers.error_event_table;
161462306a36Sopenharmony_ci	if (priv->cur_ucode == IWL_UCODE_INIT) {
161562306a36Sopenharmony_ci		if (!base)
161662306a36Sopenharmony_ci			base = priv->fw->init_errlog_ptr;
161762306a36Sopenharmony_ci	} else {
161862306a36Sopenharmony_ci		if (!base)
161962306a36Sopenharmony_ci			base = priv->fw->inst_errlog_ptr;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (!iwlagn_hw_valid_rtc_data_addr(base)) {
162362306a36Sopenharmony_ci		IWL_ERR(priv,
162462306a36Sopenharmony_ci			"Not valid error log pointer 0x%08X for %s uCode\n",
162562306a36Sopenharmony_ci			base,
162662306a36Sopenharmony_ci			(priv->cur_ucode == IWL_UCODE_INIT)
162762306a36Sopenharmony_ci					? "Init" : "RT");
162862306a36Sopenharmony_ci		return;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	/*TODO: Update dbgfs with ISR error stats obtained below */
163262306a36Sopenharmony_ci	iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
163562306a36Sopenharmony_ci		IWL_ERR(trans, "Start IWL Error Log Dump:\n");
163662306a36Sopenharmony_ci		IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
163762306a36Sopenharmony_ci			priv->status, table.valid);
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
164162306a36Sopenharmony_ci		desc_lookup(table.error_id));
164262306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
164362306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1);
164462306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2);
164562306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1);
164662306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2);
164762306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | data1\n", table.data1);
164862306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | data2\n", table.data2);
164962306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | line\n", table.line);
165062306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time);
165162306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low);
165262306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi);
165362306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1);
165462306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2);
165562306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3);
165662306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver);
165762306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver);
165862306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver);
165962306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd);
166062306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr0\n", table.isr0);
166162306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr1\n", table.isr1);
166262306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr2\n", table.isr2);
166362306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr3\n", table.isr3);
166462306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr4\n", table.isr4);
166562306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref);
166662306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event);
166762306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control);
166862306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration);
166962306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
167062306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
167162306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
167262306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp);
167362306a36Sopenharmony_ci	IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler);
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci#define EVENT_START_OFFSET  (4 * sizeof(u32))
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci/*
167962306a36Sopenharmony_ci * iwl_print_event_log - Dump error event log to syslog
168062306a36Sopenharmony_ci */
168162306a36Sopenharmony_cistatic int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
168262306a36Sopenharmony_ci			       u32 num_events, u32 mode,
168362306a36Sopenharmony_ci			       int pos, char **buf, size_t bufsz)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	u32 i;
168662306a36Sopenharmony_ci	u32 base;       /* SRAM byte address of event log header */
168762306a36Sopenharmony_ci	u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
168862306a36Sopenharmony_ci	u32 ptr;        /* SRAM byte address of log data */
168962306a36Sopenharmony_ci	u32 ev, time, data; /* event log data */
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	struct iwl_trans *trans = priv->trans;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (num_events == 0)
169462306a36Sopenharmony_ci		return pos;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	base = priv->device_pointers.log_event_table;
169762306a36Sopenharmony_ci	if (priv->cur_ucode == IWL_UCODE_INIT) {
169862306a36Sopenharmony_ci		if (!base)
169962306a36Sopenharmony_ci			base = priv->fw->init_evtlog_ptr;
170062306a36Sopenharmony_ci	} else {
170162306a36Sopenharmony_ci		if (!base)
170262306a36Sopenharmony_ci			base = priv->fw->inst_evtlog_ptr;
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	if (mode == 0)
170662306a36Sopenharmony_ci		event_size = 2 * sizeof(u32);
170762306a36Sopenharmony_ci	else
170862306a36Sopenharmony_ci		event_size = 3 * sizeof(u32);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* Make sure device is powered up for SRAM reads */
171362306a36Sopenharmony_ci	if (!iwl_trans_grab_nic_access(trans))
171462306a36Sopenharmony_ci		return pos;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	/* Set starting address; reads will auto-increment */
171762306a36Sopenharmony_ci	iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	/* "time" is actually "data" for mode 0 (no timestamp).
172062306a36Sopenharmony_ci	* place event id # at far right for easier visual parsing. */
172162306a36Sopenharmony_ci	for (i = 0; i < num_events; i++) {
172262306a36Sopenharmony_ci		ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
172362306a36Sopenharmony_ci		time = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
172462306a36Sopenharmony_ci		if (mode == 0) {
172562306a36Sopenharmony_ci			/* data, ev */
172662306a36Sopenharmony_ci			if (bufsz) {
172762306a36Sopenharmony_ci				pos += scnprintf(*buf + pos, bufsz - pos,
172862306a36Sopenharmony_ci						"EVT_LOG:0x%08x:%04u\n",
172962306a36Sopenharmony_ci						time, ev);
173062306a36Sopenharmony_ci			} else {
173162306a36Sopenharmony_ci				trace_iwlwifi_dev_ucode_event(trans->dev, 0,
173262306a36Sopenharmony_ci					time, ev);
173362306a36Sopenharmony_ci				IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
173462306a36Sopenharmony_ci					time, ev);
173562306a36Sopenharmony_ci			}
173662306a36Sopenharmony_ci		} else {
173762306a36Sopenharmony_ci			data = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
173862306a36Sopenharmony_ci			if (bufsz) {
173962306a36Sopenharmony_ci				pos += scnprintf(*buf + pos, bufsz - pos,
174062306a36Sopenharmony_ci						"EVT_LOGT:%010u:0x%08x:%04u\n",
174162306a36Sopenharmony_ci						 time, data, ev);
174262306a36Sopenharmony_ci			} else {
174362306a36Sopenharmony_ci				IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
174462306a36Sopenharmony_ci					time, data, ev);
174562306a36Sopenharmony_ci				trace_iwlwifi_dev_ucode_event(trans->dev, time,
174662306a36Sopenharmony_ci					data, ev);
174762306a36Sopenharmony_ci			}
174862306a36Sopenharmony_ci		}
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	/* Allow device to power down */
175262306a36Sopenharmony_ci	iwl_trans_release_nic_access(trans);
175362306a36Sopenharmony_ci	return pos;
175462306a36Sopenharmony_ci}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci/*
175762306a36Sopenharmony_ci * iwl_print_last_event_logs - Dump the newest # of event log to syslog
175862306a36Sopenharmony_ci */
175962306a36Sopenharmony_cistatic int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
176062306a36Sopenharmony_ci				    u32 num_wraps, u32 next_entry,
176162306a36Sopenharmony_ci				    u32 size, u32 mode,
176262306a36Sopenharmony_ci				    int pos, char **buf, size_t bufsz)
176362306a36Sopenharmony_ci{
176462306a36Sopenharmony_ci	/*
176562306a36Sopenharmony_ci	 * display the newest DEFAULT_LOG_ENTRIES entries
176662306a36Sopenharmony_ci	 * i.e the entries just before the next ont that uCode would fill.
176762306a36Sopenharmony_ci	 */
176862306a36Sopenharmony_ci	if (num_wraps) {
176962306a36Sopenharmony_ci		if (next_entry < size) {
177062306a36Sopenharmony_ci			pos = iwl_print_event_log(priv,
177162306a36Sopenharmony_ci						capacity - (size - next_entry),
177262306a36Sopenharmony_ci						size - next_entry, mode,
177362306a36Sopenharmony_ci						pos, buf, bufsz);
177462306a36Sopenharmony_ci			pos = iwl_print_event_log(priv, 0,
177562306a36Sopenharmony_ci						  next_entry, mode,
177662306a36Sopenharmony_ci						  pos, buf, bufsz);
177762306a36Sopenharmony_ci		} else
177862306a36Sopenharmony_ci			pos = iwl_print_event_log(priv, next_entry - size,
177962306a36Sopenharmony_ci						  size, mode, pos, buf, bufsz);
178062306a36Sopenharmony_ci	} else {
178162306a36Sopenharmony_ci		if (next_entry < size) {
178262306a36Sopenharmony_ci			pos = iwl_print_event_log(priv, 0, next_entry,
178362306a36Sopenharmony_ci						  mode, pos, buf, bufsz);
178462306a36Sopenharmony_ci		} else {
178562306a36Sopenharmony_ci			pos = iwl_print_event_log(priv, next_entry - size,
178662306a36Sopenharmony_ci						  size, mode, pos, buf, bufsz);
178762306a36Sopenharmony_ci		}
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci	return pos;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ciint iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
179562306a36Sopenharmony_ci			    char **buf)
179662306a36Sopenharmony_ci{
179762306a36Sopenharmony_ci	u32 base;       /* SRAM byte address of event log header */
179862306a36Sopenharmony_ci	u32 capacity;   /* event log capacity in # entries */
179962306a36Sopenharmony_ci	u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
180062306a36Sopenharmony_ci	u32 num_wraps;  /* # times uCode wrapped to top of log */
180162306a36Sopenharmony_ci	u32 next_entry; /* index of next entry to be written by uCode */
180262306a36Sopenharmony_ci	u32 size;       /* # entries that we'll print */
180362306a36Sopenharmony_ci	u32 logsize;
180462306a36Sopenharmony_ci	int pos = 0;
180562306a36Sopenharmony_ci	size_t bufsz = 0;
180662306a36Sopenharmony_ci	struct iwl_trans *trans = priv->trans;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	base = priv->device_pointers.log_event_table;
180962306a36Sopenharmony_ci	if (priv->cur_ucode == IWL_UCODE_INIT) {
181062306a36Sopenharmony_ci		logsize = priv->fw->init_evtlog_size;
181162306a36Sopenharmony_ci		if (!base)
181262306a36Sopenharmony_ci			base = priv->fw->init_evtlog_ptr;
181362306a36Sopenharmony_ci	} else {
181462306a36Sopenharmony_ci		logsize = priv->fw->inst_evtlog_size;
181562306a36Sopenharmony_ci		if (!base)
181662306a36Sopenharmony_ci			base = priv->fw->inst_evtlog_ptr;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (!iwlagn_hw_valid_rtc_data_addr(base)) {
182062306a36Sopenharmony_ci		IWL_ERR(priv,
182162306a36Sopenharmony_ci			"Invalid event log pointer 0x%08X for %s uCode\n",
182262306a36Sopenharmony_ci			base,
182362306a36Sopenharmony_ci			(priv->cur_ucode == IWL_UCODE_INIT)
182462306a36Sopenharmony_ci					? "Init" : "RT");
182562306a36Sopenharmony_ci		return -EINVAL;
182662306a36Sopenharmony_ci	}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	/* event log header */
182962306a36Sopenharmony_ci	capacity = iwl_trans_read_mem32(trans, base);
183062306a36Sopenharmony_ci	mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32)));
183162306a36Sopenharmony_ci	num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32)));
183262306a36Sopenharmony_ci	next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32)));
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	if (capacity > logsize) {
183562306a36Sopenharmony_ci		IWL_ERR(priv, "Log capacity %d is bogus, limit to %d "
183662306a36Sopenharmony_ci			"entries\n", capacity, logsize);
183762306a36Sopenharmony_ci		capacity = logsize;
183862306a36Sopenharmony_ci	}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	if (next_entry > logsize) {
184162306a36Sopenharmony_ci		IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
184262306a36Sopenharmony_ci			next_entry, logsize);
184362306a36Sopenharmony_ci		next_entry = logsize;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	size = num_wraps ? capacity : next_entry;
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	/* bail out if nothing in log */
184962306a36Sopenharmony_ci	if (size == 0) {
185062306a36Sopenharmony_ci		IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
185162306a36Sopenharmony_ci		return pos;
185262306a36Sopenharmony_ci	}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	if (!(iwl_have_debug_level(IWL_DL_FW)) && !full_log)
185562306a36Sopenharmony_ci		size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
185662306a36Sopenharmony_ci			? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
185762306a36Sopenharmony_ci	IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
185862306a36Sopenharmony_ci		size);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG
186162306a36Sopenharmony_ci	if (buf) {
186262306a36Sopenharmony_ci		if (full_log)
186362306a36Sopenharmony_ci			bufsz = capacity * 48;
186462306a36Sopenharmony_ci		else
186562306a36Sopenharmony_ci			bufsz = size * 48;
186662306a36Sopenharmony_ci		*buf = kmalloc(bufsz, GFP_KERNEL);
186762306a36Sopenharmony_ci		if (!*buf)
186862306a36Sopenharmony_ci			return -ENOMEM;
186962306a36Sopenharmony_ci	}
187062306a36Sopenharmony_ci	if (iwl_have_debug_level(IWL_DL_FW) || full_log) {
187162306a36Sopenharmony_ci		/*
187262306a36Sopenharmony_ci		 * if uCode has wrapped back to top of log,
187362306a36Sopenharmony_ci		 * start at the oldest entry,
187462306a36Sopenharmony_ci		 * i.e the next one that uCode would fill.
187562306a36Sopenharmony_ci		 */
187662306a36Sopenharmony_ci		if (num_wraps)
187762306a36Sopenharmony_ci			pos = iwl_print_event_log(priv, next_entry,
187862306a36Sopenharmony_ci						capacity - next_entry, mode,
187962306a36Sopenharmony_ci						pos, buf, bufsz);
188062306a36Sopenharmony_ci		/* (then/else) start at top of log */
188162306a36Sopenharmony_ci		pos = iwl_print_event_log(priv, 0,
188262306a36Sopenharmony_ci					  next_entry, mode, pos, buf, bufsz);
188362306a36Sopenharmony_ci	} else
188462306a36Sopenharmony_ci		pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
188562306a36Sopenharmony_ci						next_entry, size, mode,
188662306a36Sopenharmony_ci						pos, buf, bufsz);
188762306a36Sopenharmony_ci#else
188862306a36Sopenharmony_ci	pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
188962306a36Sopenharmony_ci					next_entry, size, mode,
189062306a36Sopenharmony_ci					pos, buf, bufsz);
189162306a36Sopenharmony_ci#endif
189262306a36Sopenharmony_ci	return pos;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	unsigned int reload_msec;
189862306a36Sopenharmony_ci	unsigned long reload_jiffies;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	if (iwl_have_debug_level(IWL_DL_FW))
190162306a36Sopenharmony_ci		iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	/* uCode is no longer loaded. */
190462306a36Sopenharmony_ci	priv->ucode_loaded = false;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	/* Set the FW error flag -- cleared on iwl_down */
190762306a36Sopenharmony_ci	set_bit(STATUS_FW_ERROR, &priv->status);
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	iwl_abort_notification_waits(&priv->notif_wait);
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	/* Keep the restart process from trying to send host
191262306a36Sopenharmony_ci	 * commands by clearing the ready bit */
191362306a36Sopenharmony_ci	clear_bit(STATUS_READY, &priv->status);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	if (!ondemand) {
191662306a36Sopenharmony_ci		/*
191762306a36Sopenharmony_ci		 * If firmware keep reloading, then it indicate something
191862306a36Sopenharmony_ci		 * serious wrong and firmware having problem to recover
191962306a36Sopenharmony_ci		 * from it. Instead of keep trying which will fill the syslog
192062306a36Sopenharmony_ci		 * and hang the system, let's just stop it
192162306a36Sopenharmony_ci		 */
192262306a36Sopenharmony_ci		reload_jiffies = jiffies;
192362306a36Sopenharmony_ci		reload_msec = jiffies_to_msecs((long) reload_jiffies -
192462306a36Sopenharmony_ci					(long) priv->reload_jiffies);
192562306a36Sopenharmony_ci		priv->reload_jiffies = reload_jiffies;
192662306a36Sopenharmony_ci		if (reload_msec <= IWL_MIN_RELOAD_DURATION) {
192762306a36Sopenharmony_ci			priv->reload_count++;
192862306a36Sopenharmony_ci			if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) {
192962306a36Sopenharmony_ci				IWL_ERR(priv, "BUG_ON, Stop restarting\n");
193062306a36Sopenharmony_ci				return;
193162306a36Sopenharmony_ci			}
193262306a36Sopenharmony_ci		} else
193362306a36Sopenharmony_ci			priv->reload_count = 0;
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
193762306a36Sopenharmony_ci		if (iwlwifi_mod_params.fw_restart) {
193862306a36Sopenharmony_ci			IWL_DEBUG_FW(priv,
193962306a36Sopenharmony_ci				     "Restarting adapter due to uCode error.\n");
194062306a36Sopenharmony_ci			queue_work(priv->workqueue, &priv->restart);
194162306a36Sopenharmony_ci		} else
194262306a36Sopenharmony_ci			IWL_DEBUG_FW(priv,
194362306a36Sopenharmony_ci				     "Detected FW error, but not restarting\n");
194462306a36Sopenharmony_ci	}
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic void iwl_nic_error(struct iwl_op_mode *op_mode, bool sync)
194862306a36Sopenharmony_ci{
194962306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	IWL_ERR(priv, "Loaded firmware version: %s\n",
195262306a36Sopenharmony_ci		priv->fw->fw_version);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	iwl_dump_nic_error_log(priv);
195562306a36Sopenharmony_ci	iwl_dump_nic_event_log(priv, false, NULL);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	iwlagn_fw_error(priv, false);
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_cistatic void iwl_cmd_queue_full(struct iwl_op_mode *op_mode)
196162306a36Sopenharmony_ci{
196262306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (!iwl_check_for_ct_kill(priv)) {
196562306a36Sopenharmony_ci		IWL_ERR(priv, "Restarting adapter queue is full\n");
196662306a36Sopenharmony_ci		iwlagn_fw_error(priv, false);
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci#define EEPROM_RF_CONFIG_TYPE_MAX      0x3
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_cistatic void iwl_nic_config(struct iwl_op_mode *op_mode)
197362306a36Sopenharmony_ci{
197462306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	/* SKU Control */
197762306a36Sopenharmony_ci	iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
197862306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH,
197962306a36Sopenharmony_ci				CSR_HW_REV_STEP_DASH(priv->trans->hw_rev));
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	/* write radio config values to register */
198262306a36Sopenharmony_ci	if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
198362306a36Sopenharmony_ci		u32 reg_val =
198462306a36Sopenharmony_ci			priv->nvm_data->radio_cfg_type <<
198562306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE |
198662306a36Sopenharmony_ci			priv->nvm_data->radio_cfg_step <<
198762306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_POS_PHY_STEP |
198862306a36Sopenharmony_ci			priv->nvm_data->radio_cfg_dash <<
198962306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci		iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
199262306a36Sopenharmony_ci					CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
199362306a36Sopenharmony_ci					CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
199462306a36Sopenharmony_ci					CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH,
199562306a36Sopenharmony_ci					reg_val);
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci		IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
199862306a36Sopenharmony_ci			 priv->nvm_data->radio_cfg_type,
199962306a36Sopenharmony_ci			 priv->nvm_data->radio_cfg_step,
200062306a36Sopenharmony_ci			 priv->nvm_data->radio_cfg_dash);
200162306a36Sopenharmony_ci	} else {
200262306a36Sopenharmony_ci		WARN_ON(1);
200362306a36Sopenharmony_ci	}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* set CSR_HW_CONFIG_REG for uCode use */
200662306a36Sopenharmony_ci	iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG,
200762306a36Sopenharmony_ci		    CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
200862306a36Sopenharmony_ci		    CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	/* W/A : NIC is stuck in a reset state after Early PCIe power off
201162306a36Sopenharmony_ci	 * (PCIe power is lost before PERST# is asserted),
201262306a36Sopenharmony_ci	 * causing ME FW to lose ownership and not being able to obtain it back.
201362306a36Sopenharmony_ci	 */
201462306a36Sopenharmony_ci	iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG,
201562306a36Sopenharmony_ci			       APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
201662306a36Sopenharmony_ci			       ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (priv->lib->nic_config)
201962306a36Sopenharmony_ci		priv->lib->nic_config(priv);
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_cistatic void iwl_wimax_active(struct iwl_op_mode *op_mode)
202362306a36Sopenharmony_ci{
202462306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	clear_bit(STATUS_READY, &priv->status);
202762306a36Sopenharmony_ci	IWL_ERR(priv, "RF is used by WiMAX\n");
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_cistatic void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
203362306a36Sopenharmony_ci	int mq = priv->queue_to_mac80211[queue];
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
203662306a36Sopenharmony_ci		return;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) {
203962306a36Sopenharmony_ci		IWL_DEBUG_TX_QUEUES(priv,
204062306a36Sopenharmony_ci			"queue %d (mac80211 %d) already stopped\n",
204162306a36Sopenharmony_ci			queue, mq);
204262306a36Sopenharmony_ci		return;
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	set_bit(mq, &priv->transport_queue_stop);
204662306a36Sopenharmony_ci	ieee80211_stop_queue(priv->hw, mq);
204762306a36Sopenharmony_ci}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_cistatic void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
205062306a36Sopenharmony_ci{
205162306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
205262306a36Sopenharmony_ci	int mq = priv->queue_to_mac80211[queue];
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
205562306a36Sopenharmony_ci		return;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) {
205862306a36Sopenharmony_ci		IWL_DEBUG_TX_QUEUES(priv,
205962306a36Sopenharmony_ci			"queue %d (mac80211 %d) already awake\n",
206062306a36Sopenharmony_ci			queue, mq);
206162306a36Sopenharmony_ci		return;
206262306a36Sopenharmony_ci	}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	clear_bit(mq, &priv->transport_queue_stop);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	if (!priv->passive_no_rx)
206762306a36Sopenharmony_ci		ieee80211_wake_queue(priv->hw, mq);
206862306a36Sopenharmony_ci}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_civoid iwlagn_lift_passive_no_rx(struct iwl_priv *priv)
207162306a36Sopenharmony_ci{
207262306a36Sopenharmony_ci	int mq;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (!priv->passive_no_rx)
207562306a36Sopenharmony_ci		return;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) {
207862306a36Sopenharmony_ci		if (!test_bit(mq, &priv->transport_queue_stop)) {
207962306a36Sopenharmony_ci			IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq);
208062306a36Sopenharmony_ci			ieee80211_wake_queue(priv->hw, mq);
208162306a36Sopenharmony_ci		} else {
208262306a36Sopenharmony_ci			IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq);
208362306a36Sopenharmony_ci		}
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	priv->passive_no_rx = false;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
209262306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
209562306a36Sopenharmony_ci	iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]);
209662306a36Sopenharmony_ci	ieee80211_free_txskb(priv->hw, skb);
209762306a36Sopenharmony_ci}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_cistatic bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
210062306a36Sopenharmony_ci{
210162306a36Sopenharmony_ci	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	if (state)
210462306a36Sopenharmony_ci		set_bit(STATUS_RF_KILL_HW, &priv->status);
210562306a36Sopenharmony_ci	else
210662306a36Sopenharmony_ci		clear_bit(STATUS_RF_KILL_HW, &priv->status);
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	wiphy_rfkill_set_hw_state(priv->hw->wiphy, state);
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	return false;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic const struct iwl_op_mode_ops iwl_dvm_ops = {
211462306a36Sopenharmony_ci	.start = iwl_op_mode_dvm_start,
211562306a36Sopenharmony_ci	.stop = iwl_op_mode_dvm_stop,
211662306a36Sopenharmony_ci	.rx = iwl_rx_dispatch,
211762306a36Sopenharmony_ci	.queue_full = iwl_stop_sw_queue,
211862306a36Sopenharmony_ci	.queue_not_full = iwl_wake_sw_queue,
211962306a36Sopenharmony_ci	.hw_rf_kill = iwl_set_hw_rfkill_state,
212062306a36Sopenharmony_ci	.free_skb = iwl_free_skb,
212162306a36Sopenharmony_ci	.nic_error = iwl_nic_error,
212262306a36Sopenharmony_ci	.cmd_queue_full = iwl_cmd_queue_full,
212362306a36Sopenharmony_ci	.nic_config = iwl_nic_config,
212462306a36Sopenharmony_ci	.wimax_active = iwl_wimax_active,
212562306a36Sopenharmony_ci};
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci/*****************************************************************************
212862306a36Sopenharmony_ci *
212962306a36Sopenharmony_ci * driver and module entry point
213062306a36Sopenharmony_ci *
213162306a36Sopenharmony_ci *****************************************************************************/
213262306a36Sopenharmony_cistatic int __init iwl_init(void)
213362306a36Sopenharmony_ci{
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	int ret;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	ret = iwlagn_rate_control_register();
213862306a36Sopenharmony_ci	if (ret) {
213962306a36Sopenharmony_ci		pr_err("Unable to register rate control algorithm: %d\n", ret);
214062306a36Sopenharmony_ci		return ret;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops);
214462306a36Sopenharmony_ci	if (ret) {
214562306a36Sopenharmony_ci		pr_err("Unable to register op_mode: %d\n", ret);
214662306a36Sopenharmony_ci		iwlagn_rate_control_unregister();
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	return ret;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_cimodule_init(iwl_init);
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_cistatic void __exit iwl_exit(void)
215462306a36Sopenharmony_ci{
215562306a36Sopenharmony_ci	iwl_opmode_deregister("iwldvm");
215662306a36Sopenharmony_ci	iwlagn_rate_control_unregister();
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_cimodule_exit(iwl_exit);
2159