File Index
Files and directories
Sign in
FILE

phy.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/phy.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2/*
  3 * Copyright (C) 2024-2025 Intel Corporation
  4 */
  5#include <net/mac80211.h>
  6
  7#include "phy.h"
  8#include "hcmd.h"
  9#include "fw/api/phy-ctxt.h"
 10
 11int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld)
 12{
 13	int id;
 14	unsigned long used = mld->used_phy_ids;
 15
 16	for_each_clear_bit(id, &used, NUM_PHY_CTX) {
 17		mld->used_phy_ids |= BIT(id);
 18		return id;
 19	}
 20
 21	return -ENOSPC;
 22}
 23EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id);
 24
 25struct iwl_mld_chanctx_usage_data {
 26	struct iwl_mld *mld;
 27	struct ieee80211_chanctx_conf *ctx;
 28	bool use_def;
 29};
 30
 31static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif,
 32					 struct ieee80211_chanctx_conf *ctx)
 33{
 34	if (vif->type != NL80211_IFTYPE_AP)
 35		return false;
 36
 37	return cfg80211_channel_is_psc(ctx->def.chan) ||
 38		(ctx->def.chan->band == NL80211_BAND_6GHZ &&
 39		 ctx->def.width >= NL80211_CHAN_WIDTH_80);
 40}
 41
 42static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac,
 43				       struct ieee80211_vif *vif)
 44{
 45	struct iwl_mld_chanctx_usage_data *data = _data;
 46	struct ieee80211_bss_conf *link_conf;
 47	int link_id;
 48
 49	for_each_vif_active_link(vif, link_conf, link_id) {
 50		if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx)
 51			continue;
 52
 53		if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder)
 54			data->use_def = true;
 55
 56		if (iwl_mld_chanctx_fils_enabled(vif, data->ctx))
 57			data->use_def = true;
 58	}
 59}
 60
 61struct cfg80211_chan_def *
 62iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld,
 63				 struct ieee80211_chanctx_conf *ctx)
 64{
 65	struct iwl_mld_chanctx_usage_data data = {
 66		.mld = mld,
 67		.ctx = ctx,
 68	};
 69
 70	ieee80211_iterate_active_interfaces_mtx(mld->hw,
 71						IEEE80211_IFACE_ITER_NORMAL,
 72						iwl_mld_chanctx_usage_iter,
 73						&data);
 74
 75	return data.use_def ? &ctx->def : &ctx->min_def;
 76}
 77
 78static u8
 79iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)
 80{
 81	switch (width) {
 82	case NL80211_CHAN_WIDTH_20_NOHT:
 83	case NL80211_CHAN_WIDTH_20:
 84		return IWL_PHY_CHANNEL_MODE20;
 85	case NL80211_CHAN_WIDTH_40:
 86		return IWL_PHY_CHANNEL_MODE40;
 87	case NL80211_CHAN_WIDTH_80:
 88		return IWL_PHY_CHANNEL_MODE80;
 89	case NL80211_CHAN_WIDTH_160:
 90		return IWL_PHY_CHANNEL_MODE160;
 91	case NL80211_CHAN_WIDTH_320:
 92		return IWL_PHY_CHANNEL_MODE320;
 93	default:
 94		WARN(1, "Invalid channel width=%u", width);
 95		return IWL_PHY_CHANNEL_MODE20;
 96	}
 97}
 98
 99/* Maps the driver specific control channel position (relative to the center
100 * freq) definitions to the fw values
101 */
102u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
103{
104	int offs = chandef->chan->center_freq - chandef->center_freq1;
105	int abs_offs = abs(offs);
106	u8 ret;
107
108	if (offs == 0) {
109		/* The FW is expected to check the control channel position only
110		 * when in HT/VHT and the channel width is not 20MHz. Return
111		 * this value as the default one.
112		 */
113		return 0;
114	}
115
116	/* this results in a value 0-7, i.e. fitting into 0b0111 */
117	ret = (abs_offs - 10) / 20;
118	/* But we need the value to be in 0b1011 because 0b0100 is
119	 * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in
120	 * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)
121	 */
122	ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |
123	      ((ret & BIT(2)) << 1);
124	/* and add the above bit */
125	ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;
126
127	return ret;
128}
129
130int iwl_mld_phy_fw_action(struct iwl_mld *mld,
131			  struct ieee80211_chanctx_conf *ctx, u32 action)
132{
133	struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
134	struct cfg80211_chan_def *chandef = &phy->chandef;
135	struct iwl_phy_context_cmd cmd = {
136		.id_and_color = cpu_to_le32(phy->fw_id),
137		.action = cpu_to_le32(action),
138		.puncture_mask = cpu_to_le16(chandef->punctured),
139		/* Channel info */
140		.ci.channel = cpu_to_le32(chandef->chan->hw_value),
141		.ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band),
142		.ci.width = iwl_mld_nl80211_width_to_fw(chandef->width),
143		.ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef),
144	};
145	int ret;
146
147	if (ctx->ap.chan) {
148		cmd.sbb_bandwidth =
149			iwl_mld_nl80211_width_to_fw(ctx->ap.width);
150		cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);
151	}
152
153	ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd);
154	if (ret)
155		IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret);
156
157	return ret;
158}
159
160static u32 iwl_mld_get_phy_config(struct iwl_mld *mld)
161{
162	u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
163			   FW_PHY_CFG_RX_CHAIN);
164	u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld);
165	u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld);
166
167	phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
168		      valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
169
170	return mld->fw->phy_config & phy_config;
171}
172
173int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld)
174{
175	const struct iwl_tlv_calib_ctrl *default_calib =
176		&mld->fw->default_calib[IWL_UCODE_REGULAR];
177	struct iwl_phy_cfg_cmd_v3 cmd = {
178		.phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)),
179		.calib_control.event_trigger = default_calib->event_trigger,
180		.calib_control.flow_trigger = default_calib->flow_trigger,
181		.phy_specific_cfg = mld->fwrt.phy_filters,
182	};
183
184	IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);
185
186	return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd);
187}
188
189void iwl_mld_update_phy_chandef(struct iwl_mld *mld,
190				struct ieee80211_chanctx_conf *ctx)
191{
192	struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
193	struct cfg80211_chan_def *chandef =
194		iwl_mld_get_chandef_from_chanctx(mld, ctx);
195
196	phy->chandef = *chandef;
197	iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY);
198}
~/ Read-only file preview.