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