Commit 4ea1ed1d authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by Johannes Berg
Browse files

wifi: iwlwifi: mvm: support set_antenna()



set_antenna() is supported only when the device is not started in
mac80211 which translates to the firmware not being loaded in iwlwifi.

The tricky part is that iwlwifi populates the sband data during its boot
and doesn't touch this data afterwards, but if the antenna settings
forbid MIMO, we need to update the sband data.

Rework the nvm parsing code to allow to get an existing nvm_data and
modify the sband with additional constraints (tx / rx chains masks).

Suggested-by: default avatarBen Greear <greearb@candelatech.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230921110726.81d94d630c95.I9473da818cbeeb51b2f89dcc59b00019113e7f55@changeid


[add bugfix from Benjamin for iwl_mvm_get_valid_rx_ant()]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1228c749
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
 * Copyright (C) 2005-2014, 2018-2020 Intel Corporation
 * Copyright (C) 2005-2014, 2018-2021, 2023 Intel Corporation
 * Copyright (C) 2015 Intel Mobile Communications GmbH
 */
#include <linux/types.h>
@@ -721,6 +721,9 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans,
	ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;

	ht_info->mcs.rx_mask[0] = 0xFF;
	ht_info->mcs.rx_mask[1] = 0x00;
	ht_info->mcs.rx_mask[2] = 0x00;

	if (rx_chains >= 2)
		ht_info->mcs.rx_mask[1] = 0xFF;
	if (rx_chains >= 3)
+66 −10
Original line number Diff line number Diff line
@@ -962,6 +962,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
			}
		}
	} else {
		struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp =
			&iftype_data->he_cap.he_mcs_nss_supp;

		if (iftype_data->eht_cap.has_eht) {
			struct ieee80211_eht_mcs_nss_supp *mcs_nss =
				&iftype_data->eht_cap.eht_mcs_nss_supp;
@@ -980,6 +983,19 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
			iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
				IEEE80211_HE_PHY_CAP7_MAX_NC_1;
		}

		he_mcs_nss_supp->rx_mcs_80 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
		he_mcs_nss_supp->tx_mcs_80 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
		he_mcs_nss_supp->rx_mcs_160 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
		he_mcs_nss_supp->tx_mcs_160 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
		he_mcs_nss_supp->rx_mcs_80p80 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
		he_mcs_nss_supp->tx_mcs_80p80 |=
			cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2);
	}

	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap)
@@ -1052,10 +1068,6 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
	struct ieee80211_sband_iftype_data *iftype_data;
	int i;

	/* should only initialize once */
	if (WARN_ON(sband->iftype_data))
		return;

	BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
	BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
	BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa));
@@ -1087,6 +1099,37 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
	iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains);
}

void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
		    u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw)
{
	struct ieee80211_supported_band *sband;

	sband = &data->bands[NL80211_BAND_2GHZ];
	iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_2GHZ,
			     tx_chains, rx_chains);

	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
				     fw);

	sband = &data->bands[NL80211_BAND_5GHZ];
	iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_5GHZ,
			     tx_chains, rx_chains);
	if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac)
		iwl_init_vht_hw_capab(trans, data, &sband->vht_cap,
				      tx_chains, rx_chains);

	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
				     fw);

	sband = &data->bands[NL80211_BAND_6GHZ];
	if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
		iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
				     fw);
}
IWL_EXPORT_SYMBOL(iwl_reinit_cab);

static void iwl_init_sbands(struct iwl_trans *trans,
			    struct iwl_nvm_data *data,
			    const void *nvm_ch_flags, u8 tx_chains,
@@ -1365,7 +1408,7 @@ iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg,
struct iwl_nvm_data *
iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
		       const struct iwl_mei_nvm *mei_nvm,
		       const struct iwl_fw *fw)
		       const struct iwl_fw *fw, u8 tx_ant, u8 rx_ant)
{
	struct iwl_nvm_data *data;
	u32 sbands_flags = 0;
@@ -1392,6 +1435,10 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
		tx_chains &= data->valid_tx_ant;
	if (data->valid_rx_ant)
		rx_chains &= data->valid_rx_ant;
	if (tx_ant)
		tx_chains &= tx_ant;
	if (rx_ant)
		rx_chains &= rx_ant;

	data->sku_cap_mimo_disabled = false;
	data->sku_cap_band_24ghz_enable = true;
@@ -1957,7 +2004,8 @@ int iwl_read_external_nvm(struct iwl_trans *trans,
IWL_EXPORT_SYMBOL(iwl_read_external_nvm);

struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
				 const struct iwl_fw *fw)
				 const struct iwl_fw *fw,
				 u8 set_tx_ant, u8 set_rx_ant)
{
	struct iwl_nvm_get_info cmd = {};
	struct iwl_nvm_data *nvm;
@@ -1971,6 +2019,9 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
	bool empty_otp;
	u32 mac_flags;
	u32 sbands_flags = 0;
	u8 tx_ant;
	u8 rx_ant;

	/*
	 * All the values in iwl_nvm_get_info_rsp v4 are the same as
	 * in v3, except for the channel profile part of the
@@ -2058,10 +2109,15 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
	channel_profile = v4 ? (void *)rsp->regulatory.channel_profile :
			  (void *)rsp_v3->regulatory.channel_profile;

	iwl_init_sbands(trans, nvm,
			channel_profile,
			nvm->valid_tx_ant & fw->valid_tx_ant,
			nvm->valid_rx_ant & fw->valid_rx_ant,
	tx_ant = nvm->valid_tx_ant & fw->valid_tx_ant;
	rx_ant = nvm->valid_rx_ant & fw->valid_rx_ant;

	if (set_tx_ant)
		tx_ant &= set_tx_ant;
	if (set_rx_ant)
		rx_ant &= set_rx_ant;

	iwl_init_sbands(trans, nvm, channel_profile, tx_ant, rx_ant,
			sbands_flags, v4, fw);

	iwl_free_resp(&hcmd);
+13 −6
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
 * Copyright (C) 2005-2015, 2018-2022 Intel Corporation
 * Copyright (C) 2005-2015, 2018-2023 Intel Corporation
 * Copyright (C) 2016-2017 Intel Deutschland GmbH
 */
#ifndef __iwl_nvm_parse_h__
@@ -21,7 +21,7 @@ enum iwl_nvm_sbands_flags {
	IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ	= BIT(1),
};

/**
/*
 * iwl_parse_nvm_data - parse NVM data and return values
 *
 * This function parses all NVM values we need and then
@@ -73,21 +73,28 @@ int iwl_read_external_nvm(struct iwl_trans *trans,
void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data,
		    unsigned int len);

/**
/*
 * iwl_get_nvm - retrieve NVM data from firmware
 *
 * Allocates a new iwl_nvm_data structure, fills it with
 * NVM data, and returns it to caller.
 */
struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
				 const struct iwl_fw *fw);
				 const struct iwl_fw *fw,
				 u8 set_tx_ant, u8 set_rx_ant);

/**
/*
 * iwl_parse_mei_nvm_data - parse the mei_nvm_data and get an iwl_nvm_data
 */
struct iwl_nvm_data *
iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
		       const struct iwl_mei_nvm *mei_nvm,
		       const struct iwl_fw *fw);
		       const struct iwl_fw *fw, u8 set_tx_ant, u8 set_rx_ant);

/*
 * iwl_reinit_cab - to be called when the tx_chains or rx_chains are modified
 */
void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
		    u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw);

#endif /* __iwl_nvm_parse_h__ */
+2 −1
Original line number Diff line number Diff line
@@ -681,7 +681,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm)

	/* Read the NVM only at driver load time, no need to do this twice */
	if (!IWL_MVM_PARSE_NVM && !mvm->nvm_data) {
		mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw);
		mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw,
					    mvm->set_tx_ant, mvm->set_rx_ant);
		if (IS_ERR(mvm->nvm_data)) {
			ret = PTR_ERR(mvm->nvm_data);
			mvm->nvm_data = NULL;
+25 −0
Original line number Diff line number Diff line
@@ -279,6 +279,30 @@ int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
	return 0;
}

int iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
{
	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);

	/* This has been tested on those devices only */
	if (mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_9000 &&
	    mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_22000)
		return -ENOTSUPP;

	if (!mvm->nvm_data)
		return -EBUSY;

	/* mac80211 ensures the device is not started,
	 * so the firmware cannot be running
	 */

	mvm->set_tx_ant = tx_ant;
	mvm->set_rx_ant = rx_ant;

	iwl_reinit_cab(mvm->trans, mvm->nvm_data, tx_ant, rx_ant, mvm->fw);

	return 0;
}

int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
	struct ieee80211_hw *hw = mvm->hw;
@@ -6202,6 +6226,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
	.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
	.ampdu_action = iwl_mvm_mac_ampdu_action,
	.get_antenna = iwl_mvm_op_get_antenna,
	.set_antenna = iwl_mvm_op_set_antenna,
	.start = iwl_mvm_mac_start,
	.reconfig_complete = iwl_mvm_mac_reconfig_complete,
	.stop = iwl_mvm_mac_stop,
Loading