xref: /linux/drivers/net/wireless/realtek/rtw89/core.c (revision 073350da0aa2aead9df7927a1c1046ebf5cdd816)
1e3ec7017SPing-Ke Shih // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2e3ec7017SPing-Ke Shih /* Copyright(c) 2019-2020  Realtek Corporation
3e3ec7017SPing-Ke Shih  */
415fcb103SJakub Kicinski #include <linux/ip.h>
515fcb103SJakub Kicinski #include <linux/udp.h>
6e3ec7017SPing-Ke Shih 
72ab856ccSPing-Ke Shih #include "cam.h"
8e3ec7017SPing-Ke Shih #include "coex.h"
9e3ec7017SPing-Ke Shih #include "core.h"
10e3ec7017SPing-Ke Shih #include "efuse.h"
11e3ec7017SPing-Ke Shih #include "fw.h"
12e3ec7017SPing-Ke Shih #include "mac.h"
13e3ec7017SPing-Ke Shih #include "phy.h"
14e3ec7017SPing-Ke Shih #include "ps.h"
15e3ec7017SPing-Ke Shih #include "reg.h"
16e3ec7017SPing-Ke Shih #include "sar.h"
17e3ec7017SPing-Ke Shih #include "ser.h"
18e3ec7017SPing-Ke Shih #include "txrx.h"
19e3ec7017SPing-Ke Shih #include "util.h"
20e3ec7017SPing-Ke Shih 
21e3ec7017SPing-Ke Shih static bool rtw89_disable_ps_mode;
22e3ec7017SPing-Ke Shih module_param_named(disable_ps_mode, rtw89_disable_ps_mode, bool, 0644);
23e3ec7017SPing-Ke Shih MODULE_PARM_DESC(disable_ps_mode, "Set Y to disable low power mode");
24e3ec7017SPing-Ke Shih 
250237f65aSZong-Zhe Yang #define RTW89_DEF_CHAN(_freq, _hw_val, _flags, _band)	\
260237f65aSZong-Zhe Yang 	{ .center_freq = _freq, .hw_value = _hw_val, .flags = _flags, .band = _band, }
270237f65aSZong-Zhe Yang #define RTW89_DEF_CHAN_2G(_freq, _hw_val)	\
280237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_2GHZ)
290237f65aSZong-Zhe Yang #define RTW89_DEF_CHAN_5G(_freq, _hw_val)	\
300237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_5GHZ)
310237f65aSZong-Zhe Yang #define RTW89_DEF_CHAN_5G_NO_HT40MINUS(_freq, _hw_val)	\
320237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN(_freq, _hw_val, IEEE80211_CHAN_NO_HT40MINUS, NL80211_BAND_5GHZ)
330237f65aSZong-Zhe Yang #define RTW89_DEF_CHAN_6G(_freq, _hw_val)	\
340237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN(_freq, _hw_val, 0, NL80211_BAND_6GHZ)
350237f65aSZong-Zhe Yang 
36e3ec7017SPing-Ke Shih static struct ieee80211_channel rtw89_channels_2ghz[] = {
370237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2412, 1),
380237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2417, 2),
390237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2422, 3),
400237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2427, 4),
410237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2432, 5),
420237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2437, 6),
430237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2442, 7),
440237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2447, 8),
450237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2452, 9),
460237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2457, 10),
470237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2462, 11),
480237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2467, 12),
490237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2472, 13),
500237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_2G(2484, 14),
51e3ec7017SPing-Ke Shih };
52e3ec7017SPing-Ke Shih 
53e3ec7017SPing-Ke Shih static struct ieee80211_channel rtw89_channels_5ghz[] = {
540237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5180, 36),
550237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5200, 40),
560237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5220, 44),
570237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5240, 48),
580237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5260, 52),
590237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5280, 56),
600237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5300, 60),
610237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5320, 64),
620237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5500, 100),
630237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5520, 104),
640237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5540, 108),
650237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5560, 112),
660237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5580, 116),
670237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5600, 120),
680237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5620, 124),
690237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5640, 128),
700237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5660, 132),
710237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5680, 136),
720237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5700, 140),
730237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5720, 144),
740237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5745, 149),
750237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5765, 153),
760237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5785, 157),
770237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G(5805, 161),
780237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_5G_NO_HT40MINUS(5825, 165),
790237f65aSZong-Zhe Yang };
800237f65aSZong-Zhe Yang 
810237f65aSZong-Zhe Yang static struct ieee80211_channel rtw89_channels_6ghz[] = {
820237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(5955, 1),
830237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(5975, 5),
840237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(5995, 9),
850237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6015, 13),
860237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6035, 17),
870237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6055, 21),
880237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6075, 25),
890237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6095, 29),
900237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6115, 33),
910237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6135, 37),
920237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6155, 41),
930237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6175, 45),
940237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6195, 49),
950237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6215, 53),
960237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6235, 57),
970237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6255, 61),
980237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6275, 65),
990237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6295, 69),
1000237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6315, 73),
1010237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6335, 77),
1020237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6355, 81),
1030237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6375, 85),
1040237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6395, 89),
1050237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6415, 93),
1060237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6435, 97),
1070237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6455, 101),
1080237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6475, 105),
1090237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6495, 109),
1100237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6515, 113),
1110237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6535, 117),
1120237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6555, 121),
1130237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6575, 125),
1140237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6595, 129),
1150237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6615, 133),
1160237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6635, 137),
1170237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6655, 141),
1180237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6675, 145),
1190237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6695, 149),
1200237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6715, 153),
1210237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6735, 157),
1220237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6755, 161),
1230237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6775, 165),
1240237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6795, 169),
1250237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6815, 173),
1260237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6835, 177),
1270237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6855, 181),
1280237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6875, 185),
1290237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6895, 189),
1300237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6915, 193),
1310237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6935, 197),
1320237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6955, 201),
1330237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6975, 205),
1340237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(6995, 209),
1350237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7015, 213),
1360237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7035, 217),
1370237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7055, 221),
1380237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7075, 225),
1390237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7095, 229),
1400237f65aSZong-Zhe Yang 	RTW89_DEF_CHAN_6G(7115, 233),
141e3ec7017SPing-Ke Shih };
142e3ec7017SPing-Ke Shih 
143e3ec7017SPing-Ke Shih static struct ieee80211_rate rtw89_bitrates[] = {
144e3ec7017SPing-Ke Shih 	{ .bitrate = 10,  .hw_value = 0x00, },
145e3ec7017SPing-Ke Shih 	{ .bitrate = 20,  .hw_value = 0x01, },
146e3ec7017SPing-Ke Shih 	{ .bitrate = 55,  .hw_value = 0x02, },
147e3ec7017SPing-Ke Shih 	{ .bitrate = 110, .hw_value = 0x03, },
148e3ec7017SPing-Ke Shih 	{ .bitrate = 60,  .hw_value = 0x04, },
149e3ec7017SPing-Ke Shih 	{ .bitrate = 90,  .hw_value = 0x05, },
150e3ec7017SPing-Ke Shih 	{ .bitrate = 120, .hw_value = 0x06, },
151e3ec7017SPing-Ke Shih 	{ .bitrate = 180, .hw_value = 0x07, },
152e3ec7017SPing-Ke Shih 	{ .bitrate = 240, .hw_value = 0x08, },
153e3ec7017SPing-Ke Shih 	{ .bitrate = 360, .hw_value = 0x09, },
154e3ec7017SPing-Ke Shih 	{ .bitrate = 480, .hw_value = 0x0a, },
155e3ec7017SPing-Ke Shih 	{ .bitrate = 540, .hw_value = 0x0b, },
156e3ec7017SPing-Ke Shih };
157e3ec7017SPing-Ke Shih 
158e3ec7017SPing-Ke Shih u16 rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate)
159e3ec7017SPing-Ke Shih {
160e3ec7017SPing-Ke Shih 	struct ieee80211_rate rate;
161e3ec7017SPing-Ke Shih 
162e3ec7017SPing-Ke Shih 	if (unlikely(rpt_rate >= ARRAY_SIZE(rtw89_bitrates))) {
163e3ec7017SPing-Ke Shih 		rtw89_info(rtwdev, "invalid rpt rate %d\n", rpt_rate);
164e3ec7017SPing-Ke Shih 		return 0;
165e3ec7017SPing-Ke Shih 	}
166e3ec7017SPing-Ke Shih 
167e3ec7017SPing-Ke Shih 	rate = rtw89_bitrates[rpt_rate];
168e3ec7017SPing-Ke Shih 
169e3ec7017SPing-Ke Shih 	return rate.bitrate;
170e3ec7017SPing-Ke Shih }
171e3ec7017SPing-Ke Shih 
172e3ec7017SPing-Ke Shih static struct ieee80211_supported_band rtw89_sband_2ghz = {
173e3ec7017SPing-Ke Shih 	.band		= NL80211_BAND_2GHZ,
174e3ec7017SPing-Ke Shih 	.channels	= rtw89_channels_2ghz,
175e3ec7017SPing-Ke Shih 	.n_channels	= ARRAY_SIZE(rtw89_channels_2ghz),
176e3ec7017SPing-Ke Shih 	.bitrates	= rtw89_bitrates,
177e3ec7017SPing-Ke Shih 	.n_bitrates	= ARRAY_SIZE(rtw89_bitrates),
178e3ec7017SPing-Ke Shih 	.ht_cap		= {0},
179e3ec7017SPing-Ke Shih 	.vht_cap	= {0},
180e3ec7017SPing-Ke Shih };
181e3ec7017SPing-Ke Shih 
182e3ec7017SPing-Ke Shih static struct ieee80211_supported_band rtw89_sband_5ghz = {
183e3ec7017SPing-Ke Shih 	.band		= NL80211_BAND_5GHZ,
184e3ec7017SPing-Ke Shih 	.channels	= rtw89_channels_5ghz,
185e3ec7017SPing-Ke Shih 	.n_channels	= ARRAY_SIZE(rtw89_channels_5ghz),
186e3ec7017SPing-Ke Shih 
187e3ec7017SPing-Ke Shih 	/* 5G has no CCK rates, 1M/2M/5.5M/11M */
188e3ec7017SPing-Ke Shih 	.bitrates	= rtw89_bitrates + 4,
189e3ec7017SPing-Ke Shih 	.n_bitrates	= ARRAY_SIZE(rtw89_bitrates) - 4,
190e3ec7017SPing-Ke Shih 	.ht_cap		= {0},
191e3ec7017SPing-Ke Shih 	.vht_cap	= {0},
192e3ec7017SPing-Ke Shih };
193e3ec7017SPing-Ke Shih 
1940237f65aSZong-Zhe Yang static struct ieee80211_supported_band rtw89_sband_6ghz = {
1950237f65aSZong-Zhe Yang 	.band		= NL80211_BAND_6GHZ,
1960237f65aSZong-Zhe Yang 	.channels	= rtw89_channels_6ghz,
1970237f65aSZong-Zhe Yang 	.n_channels	= ARRAY_SIZE(rtw89_channels_6ghz),
1980237f65aSZong-Zhe Yang 
1990237f65aSZong-Zhe Yang 	/* 6G has no CCK rates, 1M/2M/5.5M/11M */
2000237f65aSZong-Zhe Yang 	.bitrates	= rtw89_bitrates + 4,
2010237f65aSZong-Zhe Yang 	.n_bitrates	= ARRAY_SIZE(rtw89_bitrates) - 4,
2020237f65aSZong-Zhe Yang };
2030237f65aSZong-Zhe Yang 
204e3ec7017SPing-Ke Shih static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev,
205e3ec7017SPing-Ke Shih 				     struct rtw89_traffic_stats *stats,
206e3ec7017SPing-Ke Shih 				     struct sk_buff *skb, bool tx)
207e3ec7017SPing-Ke Shih {
208e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
209e3ec7017SPing-Ke Shih 
210e3ec7017SPing-Ke Shih 	if (!ieee80211_is_data(hdr->frame_control))
211e3ec7017SPing-Ke Shih 		return;
212e3ec7017SPing-Ke Shih 
213e3ec7017SPing-Ke Shih 	if (is_broadcast_ether_addr(hdr->addr1) ||
214e3ec7017SPing-Ke Shih 	    is_multicast_ether_addr(hdr->addr1))
215e3ec7017SPing-Ke Shih 		return;
216e3ec7017SPing-Ke Shih 
217e3ec7017SPing-Ke Shih 	if (tx) {
218e3ec7017SPing-Ke Shih 		stats->tx_cnt++;
219e3ec7017SPing-Ke Shih 		stats->tx_unicast += skb->len;
220e3ec7017SPing-Ke Shih 	} else {
221e3ec7017SPing-Ke Shih 		stats->rx_cnt++;
222e3ec7017SPing-Ke Shih 		stats->rx_unicast += skb->len;
223e3ec7017SPing-Ke Shih 	}
224e3ec7017SPing-Ke Shih }
225e3ec7017SPing-Ke Shih 
226e3ec7017SPing-Ke Shih static void rtw89_get_channel_params(struct cfg80211_chan_def *chandef,
227e3ec7017SPing-Ke Shih 				     struct rtw89_channel_params *chan_param)
228e3ec7017SPing-Ke Shih {
229e3ec7017SPing-Ke Shih 	struct ieee80211_channel *channel = chandef->chan;
230e3ec7017SPing-Ke Shih 	enum nl80211_chan_width width = chandef->width;
231e3ec7017SPing-Ke Shih 	u32 primary_freq, center_freq;
232e3ec7017SPing-Ke Shih 	u8 center_chan;
233e3ec7017SPing-Ke Shih 	u8 bandwidth = RTW89_CHANNEL_WIDTH_20;
234e3ec7017SPing-Ke Shih 	u8 primary_chan_idx = 0;
235e715f10fSPing-Ke Shih 	u32 offset;
2360237f65aSZong-Zhe Yang 	u8 band;
237e0925375SZong-Zhe Yang 	u8 subband;
238e3ec7017SPing-Ke Shih 
239e3ec7017SPing-Ke Shih 	center_chan = channel->hw_value;
240e3ec7017SPing-Ke Shih 	primary_freq = channel->center_freq;
241e3ec7017SPing-Ke Shih 	center_freq = chandef->center_freq1;
242e3ec7017SPing-Ke Shih 
243e3ec7017SPing-Ke Shih 	switch (width) {
244e3ec7017SPing-Ke Shih 	case NL80211_CHAN_WIDTH_20_NOHT:
245e3ec7017SPing-Ke Shih 	case NL80211_CHAN_WIDTH_20:
246e3ec7017SPing-Ke Shih 		bandwidth = RTW89_CHANNEL_WIDTH_20;
247e3ec7017SPing-Ke Shih 		primary_chan_idx = RTW89_SC_DONT_CARE;
248e3ec7017SPing-Ke Shih 		break;
249e3ec7017SPing-Ke Shih 	case NL80211_CHAN_WIDTH_40:
250e3ec7017SPing-Ke Shih 		bandwidth = RTW89_CHANNEL_WIDTH_40;
251e3ec7017SPing-Ke Shih 		if (primary_freq > center_freq) {
252e3ec7017SPing-Ke Shih 			primary_chan_idx = RTW89_SC_20_UPPER;
253e3ec7017SPing-Ke Shih 			center_chan -= 2;
254e3ec7017SPing-Ke Shih 		} else {
255e3ec7017SPing-Ke Shih 			primary_chan_idx = RTW89_SC_20_LOWER;
256e3ec7017SPing-Ke Shih 			center_chan += 2;
257e3ec7017SPing-Ke Shih 		}
258e3ec7017SPing-Ke Shih 		break;
259e3ec7017SPing-Ke Shih 	case NL80211_CHAN_WIDTH_80:
260e715f10fSPing-Ke Shih 	case NL80211_CHAN_WIDTH_160:
261e715f10fSPing-Ke Shih 		bandwidth = nl_to_rtw89_bandwidth(width);
262e3ec7017SPing-Ke Shih 		if (primary_freq > center_freq) {
263e715f10fSPing-Ke Shih 			offset = (primary_freq - center_freq - 10) / 20;
264e715f10fSPing-Ke Shih 			primary_chan_idx = RTW89_SC_20_UPPER + offset * 2;
265e715f10fSPing-Ke Shih 			center_chan -= 2 + offset * 4;
266e3ec7017SPing-Ke Shih 		} else {
267e715f10fSPing-Ke Shih 			offset = (center_freq - primary_freq - 10) / 20;
268e715f10fSPing-Ke Shih 			primary_chan_idx = RTW89_SC_20_LOWER + offset * 2;
269e715f10fSPing-Ke Shih 			center_chan += 2 + offset * 4;
270e3ec7017SPing-Ke Shih 		}
271e3ec7017SPing-Ke Shih 		break;
272e3ec7017SPing-Ke Shih 	default:
273e3ec7017SPing-Ke Shih 		center_chan = 0;
274e3ec7017SPing-Ke Shih 		break;
275e3ec7017SPing-Ke Shih 	}
276e3ec7017SPing-Ke Shih 
2770237f65aSZong-Zhe Yang 	switch (channel->band) {
2780237f65aSZong-Zhe Yang 	default:
2790237f65aSZong-Zhe Yang 	case NL80211_BAND_2GHZ:
2800237f65aSZong-Zhe Yang 		band = RTW89_BAND_2G;
2810237f65aSZong-Zhe Yang 		break;
2820237f65aSZong-Zhe Yang 	case NL80211_BAND_5GHZ:
2830237f65aSZong-Zhe Yang 		band = RTW89_BAND_5G;
2840237f65aSZong-Zhe Yang 		break;
2850237f65aSZong-Zhe Yang 	case NL80211_BAND_6GHZ:
2860237f65aSZong-Zhe Yang 		band = RTW89_BAND_6G;
2870237f65aSZong-Zhe Yang 		break;
2880237f65aSZong-Zhe Yang 	}
2890237f65aSZong-Zhe Yang 
2908e438ad4SZong-Zhe Yang 	switch (band) {
2918e438ad4SZong-Zhe Yang 	default:
2928e438ad4SZong-Zhe Yang 	case RTW89_BAND_2G:
293e0925375SZong-Zhe Yang 		switch (center_chan) {
294e0925375SZong-Zhe Yang 		default:
295e0925375SZong-Zhe Yang 		case 1 ... 14:
296e0925375SZong-Zhe Yang 			subband = RTW89_CH_2G;
297e0925375SZong-Zhe Yang 			break;
2988e438ad4SZong-Zhe Yang 		}
2998e438ad4SZong-Zhe Yang 		break;
3008e438ad4SZong-Zhe Yang 	case RTW89_BAND_5G:
3018e438ad4SZong-Zhe Yang 		switch (center_chan) {
3028e438ad4SZong-Zhe Yang 		default:
303e0925375SZong-Zhe Yang 		case 36 ... 64:
304e0925375SZong-Zhe Yang 			subband = RTW89_CH_5G_BAND_1;
305e0925375SZong-Zhe Yang 			break;
306e0925375SZong-Zhe Yang 		case 100 ... 144:
307e0925375SZong-Zhe Yang 			subband = RTW89_CH_5G_BAND_3;
308e0925375SZong-Zhe Yang 			break;
309e0925375SZong-Zhe Yang 		case 149 ... 177:
310e0925375SZong-Zhe Yang 			subband = RTW89_CH_5G_BAND_4;
311e0925375SZong-Zhe Yang 			break;
312e0925375SZong-Zhe Yang 		}
3138e438ad4SZong-Zhe Yang 		break;
3148e438ad4SZong-Zhe Yang 	case RTW89_BAND_6G:
3158e438ad4SZong-Zhe Yang 		switch (center_chan) {
3168e438ad4SZong-Zhe Yang 		default:
3178e438ad4SZong-Zhe Yang 		case 1 ... 29:
3188e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX0;
3198e438ad4SZong-Zhe Yang 			break;
3208e438ad4SZong-Zhe Yang 		case 33 ... 61:
3218e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX1;
3228e438ad4SZong-Zhe Yang 			break;
3238e438ad4SZong-Zhe Yang 		case 65 ... 93:
3248e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX2;
3258e438ad4SZong-Zhe Yang 			break;
3268e438ad4SZong-Zhe Yang 		case 97 ... 125:
3278e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX3;
3288e438ad4SZong-Zhe Yang 			break;
3298e438ad4SZong-Zhe Yang 		case 129 ... 157:
3308e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX4;
3318e438ad4SZong-Zhe Yang 			break;
3328e438ad4SZong-Zhe Yang 		case 161 ... 189:
3338e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX5;
3348e438ad4SZong-Zhe Yang 			break;
3358e438ad4SZong-Zhe Yang 		case 193 ... 221:
3368e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX6;
3378e438ad4SZong-Zhe Yang 			break;
3388e438ad4SZong-Zhe Yang 		case 225 ... 253:
3398e438ad4SZong-Zhe Yang 			subband = RTW89_CH_6G_BAND_IDX7;
3408e438ad4SZong-Zhe Yang 			break;
3418e438ad4SZong-Zhe Yang 		}
3428e438ad4SZong-Zhe Yang 		break;
3438e438ad4SZong-Zhe Yang 	}
344e0925375SZong-Zhe Yang 
345e3ec7017SPing-Ke Shih 	chan_param->center_chan = center_chan;
346e715f10fSPing-Ke Shih 	chan_param->center_freq = center_freq;
347e3ec7017SPing-Ke Shih 	chan_param->primary_chan = channel->hw_value;
348e3ec7017SPing-Ke Shih 	chan_param->bandwidth = bandwidth;
349e3ec7017SPing-Ke Shih 	chan_param->pri_ch_idx = primary_chan_idx;
3500237f65aSZong-Zhe Yang 	chan_param->band_type = band;
351e0925375SZong-Zhe Yang 	chan_param->subband_type = subband;
352e3ec7017SPing-Ke Shih }
353e3ec7017SPing-Ke Shih 
354e3ec7017SPing-Ke Shih void rtw89_set_channel(struct rtw89_dev *rtwdev)
355e3ec7017SPing-Ke Shih {
356e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
357e3ec7017SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
358e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
359e3ec7017SPing-Ke Shih 	struct rtw89_channel_params ch_param;
360e3ec7017SPing-Ke Shih 	struct rtw89_channel_help_params bak;
361e3ec7017SPing-Ke Shih 	u8 center_chan, bandwidth;
362e3ec7017SPing-Ke Shih 	bool band_changed;
363e3ec7017SPing-Ke Shih 
364e3ec7017SPing-Ke Shih 	rtw89_get_channel_params(&hw->conf.chandef, &ch_param);
365e3ec7017SPing-Ke Shih 	if (WARN(ch_param.center_chan == 0, "Invalid channel\n"))
366e3ec7017SPing-Ke Shih 		return;
367e3ec7017SPing-Ke Shih 
368e3ec7017SPing-Ke Shih 	center_chan = ch_param.center_chan;
369e3ec7017SPing-Ke Shih 	bandwidth = ch_param.bandwidth;
3700237f65aSZong-Zhe Yang 	band_changed = hal->current_band_type != ch_param.band_type ||
371e3ec7017SPing-Ke Shih 		       hal->current_channel == 0;
372e3ec7017SPing-Ke Shih 
373e3ec7017SPing-Ke Shih 	hal->current_band_width = bandwidth;
374e3ec7017SPing-Ke Shih 	hal->current_channel = center_chan;
375e715f10fSPing-Ke Shih 	hal->current_freq = ch_param.center_freq;
376eb4e52b3SPo Hao Huang 	hal->prev_primary_channel = hal->current_primary_channel;
37789590777SPo Hao Huang 	hal->prev_band_type = hal->current_band_type;
378e3ec7017SPing-Ke Shih 	hal->current_primary_channel = ch_param.primary_chan;
3790237f65aSZong-Zhe Yang 	hal->current_band_type = ch_param.band_type;
380e0925375SZong-Zhe Yang 	hal->current_subband = ch_param.subband_type;
381e3ec7017SPing-Ke Shih 
382e3ec7017SPing-Ke Shih 	rtw89_chip_set_channel_prepare(rtwdev, &bak);
383e3ec7017SPing-Ke Shih 
384e3ec7017SPing-Ke Shih 	chip->ops->set_channel(rtwdev, &ch_param);
385e3ec7017SPing-Ke Shih 
386e3ec7017SPing-Ke Shih 	rtw89_chip_set_txpwr(rtwdev);
387e3ec7017SPing-Ke Shih 
388e3ec7017SPing-Ke Shih 	rtw89_chip_set_channel_done(rtwdev, &bak);
389e3ec7017SPing-Ke Shih 
390e3ec7017SPing-Ke Shih 	if (band_changed) {
391e3ec7017SPing-Ke Shih 		rtw89_btc_ntfy_switch_band(rtwdev, RTW89_PHY_0, hal->current_band_type);
392e3ec7017SPing-Ke Shih 		rtw89_chip_rfk_band_changed(rtwdev);
393e3ec7017SPing-Ke Shih 	}
394e3ec7017SPing-Ke Shih }
395e3ec7017SPing-Ke Shih 
396e3ec7017SPing-Ke Shih static enum rtw89_core_tx_type
397e3ec7017SPing-Ke Shih rtw89_core_get_tx_type(struct rtw89_dev *rtwdev,
398e3ec7017SPing-Ke Shih 		       struct sk_buff *skb)
399e3ec7017SPing-Ke Shih {
400e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (void *)skb->data;
401e3ec7017SPing-Ke Shih 	__le16 fc = hdr->frame_control;
402e3ec7017SPing-Ke Shih 
403e3ec7017SPing-Ke Shih 	if (ieee80211_is_mgmt(fc) || ieee80211_is_nullfunc(fc))
404e3ec7017SPing-Ke Shih 		return RTW89_CORE_TX_TYPE_MGMT;
405e3ec7017SPing-Ke Shih 
406e3ec7017SPing-Ke Shih 	return RTW89_CORE_TX_TYPE_DATA;
407e3ec7017SPing-Ke Shih }
408e3ec7017SPing-Ke Shih 
409e3ec7017SPing-Ke Shih static void
410e3ec7017SPing-Ke Shih rtw89_core_tx_update_ampdu_info(struct rtw89_dev *rtwdev,
411e3ec7017SPing-Ke Shih 				struct rtw89_core_tx_request *tx_req, u8 tid)
412e3ec7017SPing-Ke Shih {
413e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = tx_req->sta;
414e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
415e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta;
416e3ec7017SPing-Ke Shih 	u8 ampdu_num;
417e3ec7017SPing-Ke Shih 
418e3ec7017SPing-Ke Shih 	if (!sta) {
419e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "cannot set ampdu info without sta\n");
420e3ec7017SPing-Ke Shih 		return;
421e3ec7017SPing-Ke Shih 	}
422e3ec7017SPing-Ke Shih 
423e3ec7017SPing-Ke Shih 	rtwsta = (struct rtw89_sta *)sta->drv_priv;
424e3ec7017SPing-Ke Shih 
425e3ec7017SPing-Ke Shih 	ampdu_num = (u8)((rtwsta->ampdu_params[tid].agg_num ?
426e3ec7017SPing-Ke Shih 			  rtwsta->ampdu_params[tid].agg_num :
427046d2e7cSSriram R 			  4 << sta->deflink.ht_cap.ampdu_factor) - 1);
428e3ec7017SPing-Ke Shih 
429e3ec7017SPing-Ke Shih 	desc_info->agg_en = true;
430046d2e7cSSriram R 	desc_info->ampdu_density = sta->deflink.ht_cap.ampdu_density;
431e3ec7017SPing-Ke Shih 	desc_info->ampdu_num = ampdu_num;
432e3ec7017SPing-Ke Shih }
433e3ec7017SPing-Ke Shih 
434e3ec7017SPing-Ke Shih static void
435e3ec7017SPing-Ke Shih rtw89_core_tx_update_sec_key(struct rtw89_dev *rtwdev,
436e3ec7017SPing-Ke Shih 			     struct rtw89_core_tx_request *tx_req)
437e3ec7017SPing-Ke Shih {
43879a6c9a4SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
439e3ec7017SPing-Ke Shih 	struct ieee80211_vif *vif = tx_req->vif;
4402ab856ccSPing-Ke Shih 	struct ieee80211_sta *sta = tx_req->sta;
441e3ec7017SPing-Ke Shih 	struct ieee80211_tx_info *info;
442e3ec7017SPing-Ke Shih 	struct ieee80211_key_conf *key;
443e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif;
4442ab856ccSPing-Ke Shih 	struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta);
445e3ec7017SPing-Ke Shih 	struct rtw89_addr_cam_entry *addr_cam;
446e3ec7017SPing-Ke Shih 	struct rtw89_sec_cam_entry *sec_cam;
447e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
448e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
449e3ec7017SPing-Ke Shih 	u8 sec_type = RTW89_SEC_KEY_TYPE_NONE;
45079a6c9a4SPing-Ke Shih 	u64 pn64;
451e3ec7017SPing-Ke Shih 
452e3ec7017SPing-Ke Shih 	if (!vif) {
453e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "cannot set sec key without vif\n");
454e3ec7017SPing-Ke Shih 		return;
455e3ec7017SPing-Ke Shih 	}
456e3ec7017SPing-Ke Shih 
457e3ec7017SPing-Ke Shih 	rtwvif = (struct rtw89_vif *)vif->drv_priv;
4582ab856ccSPing-Ke Shih 	addr_cam = rtw89_get_addr_cam_of(rtwvif, rtwsta);
459e3ec7017SPing-Ke Shih 
460e3ec7017SPing-Ke Shih 	info = IEEE80211_SKB_CB(skb);
461e3ec7017SPing-Ke Shih 	key = info->control.hw_key;
462e3ec7017SPing-Ke Shih 	sec_cam = addr_cam->sec_entries[key->hw_key_idx];
463e3ec7017SPing-Ke Shih 	if (!sec_cam) {
464e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "sec cam entry is empty\n");
465e3ec7017SPing-Ke Shih 		return;
466e3ec7017SPing-Ke Shih 	}
467e3ec7017SPing-Ke Shih 
468e3ec7017SPing-Ke Shih 	switch (key->cipher) {
469e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_WEP40:
470e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_WEP40;
471e3ec7017SPing-Ke Shih 		break;
472e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_WEP104:
473e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_WEP104;
474e3ec7017SPing-Ke Shih 		break;
475e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_TKIP:
476e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_TKIP;
477e3ec7017SPing-Ke Shih 		break;
478e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_CCMP:
479e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_CCMP128;
480e3ec7017SPing-Ke Shih 		break;
481e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_CCMP_256:
482e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_CCMP256;
483e3ec7017SPing-Ke Shih 		break;
484e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_GCMP:
485e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_GCMP128;
486e3ec7017SPing-Ke Shih 		break;
487e3ec7017SPing-Ke Shih 	case WLAN_CIPHER_SUITE_GCMP_256:
488e3ec7017SPing-Ke Shih 		sec_type = RTW89_SEC_KEY_TYPE_GCMP256;
489e3ec7017SPing-Ke Shih 		break;
490e3ec7017SPing-Ke Shih 	default:
491e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "key cipher not supported %d\n", key->cipher);
492e3ec7017SPing-Ke Shih 		return;
493e3ec7017SPing-Ke Shih 	}
494e3ec7017SPing-Ke Shih 
495e3ec7017SPing-Ke Shih 	desc_info->sec_en = true;
49679a6c9a4SPing-Ke Shih 	desc_info->sec_keyid = key->keyidx;
497e3ec7017SPing-Ke Shih 	desc_info->sec_type = sec_type;
498e3ec7017SPing-Ke Shih 	desc_info->sec_cam_idx = sec_cam->sec_cam_idx;
49979a6c9a4SPing-Ke Shih 
50079a6c9a4SPing-Ke Shih 	if (!chip->hw_sec_hdr)
50179a6c9a4SPing-Ke Shih 		return;
50279a6c9a4SPing-Ke Shih 
50379a6c9a4SPing-Ke Shih 	pn64 = atomic64_inc_return(&key->tx_pn);
50479a6c9a4SPing-Ke Shih 	desc_info->sec_seq[0] = pn64;
50579a6c9a4SPing-Ke Shih 	desc_info->sec_seq[1] = pn64 >> 8;
50679a6c9a4SPing-Ke Shih 	desc_info->sec_seq[2] = pn64 >> 16;
50779a6c9a4SPing-Ke Shih 	desc_info->sec_seq[3] = pn64 >> 24;
50879a6c9a4SPing-Ke Shih 	desc_info->sec_seq[4] = pn64 >> 32;
50979a6c9a4SPing-Ke Shih 	desc_info->sec_seq[5] = pn64 >> 40;
51079a6c9a4SPing-Ke Shih 	desc_info->wp_offset = 1; /* in unit of 8 bytes for security header */
511e3ec7017SPing-Ke Shih }
512e3ec7017SPing-Ke Shih 
513e3ec7017SPing-Ke Shih static u16 rtw89_core_get_mgmt_rate(struct rtw89_dev *rtwdev,
514e3ec7017SPing-Ke Shih 				    struct rtw89_core_tx_request *tx_req)
515e3ec7017SPing-Ke Shih {
516e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
517e3ec7017SPing-Ke Shih 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
518e3ec7017SPing-Ke Shih 	struct ieee80211_vif *vif = tx_info->control.vif;
519e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
520e3ec7017SPing-Ke Shih 	u16 lowest_rate = hal->current_band_type == RTW89_BAND_2G ?
521e3ec7017SPing-Ke Shih 			  RTW89_HW_RATE_CCK1 : RTW89_HW_RATE_OFDM6;
522e3ec7017SPing-Ke Shih 
523e3ec7017SPing-Ke Shih 	if (!vif || !vif->bss_conf.basic_rates || !tx_req->sta)
524e3ec7017SPing-Ke Shih 		return lowest_rate;
525e3ec7017SPing-Ke Shih 
526e3ec7017SPing-Ke Shih 	return __ffs(vif->bss_conf.basic_rates) + lowest_rate;
527e3ec7017SPing-Ke Shih }
528e3ec7017SPing-Ke Shih 
529e3ec7017SPing-Ke Shih static void
530e3ec7017SPing-Ke Shih rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev,
531e3ec7017SPing-Ke Shih 			       struct rtw89_core_tx_request *tx_req)
532e3ec7017SPing-Ke Shih {
5339eecaec2SPing-Ke Shih 	struct ieee80211_vif *vif = tx_req->vif;
5349eecaec2SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
535e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
536e3ec7017SPing-Ke Shih 	u8 qsel, ch_dma;
537e3ec7017SPing-Ke Shih 
53811d261f2SPing-Ke Shih 	qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : RTW89_TX_QSEL_B0_MGMT;
539e3ec7017SPing-Ke Shih 	ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
540e3ec7017SPing-Ke Shih 
54111d261f2SPing-Ke Shih 	desc_info->qsel = qsel;
542e3ec7017SPing-Ke Shih 	desc_info->ch_dma = ch_dma;
5439eecaec2SPing-Ke Shih 	desc_info->port = desc_info->hiq ? rtwvif->port : 0;
54491644020SPing-Ke Shih 	desc_info->hw_ssn_sel = RTW89_MGMT_HW_SSN_SEL;
54591644020SPing-Ke Shih 	desc_info->hw_seq_mode = RTW89_MGMT_HW_SEQ_MODE;
546e3ec7017SPing-Ke Shih 
547e3ec7017SPing-Ke Shih 	/* fixed data rate for mgmt frames */
548e3ec7017SPing-Ke Shih 	desc_info->en_wd_info = true;
549e3ec7017SPing-Ke Shih 	desc_info->use_rate = true;
550e3ec7017SPing-Ke Shih 	desc_info->dis_data_fb = true;
551e3ec7017SPing-Ke Shih 	desc_info->data_rate = rtw89_core_get_mgmt_rate(rtwdev, tx_req);
552e3ec7017SPing-Ke Shih 
553e3ec7017SPing-Ke Shih 	rtw89_debug(rtwdev, RTW89_DBG_TXRX,
554e3ec7017SPing-Ke Shih 		    "tx mgmt frame with rate 0x%x on channel %d (bw %d)\n",
555e3ec7017SPing-Ke Shih 		    desc_info->data_rate, rtwdev->hal.current_channel,
556e3ec7017SPing-Ke Shih 		    rtwdev->hal.current_band_width);
557e3ec7017SPing-Ke Shih }
558e3ec7017SPing-Ke Shih 
559e3ec7017SPing-Ke Shih static void
560e3ec7017SPing-Ke Shih rtw89_core_tx_update_h2c_info(struct rtw89_dev *rtwdev,
561e3ec7017SPing-Ke Shih 			      struct rtw89_core_tx_request *tx_req)
562e3ec7017SPing-Ke Shih {
563e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
564e3ec7017SPing-Ke Shih 
565e3ec7017SPing-Ke Shih 	desc_info->is_bmc = false;
566e3ec7017SPing-Ke Shih 	desc_info->wd_page = false;
567e3ec7017SPing-Ke Shih 	desc_info->ch_dma = RTW89_DMA_H2C;
568e3ec7017SPing-Ke Shih }
569e3ec7017SPing-Ke Shih 
570e3ec7017SPing-Ke Shih static void rtw89_core_get_no_ul_ofdma_htc(struct rtw89_dev *rtwdev, __le32 *htc)
571e3ec7017SPing-Ke Shih {
572e3ec7017SPing-Ke Shih 	static const u8 rtw89_bandwidth_to_om[] = {
573e3ec7017SPing-Ke Shih 		[RTW89_CHANNEL_WIDTH_20] = HTC_OM_CHANNEL_WIDTH_20,
574e3ec7017SPing-Ke Shih 		[RTW89_CHANNEL_WIDTH_40] = HTC_OM_CHANNEL_WIDTH_40,
575e3ec7017SPing-Ke Shih 		[RTW89_CHANNEL_WIDTH_80] = HTC_OM_CHANNEL_WIDTH_80,
576e3ec7017SPing-Ke Shih 		[RTW89_CHANNEL_WIDTH_160] = HTC_OM_CHANNEL_WIDTH_160_OR_80_80,
577e3ec7017SPing-Ke Shih 		[RTW89_CHANNEL_WIDTH_80_80] = HTC_OM_CHANNEL_WIDTH_160_OR_80_80,
578e3ec7017SPing-Ke Shih 	};
579e3ec7017SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
580e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
581e3ec7017SPing-Ke Shih 	u8 om_bandwidth;
582e3ec7017SPing-Ke Shih 
583e3ec7017SPing-Ke Shih 	if (!chip->dis_2g_40m_ul_ofdma ||
584e3ec7017SPing-Ke Shih 	    hal->current_band_type != RTW89_BAND_2G ||
585e3ec7017SPing-Ke Shih 	    hal->current_band_width != RTW89_CHANNEL_WIDTH_40)
586e3ec7017SPing-Ke Shih 		return;
587e3ec7017SPing-Ke Shih 
588e3ec7017SPing-Ke Shih 	om_bandwidth = hal->current_band_width < ARRAY_SIZE(rtw89_bandwidth_to_om) ?
589e3ec7017SPing-Ke Shih 		       rtw89_bandwidth_to_om[hal->current_band_width] : 0;
590e3ec7017SPing-Ke Shih 	*htc = le32_encode_bits(RTW89_HTC_VARIANT_HE, RTW89_HTC_MASK_VARIANT) |
591e3ec7017SPing-Ke Shih 	       le32_encode_bits(RTW89_HTC_VARIANT_HE_CID_OM, RTW89_HTC_MASK_CTL_ID) |
592e3ec7017SPing-Ke Shih 	       le32_encode_bits(hal->rx_nss - 1, RTW89_HTC_MASK_HTC_OM_RX_NSS) |
593e3ec7017SPing-Ke Shih 	       le32_encode_bits(om_bandwidth, RTW89_HTC_MASK_HTC_OM_CH_WIDTH) |
594e3ec7017SPing-Ke Shih 	       le32_encode_bits(1, RTW89_HTC_MASK_HTC_OM_UL_MU_DIS) |
595e3ec7017SPing-Ke Shih 	       le32_encode_bits(hal->tx_nss - 1, RTW89_HTC_MASK_HTC_OM_TX_NSTS) |
596e3ec7017SPing-Ke Shih 	       le32_encode_bits(0, RTW89_HTC_MASK_HTC_OM_ER_SU_DIS) |
597e3ec7017SPing-Ke Shih 	       le32_encode_bits(0, RTW89_HTC_MASK_HTC_OM_DL_MU_MIMO_RR) |
598e3ec7017SPing-Ke Shih 	       le32_encode_bits(0, RTW89_HTC_MASK_HTC_OM_UL_MU_DATA_DIS);
599e3ec7017SPing-Ke Shih }
600e3ec7017SPing-Ke Shih 
601e3ec7017SPing-Ke Shih static bool
602e3ec7017SPing-Ke Shih __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev,
603e3ec7017SPing-Ke Shih 				 struct rtw89_core_tx_request *tx_req,
604e3ec7017SPing-Ke Shih 				 enum btc_pkt_type pkt_type)
605e3ec7017SPing-Ke Shih {
606e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = tx_req->sta;
607e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
608e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (void *)skb->data;
609e3ec7017SPing-Ke Shih 	__le16 fc = hdr->frame_control;
610e3ec7017SPing-Ke Shih 
611e3ec7017SPing-Ke Shih 	/* AP IOT issue with EAPoL, ARP and DHCP */
612e3ec7017SPing-Ke Shih 	if (pkt_type < PACKET_MAX)
613e3ec7017SPing-Ke Shih 		return false;
614e3ec7017SPing-Ke Shih 
615046d2e7cSSriram R 	if (!sta || !sta->deflink.he_cap.has_he)
616e3ec7017SPing-Ke Shih 		return false;
617e3ec7017SPing-Ke Shih 
618e3ec7017SPing-Ke Shih 	if (!ieee80211_is_data_qos(fc))
619e3ec7017SPing-Ke Shih 		return false;
620e3ec7017SPing-Ke Shih 
621e3ec7017SPing-Ke Shih 	if (skb_headroom(skb) < IEEE80211_HT_CTL_LEN)
622e3ec7017SPing-Ke Shih 		return false;
623e3ec7017SPing-Ke Shih 
624e3ec7017SPing-Ke Shih 	return true;
625e3ec7017SPing-Ke Shih }
626e3ec7017SPing-Ke Shih 
627e3ec7017SPing-Ke Shih static void
628e3ec7017SPing-Ke Shih __rtw89_core_tx_adjust_he_qos_htc(struct rtw89_dev *rtwdev,
629e3ec7017SPing-Ke Shih 				  struct rtw89_core_tx_request *tx_req)
630e3ec7017SPing-Ke Shih {
631e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = tx_req->sta;
632e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
633e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
634e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (void *)skb->data;
635e3ec7017SPing-Ke Shih 	__le16 fc = hdr->frame_control;
636e3ec7017SPing-Ke Shih 	void *data;
637e3ec7017SPing-Ke Shih 	__le32 *htc;
638e3ec7017SPing-Ke Shih 	u8 *qc;
639e3ec7017SPing-Ke Shih 	int hdr_len;
640e3ec7017SPing-Ke Shih 
641e3ec7017SPing-Ke Shih 	hdr_len = ieee80211_has_a4(fc) ? 32 : 26;
642e3ec7017SPing-Ke Shih 	data = skb_push(skb, IEEE80211_HT_CTL_LEN);
643e3ec7017SPing-Ke Shih 	memmove(data, data + IEEE80211_HT_CTL_LEN, hdr_len);
644e3ec7017SPing-Ke Shih 
645e3ec7017SPing-Ke Shih 	hdr = data;
646e3ec7017SPing-Ke Shih 	htc = data + hdr_len;
647e3ec7017SPing-Ke Shih 	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_ORDER);
648e3ec7017SPing-Ke Shih 	*htc = rtwsta->htc_template ? rtwsta->htc_template :
649e3ec7017SPing-Ke Shih 	       le32_encode_bits(RTW89_HTC_VARIANT_HE, RTW89_HTC_MASK_VARIANT) |
650e3ec7017SPing-Ke Shih 	       le32_encode_bits(RTW89_HTC_VARIANT_HE_CID_CAS, RTW89_HTC_MASK_CTL_ID);
651e3ec7017SPing-Ke Shih 
652e3ec7017SPing-Ke Shih 	qc = data + hdr_len - IEEE80211_QOS_CTL_LEN;
653e3ec7017SPing-Ke Shih 	qc[0] |= IEEE80211_QOS_CTL_EOSP;
654e3ec7017SPing-Ke Shih }
655e3ec7017SPing-Ke Shih 
656e3ec7017SPing-Ke Shih static void
657e3ec7017SPing-Ke Shih rtw89_core_tx_update_he_qos_htc(struct rtw89_dev *rtwdev,
658e3ec7017SPing-Ke Shih 				struct rtw89_core_tx_request *tx_req,
659e3ec7017SPing-Ke Shih 				enum btc_pkt_type pkt_type)
660e3ec7017SPing-Ke Shih {
661e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
662e3ec7017SPing-Ke Shih 	struct ieee80211_vif *vif = tx_req->vif;
663e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
664e3ec7017SPing-Ke Shih 
665e3ec7017SPing-Ke Shih 	if (!__rtw89_core_tx_check_he_qos_htc(rtwdev, tx_req, pkt_type))
666e3ec7017SPing-Ke Shih 		goto desc_bk;
667e3ec7017SPing-Ke Shih 
668e3ec7017SPing-Ke Shih 	__rtw89_core_tx_adjust_he_qos_htc(rtwdev, tx_req);
669e3ec7017SPing-Ke Shih 
670e3ec7017SPing-Ke Shih 	desc_info->pkt_size += IEEE80211_HT_CTL_LEN;
671e3ec7017SPing-Ke Shih 	desc_info->a_ctrl_bsr = true;
672e3ec7017SPing-Ke Shih 
673e3ec7017SPing-Ke Shih desc_bk:
674e3ec7017SPing-Ke Shih 	if (!rtwvif || rtwvif->last_a_ctrl == desc_info->a_ctrl_bsr)
675e3ec7017SPing-Ke Shih 		return;
676e3ec7017SPing-Ke Shih 
677e3ec7017SPing-Ke Shih 	rtwvif->last_a_ctrl = desc_info->a_ctrl_bsr;
678e3ec7017SPing-Ke Shih 	desc_info->bk = true;
679e3ec7017SPing-Ke Shih }
680e3ec7017SPing-Ke Shih 
6819eecaec2SPing-Ke Shih static u8 rtw89_core_tx_get_mac_id(struct rtw89_dev *rtwdev,
6829eecaec2SPing-Ke Shih 				   struct rtw89_core_tx_request *tx_req)
6839eecaec2SPing-Ke Shih {
6849eecaec2SPing-Ke Shih 	struct ieee80211_vif *vif = tx_req->vif;
6859eecaec2SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
6869eecaec2SPing-Ke Shih 	struct ieee80211_sta *sta = tx_req->sta;
6879eecaec2SPing-Ke Shih 	struct rtw89_sta *rtwsta;
6889eecaec2SPing-Ke Shih 
6899eecaec2SPing-Ke Shih 	if (!sta)
6909eecaec2SPing-Ke Shih 		return rtwvif->mac_id;
6919eecaec2SPing-Ke Shih 
6929eecaec2SPing-Ke Shih 	rtwsta = (struct rtw89_sta *)sta->drv_priv;
6939eecaec2SPing-Ke Shih 	return rtwsta->mac_id;
6949eecaec2SPing-Ke Shih }
6959eecaec2SPing-Ke Shih 
696e3ec7017SPing-Ke Shih static void
697e3ec7017SPing-Ke Shih rtw89_core_tx_update_data_info(struct rtw89_dev *rtwdev,
698e3ec7017SPing-Ke Shih 			       struct rtw89_core_tx_request *tx_req)
699e3ec7017SPing-Ke Shih {
700e3ec7017SPing-Ke Shih 	struct ieee80211_vif *vif = tx_req->vif;
701e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
702e3ec7017SPing-Ke Shih 	struct rtw89_phy_rate_pattern *rate_pattern = &rtwvif->rate_pattern;
703e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
704e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
705e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
706e3ec7017SPing-Ke Shih 	u8 tid, tid_indicate;
707e3ec7017SPing-Ke Shih 	u8 qsel, ch_dma;
708e3ec7017SPing-Ke Shih 
709e3ec7017SPing-Ke Shih 	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
710e3ec7017SPing-Ke Shih 	tid_indicate = rtw89_core_get_tid_indicate(rtwdev, tid);
71111d261f2SPing-Ke Shih 	qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : rtw89_core_get_qsel(rtwdev, tid);
712e3ec7017SPing-Ke Shih 	ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
713e3ec7017SPing-Ke Shih 
714e3ec7017SPing-Ke Shih 	desc_info->ch_dma = ch_dma;
715e3ec7017SPing-Ke Shih 	desc_info->tid_indicate = tid_indicate;
716e3ec7017SPing-Ke Shih 	desc_info->qsel = qsel;
7179eecaec2SPing-Ke Shih 	desc_info->mac_id = rtw89_core_tx_get_mac_id(rtwdev, tx_req);
7189eecaec2SPing-Ke Shih 	desc_info->port = desc_info->hiq ? rtwvif->port : 0;
719e3ec7017SPing-Ke Shih 
720e3ec7017SPing-Ke Shih 	/* enable wd_info for AMPDU */
721e3ec7017SPing-Ke Shih 	desc_info->en_wd_info = true;
722e3ec7017SPing-Ke Shih 
723e3ec7017SPing-Ke Shih 	if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)
724e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, tid);
725e3ec7017SPing-Ke Shih 	if (IEEE80211_SKB_CB(skb)->control.hw_key)
726e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_sec_key(rtwdev, tx_req);
727e3ec7017SPing-Ke Shih 
728e3ec7017SPing-Ke Shih 	if (rate_pattern->enable)
729e3ec7017SPing-Ke Shih 		desc_info->data_retry_lowest_rate = rate_pattern->rate;
730e3ec7017SPing-Ke Shih 	else if (hal->current_band_type == RTW89_BAND_2G)
731e3ec7017SPing-Ke Shih 		desc_info->data_retry_lowest_rate = RTW89_HW_RATE_CCK1;
732e3ec7017SPing-Ke Shih 	else
733e3ec7017SPing-Ke Shih 		desc_info->data_retry_lowest_rate = RTW89_HW_RATE_OFDM6;
734e3ec7017SPing-Ke Shih }
735e3ec7017SPing-Ke Shih 
736e3ec7017SPing-Ke Shih static enum btc_pkt_type
737e3ec7017SPing-Ke Shih rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,
738e3ec7017SPing-Ke Shih 				  struct rtw89_core_tx_request *tx_req)
739e3ec7017SPing-Ke Shih {
740e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
741e3ec7017SPing-Ke Shih 	struct udphdr *udphdr;
742e3ec7017SPing-Ke Shih 
743e3ec7017SPing-Ke Shih 	if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
744e3ec7017SPing-Ke Shih 		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work);
745e3ec7017SPing-Ke Shih 		return PACKET_EAPOL;
746e3ec7017SPing-Ke Shih 	}
747e3ec7017SPing-Ke Shih 
748e3ec7017SPing-Ke Shih 	if (skb->protocol == htons(ETH_P_ARP)) {
749e3ec7017SPing-Ke Shih 		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work);
750e3ec7017SPing-Ke Shih 		return PACKET_ARP;
751e3ec7017SPing-Ke Shih 	}
752e3ec7017SPing-Ke Shih 
753e3ec7017SPing-Ke Shih 	if (skb->protocol == htons(ETH_P_IP) &&
754e3ec7017SPing-Ke Shih 	    ip_hdr(skb)->protocol == IPPROTO_UDP) {
755e3ec7017SPing-Ke Shih 		udphdr = udp_hdr(skb);
756e3ec7017SPing-Ke Shih 		if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) ||
757e3ec7017SPing-Ke Shih 		     (udphdr->source == htons(68) && udphdr->dest == htons(67))) &&
758e3ec7017SPing-Ke Shih 		    skb->len > 282) {
759e3ec7017SPing-Ke Shih 			ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work);
760e3ec7017SPing-Ke Shih 			return PACKET_DHCP;
761e3ec7017SPing-Ke Shih 		}
762e3ec7017SPing-Ke Shih 	}
763e3ec7017SPing-Ke Shih 
764e3ec7017SPing-Ke Shih 	if (skb->protocol == htons(ETH_P_IP) &&
765e3ec7017SPing-Ke Shih 	    ip_hdr(skb)->protocol == IPPROTO_ICMP) {
766e3ec7017SPing-Ke Shih 		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.icmp_notify_work);
767e3ec7017SPing-Ke Shih 		return PACKET_ICMP;
768e3ec7017SPing-Ke Shih 	}
769e3ec7017SPing-Ke Shih 
770e3ec7017SPing-Ke Shih 	return PACKET_MAX;
771e3ec7017SPing-Ke Shih }
772e3ec7017SPing-Ke Shih 
77379a6c9a4SPing-Ke Shih static void rtw89_core_tx_update_llc_hdr(struct rtw89_dev *rtwdev,
77479a6c9a4SPing-Ke Shih 					 struct rtw89_tx_desc_info *desc_info,
77579a6c9a4SPing-Ke Shih 					 struct sk_buff *skb)
77679a6c9a4SPing-Ke Shih {
77779a6c9a4SPing-Ke Shih 	struct ieee80211_hdr *hdr = (void *)skb->data;
77879a6c9a4SPing-Ke Shih 	__le16 fc = hdr->frame_control;
77979a6c9a4SPing-Ke Shih 
78079a6c9a4SPing-Ke Shih 	desc_info->hdr_llc_len = ieee80211_hdrlen(fc);
78179a6c9a4SPing-Ke Shih 	desc_info->hdr_llc_len >>= 1; /* in unit of 2 bytes */
78279a6c9a4SPing-Ke Shih }
78379a6c9a4SPing-Ke Shih 
784e3ec7017SPing-Ke Shih static void
7857bfd05ffSChin-Yen Lee rtw89_core_tx_wake(struct rtw89_dev *rtwdev,
7867bfd05ffSChin-Yen Lee 		   struct rtw89_core_tx_request *tx_req)
7877bfd05ffSChin-Yen Lee {
78811fe4ccdSZong-Zhe Yang 	if (!RTW89_CHK_FW_FEATURE(TX_WAKE, &rtwdev->fw))
7897bfd05ffSChin-Yen Lee 		return;
7907bfd05ffSChin-Yen Lee 
7917bfd05ffSChin-Yen Lee 	if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
7927bfd05ffSChin-Yen Lee 		return;
7937bfd05ffSChin-Yen Lee 
7947bfd05ffSChin-Yen Lee 	if (tx_req->tx_type != RTW89_CORE_TX_TYPE_MGMT)
7957bfd05ffSChin-Yen Lee 		return;
7967bfd05ffSChin-Yen Lee 
7977bfd05ffSChin-Yen Lee 	rtw89_mac_notify_wake(rtwdev);
7987bfd05ffSChin-Yen Lee }
7997bfd05ffSChin-Yen Lee 
8007bfd05ffSChin-Yen Lee static void
801e3ec7017SPing-Ke Shih rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
802e3ec7017SPing-Ke Shih 			       struct rtw89_core_tx_request *tx_req)
803e3ec7017SPing-Ke Shih {
804e3ec7017SPing-Ke Shih 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
805e3ec7017SPing-Ke Shih 	struct sk_buff *skb = tx_req->skb;
80611d261f2SPing-Ke Shih 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
807e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (void *)skb->data;
808e3ec7017SPing-Ke Shih 	enum rtw89_core_tx_type tx_type;
809e3ec7017SPing-Ke Shih 	enum btc_pkt_type pkt_type;
810e3ec7017SPing-Ke Shih 	bool is_bmc;
811e3ec7017SPing-Ke Shih 	u16 seq;
812e3ec7017SPing-Ke Shih 
813e3ec7017SPing-Ke Shih 	seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
814e3ec7017SPing-Ke Shih 	if (tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD) {
815e3ec7017SPing-Ke Shih 		tx_type = rtw89_core_get_tx_type(rtwdev, skb);
816e3ec7017SPing-Ke Shih 		tx_req->tx_type = tx_type;
817e3ec7017SPing-Ke Shih 	}
818e3ec7017SPing-Ke Shih 	is_bmc = (is_broadcast_ether_addr(hdr->addr1) ||
819e3ec7017SPing-Ke Shih 		  is_multicast_ether_addr(hdr->addr1));
820e3ec7017SPing-Ke Shih 
821e3ec7017SPing-Ke Shih 	desc_info->seq = seq;
822e3ec7017SPing-Ke Shih 	desc_info->pkt_size = skb->len;
823e3ec7017SPing-Ke Shih 	desc_info->is_bmc = is_bmc;
824e3ec7017SPing-Ke Shih 	desc_info->wd_page = true;
82511d261f2SPing-Ke Shih 	desc_info->hiq = info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM;
826e3ec7017SPing-Ke Shih 
827e3ec7017SPing-Ke Shih 	switch (tx_req->tx_type) {
828e3ec7017SPing-Ke Shih 	case RTW89_CORE_TX_TYPE_MGMT:
829e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_mgmt_info(rtwdev, tx_req);
830e3ec7017SPing-Ke Shih 		break;
831e3ec7017SPing-Ke Shih 	case RTW89_CORE_TX_TYPE_DATA:
832e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_data_info(rtwdev, tx_req);
833e3ec7017SPing-Ke Shih 		pkt_type = rtw89_core_tx_btc_spec_pkt_notify(rtwdev, tx_req);
834e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_he_qos_htc(rtwdev, tx_req, pkt_type);
83579a6c9a4SPing-Ke Shih 		rtw89_core_tx_update_llc_hdr(rtwdev, desc_info, skb);
836e3ec7017SPing-Ke Shih 		break;
837e3ec7017SPing-Ke Shih 	case RTW89_CORE_TX_TYPE_FWCMD:
838e3ec7017SPing-Ke Shih 		rtw89_core_tx_update_h2c_info(rtwdev, tx_req);
839e3ec7017SPing-Ke Shih 		break;
840e3ec7017SPing-Ke Shih 	}
841e3ec7017SPing-Ke Shih }
842e3ec7017SPing-Ke Shih 
843e3ec7017SPing-Ke Shih void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel)
844e3ec7017SPing-Ke Shih {
845e3ec7017SPing-Ke Shih 	u8 ch_dma;
846e3ec7017SPing-Ke Shih 
847e3ec7017SPing-Ke Shih 	ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
848e3ec7017SPing-Ke Shih 
849e3ec7017SPing-Ke Shih 	rtw89_hci_tx_kick_off(rtwdev, ch_dma);
850e3ec7017SPing-Ke Shih }
851e3ec7017SPing-Ke Shih 
852e3ec7017SPing-Ke Shih int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
853e3ec7017SPing-Ke Shih 		 struct sk_buff *skb, bool fwdl)
854e3ec7017SPing-Ke Shih {
855e3ec7017SPing-Ke Shih 	struct rtw89_core_tx_request tx_req = {0};
856e3ec7017SPing-Ke Shih 	u32 cnt;
857e3ec7017SPing-Ke Shih 	int ret;
858e3ec7017SPing-Ke Shih 
859fc5f311fSPing-Ke Shih 	if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) {
860fc5f311fSPing-Ke Shih 		rtw89_debug(rtwdev, RTW89_DBG_FW,
861fc5f311fSPing-Ke Shih 			    "ignore h2c due to power is off with firmware state=%d\n",
862fc5f311fSPing-Ke Shih 			    test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags));
863fc5f311fSPing-Ke Shih 		return 0;
864fc5f311fSPing-Ke Shih 	}
865fc5f311fSPing-Ke Shih 
866e3ec7017SPing-Ke Shih 	tx_req.skb = skb;
867e3ec7017SPing-Ke Shih 	tx_req.tx_type = RTW89_CORE_TX_TYPE_FWCMD;
868e3ec7017SPing-Ke Shih 	if (fwdl)
869e3ec7017SPing-Ke Shih 		tx_req.desc_info.fw_dl = true;
870e3ec7017SPing-Ke Shih 
871e3ec7017SPing-Ke Shih 	rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
872e3ec7017SPing-Ke Shih 
873e3ec7017SPing-Ke Shih 	if (!fwdl)
874e3ec7017SPing-Ke Shih 		rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "H2C: ", skb->data, skb->len);
875e3ec7017SPing-Ke Shih 
876e3ec7017SPing-Ke Shih 	cnt = rtw89_hci_check_and_reclaim_tx_resource(rtwdev, RTW89_TXCH_CH12);
877e3ec7017SPing-Ke Shih 	if (cnt == 0) {
878e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "no tx fwcmd resource\n");
879e3ec7017SPing-Ke Shih 		return -ENOSPC;
880e3ec7017SPing-Ke Shih 	}
881e3ec7017SPing-Ke Shih 
882e3ec7017SPing-Ke Shih 	ret = rtw89_hci_tx_write(rtwdev, &tx_req);
883e3ec7017SPing-Ke Shih 	if (ret) {
884e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to transmit skb to HCI\n");
885e3ec7017SPing-Ke Shih 		return ret;
886e3ec7017SPing-Ke Shih 	}
887e3ec7017SPing-Ke Shih 	rtw89_hci_tx_kick_off(rtwdev, RTW89_TXCH_CH12);
888e3ec7017SPing-Ke Shih 
889e3ec7017SPing-Ke Shih 	return 0;
890e3ec7017SPing-Ke Shih }
891e3ec7017SPing-Ke Shih 
892e3ec7017SPing-Ke Shih int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
893e3ec7017SPing-Ke Shih 			struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel)
894e3ec7017SPing-Ke Shih {
895e3ec7017SPing-Ke Shih 	struct rtw89_core_tx_request tx_req = {0};
896e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
897e3ec7017SPing-Ke Shih 	int ret;
898e3ec7017SPing-Ke Shih 
899e3ec7017SPing-Ke Shih 	tx_req.skb = skb;
900e3ec7017SPing-Ke Shih 	tx_req.sta = sta;
901e3ec7017SPing-Ke Shih 	tx_req.vif = vif;
902e3ec7017SPing-Ke Shih 
903e3ec7017SPing-Ke Shih 	rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true);
904e3ec7017SPing-Ke Shih 	rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true);
905e3ec7017SPing-Ke Shih 	rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
9067bfd05ffSChin-Yen Lee 	rtw89_core_tx_wake(rtwdev, &tx_req);
9077bfd05ffSChin-Yen Lee 
908e3ec7017SPing-Ke Shih 	ret = rtw89_hci_tx_write(rtwdev, &tx_req);
909e3ec7017SPing-Ke Shih 	if (ret) {
910e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to transmit skb to HCI\n");
911e3ec7017SPing-Ke Shih 		return ret;
912e3ec7017SPing-Ke Shih 	}
913e3ec7017SPing-Ke Shih 
914e3ec7017SPing-Ke Shih 	if (qsel)
915e3ec7017SPing-Ke Shih 		*qsel = tx_req.desc_info.qsel;
916e3ec7017SPing-Ke Shih 
917e3ec7017SPing-Ke Shih 	return 0;
918e3ec7017SPing-Ke Shih }
919e3ec7017SPing-Ke Shih 
920e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_body0(struct rtw89_tx_desc_info *desc_info)
921e3ec7017SPing-Ke Shih {
922e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY0_WP_OFFSET, desc_info->wp_offset) |
923e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_WD_INFO_EN, desc_info->en_wd_info) |
924e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_CHANNEL_DMA, desc_info->ch_dma) |
925e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_HDR_LLC_LEN, desc_info->hdr_llc_len) |
926e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_WD_PAGE, desc_info->wd_page) |
92791644020SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_FW_DL, desc_info->fw_dl) |
92891644020SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_HW_SSN_SEL, desc_info->hw_ssn_sel) |
92991644020SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_HW_SSN_MODE, desc_info->hw_seq_mode);
930e3ec7017SPing-Ke Shih 
931e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
932e3ec7017SPing-Ke Shih }
933e3ec7017SPing-Ke Shih 
934f59acddeSPing-Ke Shih static __le32 rtw89_build_txwd_body0_v1(struct rtw89_tx_desc_info *desc_info)
935f59acddeSPing-Ke Shih {
936f59acddeSPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY0_WP_OFFSET_V1, desc_info->wp_offset) |
937f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_WD_INFO_EN, desc_info->en_wd_info) |
938f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_CHANNEL_DMA, desc_info->ch_dma) |
939f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_HDR_LLC_LEN, desc_info->hdr_llc_len) |
940f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_WD_PAGE, desc_info->wd_page) |
941f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY0_FW_DL, desc_info->fw_dl);
942f59acddeSPing-Ke Shih 
943f59acddeSPing-Ke Shih 	return cpu_to_le32(dword);
944f59acddeSPing-Ke Shih }
945f59acddeSPing-Ke Shih 
946f59acddeSPing-Ke Shih static __le32 rtw89_build_txwd_body1_v1(struct rtw89_tx_desc_info *desc_info)
947f59acddeSPing-Ke Shih {
948f59acddeSPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY1_ADDR_INFO_NUM, desc_info->addr_info_nr) |
94979a6c9a4SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY1_SEC_KEYID, desc_info->sec_keyid) |
950f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY1_SEC_TYPE, desc_info->sec_type);
951f59acddeSPing-Ke Shih 
952f59acddeSPing-Ke Shih 	return cpu_to_le32(dword);
953f59acddeSPing-Ke Shih }
954f59acddeSPing-Ke Shih 
955e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_body2(struct rtw89_tx_desc_info *desc_info)
956e3ec7017SPing-Ke Shih {
957e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY2_TID_INDICATE, desc_info->tid_indicate) |
958e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY2_QSEL, desc_info->qsel) |
9599eecaec2SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY2_TXPKT_SIZE, desc_info->pkt_size) |
9609eecaec2SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY2_MACID, desc_info->mac_id);
961e3ec7017SPing-Ke Shih 
962e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
963e3ec7017SPing-Ke Shih }
964e3ec7017SPing-Ke Shih 
965e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_body3(struct rtw89_tx_desc_info *desc_info)
966e3ec7017SPing-Ke Shih {
967e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY3_SW_SEQ, desc_info->seq) |
968e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY3_AGG_EN, desc_info->agg_en) |
969e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY3_BK, desc_info->bk);
970e3ec7017SPing-Ke Shih 
971e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
972e3ec7017SPing-Ke Shih }
973e3ec7017SPing-Ke Shih 
97479a6c9a4SPing-Ke Shih static __le32 rtw89_build_txwd_body4(struct rtw89_tx_desc_info *desc_info)
97579a6c9a4SPing-Ke Shih {
97679a6c9a4SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY4_SEC_IV_L0, desc_info->sec_seq[0]) |
97779a6c9a4SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY4_SEC_IV_L1, desc_info->sec_seq[1]);
97879a6c9a4SPing-Ke Shih 
97979a6c9a4SPing-Ke Shih 	return cpu_to_le32(dword);
98079a6c9a4SPing-Ke Shih }
98179a6c9a4SPing-Ke Shih 
98279a6c9a4SPing-Ke Shih static __le32 rtw89_build_txwd_body5(struct rtw89_tx_desc_info *desc_info)
98379a6c9a4SPing-Ke Shih {
98479a6c9a4SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H2, desc_info->sec_seq[2]) |
98579a6c9a4SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H3, desc_info->sec_seq[3]) |
98679a6c9a4SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H4, desc_info->sec_seq[4]) |
98779a6c9a4SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY5_SEC_IV_H5, desc_info->sec_seq[5]);
98879a6c9a4SPing-Ke Shih 
98979a6c9a4SPing-Ke Shih 	return cpu_to_le32(dword);
99079a6c9a4SPing-Ke Shih }
99179a6c9a4SPing-Ke Shih 
992f59acddeSPing-Ke Shih static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info)
993f59acddeSPing-Ke Shih {
994f59acddeSPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_BODY7_USE_RATE_V1, desc_info->use_rate) |
995f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_BODY7_DATA_RATE, desc_info->data_rate);
996f59acddeSPing-Ke Shih 
997f59acddeSPing-Ke Shih 	return cpu_to_le32(dword);
998f59acddeSPing-Ke Shih }
999f59acddeSPing-Ke Shih 
1000e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_info0(struct rtw89_tx_desc_info *desc_info)
1001e3ec7017SPing-Ke Shih {
1002e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO0_USE_RATE, desc_info->use_rate) |
1003e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO0_DATA_RATE, desc_info->data_rate) |
10049eecaec2SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO0_DISDATAFB, desc_info->dis_data_fb) |
10059eecaec2SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO0_MULTIPORT_ID, desc_info->port);
1006e3ec7017SPing-Ke Shih 
1007e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
1008e3ec7017SPing-Ke Shih }
1009e3ec7017SPing-Ke Shih 
1010f59acddeSPing-Ke Shih static __le32 rtw89_build_txwd_info0_v1(struct rtw89_tx_desc_info *desc_info)
1011f59acddeSPing-Ke Shih {
1012f59acddeSPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO0_DISDATAFB, desc_info->dis_data_fb);
1013f59acddeSPing-Ke Shih 
1014f59acddeSPing-Ke Shih 	return cpu_to_le32(dword);
1015f59acddeSPing-Ke Shih }
1016f59acddeSPing-Ke Shih 
1017e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_info1(struct rtw89_tx_desc_info *desc_info)
1018e3ec7017SPing-Ke Shih {
1019e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO1_MAX_AGGNUM, desc_info->ampdu_num) |
1020e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) |
1021e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE,
1022e3ec7017SPing-Ke Shih 			       desc_info->data_retry_lowest_rate);
1023e3ec7017SPing-Ke Shih 
1024e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
1025e3ec7017SPing-Ke Shih }
1026e3ec7017SPing-Ke Shih 
1027e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_info2(struct rtw89_tx_desc_info *desc_info)
1028e3ec7017SPing-Ke Shih {
1029e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO2_AMPDU_DENSITY, desc_info->ampdu_density) |
1030e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO2_SEC_TYPE, desc_info->sec_type) |
1031e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO2_SEC_HW_ENC, desc_info->sec_en) |
1032e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx);
1033e3ec7017SPing-Ke Shih 
1034e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
1035e3ec7017SPing-Ke Shih }
1036e3ec7017SPing-Ke Shih 
1037f59acddeSPing-Ke Shih static __le32 rtw89_build_txwd_info2_v1(struct rtw89_tx_desc_info *desc_info)
1038f59acddeSPing-Ke Shih {
1039f59acddeSPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO2_AMPDU_DENSITY, desc_info->ampdu_density) |
1040f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO2_FORCE_KEY_EN, desc_info->sec_en) |
1041f59acddeSPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx);
1042f59acddeSPing-Ke Shih 
1043f59acddeSPing-Ke Shih 	return cpu_to_le32(dword);
1044f59acddeSPing-Ke Shih }
1045f59acddeSPing-Ke Shih 
1046e3ec7017SPing-Ke Shih static __le32 rtw89_build_txwd_info4(struct rtw89_tx_desc_info *desc_info)
1047e3ec7017SPing-Ke Shih {
1048e3ec7017SPing-Ke Shih 	u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, 1) |
1049e3ec7017SPing-Ke Shih 		    FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1);
1050e3ec7017SPing-Ke Shih 
1051e3ec7017SPing-Ke Shih 	return cpu_to_le32(dword);
1052e3ec7017SPing-Ke Shih }
1053e3ec7017SPing-Ke Shih 
1054e3ec7017SPing-Ke Shih void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev,
1055e3ec7017SPing-Ke Shih 			    struct rtw89_tx_desc_info *desc_info,
1056e3ec7017SPing-Ke Shih 			    void *txdesc)
1057e3ec7017SPing-Ke Shih {
1058e3ec7017SPing-Ke Shih 	struct rtw89_txwd_body *txwd_body = (struct rtw89_txwd_body *)txdesc;
1059e3ec7017SPing-Ke Shih 	struct rtw89_txwd_info *txwd_info;
1060e3ec7017SPing-Ke Shih 
1061e3ec7017SPing-Ke Shih 	txwd_body->dword0 = rtw89_build_txwd_body0(desc_info);
1062e3ec7017SPing-Ke Shih 	txwd_body->dword2 = rtw89_build_txwd_body2(desc_info);
1063e3ec7017SPing-Ke Shih 	txwd_body->dword3 = rtw89_build_txwd_body3(desc_info);
1064e3ec7017SPing-Ke Shih 
1065e3ec7017SPing-Ke Shih 	if (!desc_info->en_wd_info)
1066e3ec7017SPing-Ke Shih 		return;
1067e3ec7017SPing-Ke Shih 
1068e3ec7017SPing-Ke Shih 	txwd_info = (struct rtw89_txwd_info *)(txwd_body + 1);
1069e3ec7017SPing-Ke Shih 	txwd_info->dword0 = rtw89_build_txwd_info0(desc_info);
1070e3ec7017SPing-Ke Shih 	txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
1071e3ec7017SPing-Ke Shih 	txwd_info->dword2 = rtw89_build_txwd_info2(desc_info);
1072e3ec7017SPing-Ke Shih 	txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
1073e3ec7017SPing-Ke Shih 
1074e3ec7017SPing-Ke Shih }
1075e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_fill_txdesc);
1076e3ec7017SPing-Ke Shih 
1077f59acddeSPing-Ke Shih void rtw89_core_fill_txdesc_v1(struct rtw89_dev *rtwdev,
1078f59acddeSPing-Ke Shih 			       struct rtw89_tx_desc_info *desc_info,
1079f59acddeSPing-Ke Shih 			       void *txdesc)
1080f59acddeSPing-Ke Shih {
1081f59acddeSPing-Ke Shih 	struct rtw89_txwd_body_v1 *txwd_body = (struct rtw89_txwd_body_v1 *)txdesc;
1082f59acddeSPing-Ke Shih 	struct rtw89_txwd_info *txwd_info;
1083f59acddeSPing-Ke Shih 
1084f59acddeSPing-Ke Shih 	txwd_body->dword0 = rtw89_build_txwd_body0_v1(desc_info);
1085f59acddeSPing-Ke Shih 	txwd_body->dword1 = rtw89_build_txwd_body1_v1(desc_info);
1086f59acddeSPing-Ke Shih 	txwd_body->dword2 = rtw89_build_txwd_body2(desc_info);
1087f59acddeSPing-Ke Shih 	txwd_body->dword3 = rtw89_build_txwd_body3(desc_info);
108879a6c9a4SPing-Ke Shih 	if (desc_info->sec_en) {
108979a6c9a4SPing-Ke Shih 		txwd_body->dword4 = rtw89_build_txwd_body4(desc_info);
109079a6c9a4SPing-Ke Shih 		txwd_body->dword5 = rtw89_build_txwd_body5(desc_info);
109179a6c9a4SPing-Ke Shih 	}
1092f59acddeSPing-Ke Shih 	txwd_body->dword7 = rtw89_build_txwd_body7_v1(desc_info);
1093f59acddeSPing-Ke Shih 
1094f59acddeSPing-Ke Shih 	if (!desc_info->en_wd_info)
1095f59acddeSPing-Ke Shih 		return;
1096f59acddeSPing-Ke Shih 
1097f59acddeSPing-Ke Shih 	txwd_info = (struct rtw89_txwd_info *)(txwd_body + 1);
1098f59acddeSPing-Ke Shih 	txwd_info->dword0 = rtw89_build_txwd_info0_v1(desc_info);
1099f59acddeSPing-Ke Shih 	txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
1100f59acddeSPing-Ke Shih 	txwd_info->dword2 = rtw89_build_txwd_info2_v1(desc_info);
1101f59acddeSPing-Ke Shih 	txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
1102f59acddeSPing-Ke Shih }
1103f59acddeSPing-Ke Shih EXPORT_SYMBOL(rtw89_core_fill_txdesc_v1);
1104f59acddeSPing-Ke Shih 
1105a95bd62eSPing-Ke Shih static __le32 rtw89_build_txwd_fwcmd0_v1(struct rtw89_tx_desc_info *desc_info)
1106a95bd62eSPing-Ke Shih {
1107a95bd62eSPing-Ke Shih 	u32 dword = FIELD_PREP(AX_RXD_RPKT_LEN_MASK, desc_info->pkt_size) |
1108a95bd62eSPing-Ke Shih 		    FIELD_PREP(AX_RXD_RPKT_TYPE_MASK, desc_info->fw_dl ?
1109a95bd62eSPing-Ke Shih 						      RTW89_CORE_RX_TYPE_FWDL :
1110a95bd62eSPing-Ke Shih 						      RTW89_CORE_RX_TYPE_H2C);
1111a95bd62eSPing-Ke Shih 
1112a95bd62eSPing-Ke Shih 	return cpu_to_le32(dword);
1113a95bd62eSPing-Ke Shih }
1114a95bd62eSPing-Ke Shih 
1115a95bd62eSPing-Ke Shih void rtw89_core_fill_txdesc_fwcmd_v1(struct rtw89_dev *rtwdev,
1116a95bd62eSPing-Ke Shih 				     struct rtw89_tx_desc_info *desc_info,
1117a95bd62eSPing-Ke Shih 				     void *txdesc)
1118a95bd62eSPing-Ke Shih {
1119a95bd62eSPing-Ke Shih 	struct rtw89_rxdesc_short *txwd_v1 = (struct rtw89_rxdesc_short *)txdesc;
1120a95bd62eSPing-Ke Shih 
1121a95bd62eSPing-Ke Shih 	txwd_v1->dword0 = rtw89_build_txwd_fwcmd0_v1(desc_info);
1122a95bd62eSPing-Ke Shih }
1123a95bd62eSPing-Ke Shih EXPORT_SYMBOL(rtw89_core_fill_txdesc_fwcmd_v1);
1124a95bd62eSPing-Ke Shih 
1125e3ec7017SPing-Ke Shih static int rtw89_core_rx_process_mac_ppdu(struct rtw89_dev *rtwdev,
1126e3ec7017SPing-Ke Shih 					  struct sk_buff *skb,
1127e3ec7017SPing-Ke Shih 					  struct rtw89_rx_phy_ppdu *phy_ppdu)
1128e3ec7017SPing-Ke Shih {
1129e3ec7017SPing-Ke Shih 	bool rx_cnt_valid = false;
1130e3ec7017SPing-Ke Shih 	u8 plcp_size = 0;
1131e3ec7017SPing-Ke Shih 	u8 usr_num = 0;
1132e3ec7017SPing-Ke Shih 	u8 *phy_sts;
1133e3ec7017SPing-Ke Shih 
1134e3ec7017SPing-Ke Shih 	rx_cnt_valid = RTW89_GET_RXINFO_RX_CNT_VLD(skb->data);
1135e3ec7017SPing-Ke Shih 	plcp_size = RTW89_GET_RXINFO_PLCP_LEN(skb->data) << 3;
1136e3ec7017SPing-Ke Shih 	usr_num = RTW89_GET_RXINFO_USR_NUM(skb->data);
1137e3ec7017SPing-Ke Shih 	if (usr_num > RTW89_PPDU_MAX_USR) {
1138e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "Invalid user number in mac info\n");
1139e3ec7017SPing-Ke Shih 		return -EINVAL;
1140e3ec7017SPing-Ke Shih 	}
1141e3ec7017SPing-Ke Shih 
1142e3ec7017SPing-Ke Shih 	phy_sts = skb->data + RTW89_PPDU_MAC_INFO_SIZE;
1143e3ec7017SPing-Ke Shih 	phy_sts += usr_num * RTW89_PPDU_MAC_INFO_USR_SIZE;
1144e3ec7017SPing-Ke Shih 	/* 8-byte alignment */
1145e3ec7017SPing-Ke Shih 	if (usr_num & BIT(0))
1146e3ec7017SPing-Ke Shih 		phy_sts += RTW89_PPDU_MAC_INFO_USR_SIZE;
1147e3ec7017SPing-Ke Shih 	if (rx_cnt_valid)
1148e3ec7017SPing-Ke Shih 		phy_sts += RTW89_PPDU_MAC_RX_CNT_SIZE;
1149e3ec7017SPing-Ke Shih 	phy_sts += plcp_size;
1150e3ec7017SPing-Ke Shih 
1151e3ec7017SPing-Ke Shih 	phy_ppdu->buf = phy_sts;
1152e3ec7017SPing-Ke Shih 	phy_ppdu->len = skb->data + skb->len - phy_sts;
1153e3ec7017SPing-Ke Shih 
1154e3ec7017SPing-Ke Shih 	return 0;
1155e3ec7017SPing-Ke Shih }
1156e3ec7017SPing-Ke Shih 
1157e3ec7017SPing-Ke Shih static void rtw89_core_rx_process_phy_ppdu_iter(void *data,
1158e3ec7017SPing-Ke Shih 						struct ieee80211_sta *sta)
1159e3ec7017SPing-Ke Shih {
1160e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
1161e3ec7017SPing-Ke Shih 	struct rtw89_rx_phy_ppdu *phy_ppdu = (struct rtw89_rx_phy_ppdu *)data;
1162e3ec7017SPing-Ke Shih 
1163e3ec7017SPing-Ke Shih 	if (rtwsta->mac_id == phy_ppdu->mac_id && phy_ppdu->to_self)
1164e3ec7017SPing-Ke Shih 		ewma_rssi_add(&rtwsta->avg_rssi, phy_ppdu->rssi_avg);
1165e3ec7017SPing-Ke Shih }
1166e3ec7017SPing-Ke Shih 
1167e3ec7017SPing-Ke Shih #define VAR_LEN 0xff
1168e3ec7017SPing-Ke Shih #define VAR_LEN_UNIT 8
1169e3ec7017SPing-Ke Shih static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, u8 *addr)
1170e3ec7017SPing-Ke Shih {
1171e3ec7017SPing-Ke Shih 	static const u8 physts_ie_len_tab[32] = {
1172e3ec7017SPing-Ke Shih 		16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
1173e3ec7017SPing-Ke Shih 		VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN,
1174e3ec7017SPing-Ke Shih 		VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32
1175e3ec7017SPing-Ke Shih 	};
1176e3ec7017SPing-Ke Shih 	u16 ie_len;
1177e3ec7017SPing-Ke Shih 	u8 ie;
1178e3ec7017SPing-Ke Shih 
1179e3ec7017SPing-Ke Shih 	ie = RTW89_GET_PHY_STS_IE_TYPE(addr);
1180e3ec7017SPing-Ke Shih 	if (physts_ie_len_tab[ie] != VAR_LEN)
1181e3ec7017SPing-Ke Shih 		ie_len = physts_ie_len_tab[ie];
1182e3ec7017SPing-Ke Shih 	else
1183e3ec7017SPing-Ke Shih 		ie_len = RTW89_GET_PHY_STS_IE_LEN(addr) * VAR_LEN_UNIT;
1184e3ec7017SPing-Ke Shih 
1185e3ec7017SPing-Ke Shih 	return ie_len;
1186e3ec7017SPing-Ke Shih }
1187e3ec7017SPing-Ke Shih 
1188e3ec7017SPing-Ke Shih static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr,
1189e3ec7017SPing-Ke Shih 					     struct rtw89_rx_phy_ppdu *phy_ppdu)
1190e3ec7017SPing-Ke Shih {
1191e3ec7017SPing-Ke Shih 	s16 cfo;
1192e3ec7017SPing-Ke Shih 
1193eb4e52b3SPo Hao Huang 	phy_ppdu->chan_idx = RTW89_GET_PHY_STS_IE01_CH_IDX(addr);
1194eb4e52b3SPo Hao Huang 	if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6)
1195eb4e52b3SPo Hao Huang 		return;
1196e3ec7017SPing-Ke Shih 	/* sign conversion for S(12,2) */
1197eb4e52b3SPo Hao Huang 	cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_CFO(addr), 11);
1198e3ec7017SPing-Ke Shih 	rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu);
1199e3ec7017SPing-Ke Shih }
1200e3ec7017SPing-Ke Shih 
1201e3ec7017SPing-Ke Shih static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, u8 *addr,
1202e3ec7017SPing-Ke Shih 					    struct rtw89_rx_phy_ppdu *phy_ppdu)
1203e3ec7017SPing-Ke Shih {
1204e3ec7017SPing-Ke Shih 	u8 ie;
1205e3ec7017SPing-Ke Shih 
1206e3ec7017SPing-Ke Shih 	ie = RTW89_GET_PHY_STS_IE_TYPE(addr);
1207e3ec7017SPing-Ke Shih 	switch (ie) {
1208e3ec7017SPing-Ke Shih 	case RTW89_PHYSTS_IE01_CMN_OFDM:
1209e3ec7017SPing-Ke Shih 		rtw89_core_parse_phy_status_ie01(rtwdev, addr, phy_ppdu);
1210e3ec7017SPing-Ke Shih 		break;
1211e3ec7017SPing-Ke Shih 	default:
1212e3ec7017SPing-Ke Shih 		break;
1213e3ec7017SPing-Ke Shih 	}
1214e3ec7017SPing-Ke Shih 
1215e3ec7017SPing-Ke Shih 	return 0;
1216e3ec7017SPing-Ke Shih }
1217e3ec7017SPing-Ke Shih 
1218e3ec7017SPing-Ke Shih static void rtw89_core_update_phy_ppdu(struct rtw89_rx_phy_ppdu *phy_ppdu)
1219e3ec7017SPing-Ke Shih {
1220e3ec7017SPing-Ke Shih 	s8 *rssi = phy_ppdu->rssi;
1221e3ec7017SPing-Ke Shih 	u8 *buf = phy_ppdu->buf;
1222e3ec7017SPing-Ke Shih 
1223eb4e52b3SPo Hao Huang 	phy_ppdu->ie = RTW89_GET_PHY_STS_IE_MAP(buf);
1224e3ec7017SPing-Ke Shih 	phy_ppdu->rssi_avg = RTW89_GET_PHY_STS_RSSI_AVG(buf);
1225e3ec7017SPing-Ke Shih 	rssi[RF_PATH_A] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_A(buf));
1226e3ec7017SPing-Ke Shih 	rssi[RF_PATH_B] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_B(buf));
1227e3ec7017SPing-Ke Shih 	rssi[RF_PATH_C] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_C(buf));
1228e3ec7017SPing-Ke Shih 	rssi[RF_PATH_D] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_D(buf));
1229e3ec7017SPing-Ke Shih }
1230e3ec7017SPing-Ke Shih 
1231e3ec7017SPing-Ke Shih static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev,
1232e3ec7017SPing-Ke Shih 					  struct rtw89_rx_phy_ppdu *phy_ppdu)
1233e3ec7017SPing-Ke Shih {
1234e3ec7017SPing-Ke Shih 	if (RTW89_GET_PHY_STS_LEN(phy_ppdu->buf) << 3 != phy_ppdu->len) {
1235e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "phy ppdu len mismatch\n");
1236e3ec7017SPing-Ke Shih 		return -EINVAL;
1237e3ec7017SPing-Ke Shih 	}
1238e3ec7017SPing-Ke Shih 	rtw89_core_update_phy_ppdu(phy_ppdu);
1239e3ec7017SPing-Ke Shih 	ieee80211_iterate_stations_atomic(rtwdev->hw,
1240e3ec7017SPing-Ke Shih 					  rtw89_core_rx_process_phy_ppdu_iter,
1241e3ec7017SPing-Ke Shih 					  phy_ppdu);
1242e3ec7017SPing-Ke Shih 
1243e3ec7017SPing-Ke Shih 	return 0;
1244e3ec7017SPing-Ke Shih }
1245e3ec7017SPing-Ke Shih 
1246e3ec7017SPing-Ke Shih static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
1247e3ec7017SPing-Ke Shih 				       struct rtw89_rx_phy_ppdu *phy_ppdu)
1248e3ec7017SPing-Ke Shih {
1249e3ec7017SPing-Ke Shih 	u16 ie_len;
1250e3ec7017SPing-Ke Shih 	u8 *pos, *end;
1251e3ec7017SPing-Ke Shih 
1252eb4e52b3SPo Hao Huang 	/* mark invalid reports and bypass them */
1253eb4e52b3SPo Hao Huang 	if (phy_ppdu->ie < RTW89_CCK_PKT)
1254eb4e52b3SPo Hao Huang 		return -EINVAL;
1255e3ec7017SPing-Ke Shih 
1256e3ec7017SPing-Ke Shih 	pos = (u8 *)phy_ppdu->buf + PHY_STS_HDR_LEN;
1257e3ec7017SPing-Ke Shih 	end = (u8 *)phy_ppdu->buf + phy_ppdu->len;
1258e3ec7017SPing-Ke Shih 	while (pos < end) {
1259e3ec7017SPing-Ke Shih 		ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, pos);
1260e3ec7017SPing-Ke Shih 		rtw89_core_process_phy_status_ie(rtwdev, pos, phy_ppdu);
1261e3ec7017SPing-Ke Shih 		pos += ie_len;
1262e3ec7017SPing-Ke Shih 		if (pos > end || ie_len == 0) {
1263e3ec7017SPing-Ke Shih 			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
1264e3ec7017SPing-Ke Shih 				    "phy status parse failed\n");
1265e3ec7017SPing-Ke Shih 			return -EINVAL;
1266e3ec7017SPing-Ke Shih 		}
1267e3ec7017SPing-Ke Shih 	}
1268e3ec7017SPing-Ke Shih 
1269e3ec7017SPing-Ke Shih 	return 0;
1270e3ec7017SPing-Ke Shih }
1271e3ec7017SPing-Ke Shih 
1272e3ec7017SPing-Ke Shih static void rtw89_core_rx_process_phy_sts(struct rtw89_dev *rtwdev,
1273e3ec7017SPing-Ke Shih 					  struct rtw89_rx_phy_ppdu *phy_ppdu)
1274e3ec7017SPing-Ke Shih {
1275e3ec7017SPing-Ke Shih 	int ret;
1276e3ec7017SPing-Ke Shih 
1277e3ec7017SPing-Ke Shih 	ret = rtw89_core_rx_parse_phy_sts(rtwdev, phy_ppdu);
1278e3ec7017SPing-Ke Shih 	if (ret)
1279e3ec7017SPing-Ke Shih 		rtw89_debug(rtwdev, RTW89_DBG_TXRX, "parse phy sts failed\n");
1280e3ec7017SPing-Ke Shih 	else
1281e3ec7017SPing-Ke Shih 		phy_ppdu->valid = true;
1282e3ec7017SPing-Ke Shih }
1283e3ec7017SPing-Ke Shih 
1284e3ec7017SPing-Ke Shih static u8 rtw89_rxdesc_to_nl_he_gi(struct rtw89_dev *rtwdev,
1285e3ec7017SPing-Ke Shih 				   const struct rtw89_rx_desc_info *desc_info,
1286e3ec7017SPing-Ke Shih 				   bool rx_status)
1287e3ec7017SPing-Ke Shih {
1288e3ec7017SPing-Ke Shih 	switch (desc_info->gi_ltf) {
1289e3ec7017SPing-Ke Shih 	case RTW89_GILTF_SGI_4XHE08:
1290e3ec7017SPing-Ke Shih 	case RTW89_GILTF_2XHE08:
1291e3ec7017SPing-Ke Shih 	case RTW89_GILTF_1XHE08:
1292e3ec7017SPing-Ke Shih 		return NL80211_RATE_INFO_HE_GI_0_8;
1293e3ec7017SPing-Ke Shih 	case RTW89_GILTF_2XHE16:
1294e3ec7017SPing-Ke Shih 	case RTW89_GILTF_1XHE16:
1295e3ec7017SPing-Ke Shih 		return NL80211_RATE_INFO_HE_GI_1_6;
1296e3ec7017SPing-Ke Shih 	case RTW89_GILTF_LGI_4XHE32:
1297e3ec7017SPing-Ke Shih 		return NL80211_RATE_INFO_HE_GI_3_2;
1298e3ec7017SPing-Ke Shih 	default:
1299e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "invalid gi_ltf=%d", desc_info->gi_ltf);
1300e3ec7017SPing-Ke Shih 		return rx_status ? NL80211_RATE_INFO_HE_GI_3_2 : U8_MAX;
1301e3ec7017SPing-Ke Shih 	}
1302e3ec7017SPing-Ke Shih }
1303e3ec7017SPing-Ke Shih 
1304e3ec7017SPing-Ke Shih static bool rtw89_core_rx_ppdu_match(struct rtw89_dev *rtwdev,
1305e3ec7017SPing-Ke Shih 				     struct rtw89_rx_desc_info *desc_info,
1306e3ec7017SPing-Ke Shih 				     struct ieee80211_rx_status *status)
1307e3ec7017SPing-Ke Shih {
1308e3ec7017SPing-Ke Shih 	u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
1309e3ec7017SPing-Ke Shih 	u8 data_rate_mode, bw, rate_idx = MASKBYTE0, gi_ltf;
1310e3ec7017SPing-Ke Shih 	u16 data_rate;
1311e3ec7017SPing-Ke Shih 	bool ret;
1312e3ec7017SPing-Ke Shih 
1313e3ec7017SPing-Ke Shih 	data_rate = desc_info->data_rate;
1314e3ec7017SPing-Ke Shih 	data_rate_mode = GET_DATA_RATE_MODE(data_rate);
1315e3ec7017SPing-Ke Shih 	if (data_rate_mode == DATA_RATE_MODE_NON_HT) {
1316e3ec7017SPing-Ke Shih 		rate_idx = GET_DATA_RATE_NOT_HT_IDX(data_rate);
1317eb4e52b3SPo Hao Huang 		/* rate_idx is still hardware value here */
1318e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_HT) {
1319e3ec7017SPing-Ke Shih 		rate_idx = GET_DATA_RATE_HT_IDX(data_rate);
1320e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_VHT) {
1321e3ec7017SPing-Ke Shih 		rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
1322e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_HE) {
1323e3ec7017SPing-Ke Shih 		rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
1324e3ec7017SPing-Ke Shih 	} else {
1325e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "invalid RX rate mode %d\n", data_rate_mode);
1326e3ec7017SPing-Ke Shih 	}
1327e3ec7017SPing-Ke Shih 
1328167044afSPing-Ke Shih 	bw = rtw89_hw_to_rate_info_bw(desc_info->bw);
1329e3ec7017SPing-Ke Shih 	gi_ltf = rtw89_rxdesc_to_nl_he_gi(rtwdev, desc_info, false);
1330e3ec7017SPing-Ke Shih 	ret = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band] == desc_info->ppdu_cnt &&
1331e3ec7017SPing-Ke Shih 	      status->rate_idx == rate_idx &&
1332e3ec7017SPing-Ke Shih 	      status->he_gi == gi_ltf &&
1333e3ec7017SPing-Ke Shih 	      status->bw == bw;
1334e3ec7017SPing-Ke Shih 
1335e3ec7017SPing-Ke Shih 	return ret;
1336e3ec7017SPing-Ke Shih }
1337e3ec7017SPing-Ke Shih 
1338e3ec7017SPing-Ke Shih struct rtw89_vif_rx_stats_iter_data {
1339e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev;
1340e3ec7017SPing-Ke Shih 	struct rtw89_rx_phy_ppdu *phy_ppdu;
1341e3ec7017SPing-Ke Shih 	struct rtw89_rx_desc_info *desc_info;
1342e3ec7017SPing-Ke Shih 	struct sk_buff *skb;
1343e3ec7017SPing-Ke Shih 	const u8 *bssid;
1344e3ec7017SPing-Ke Shih };
1345e3ec7017SPing-Ke Shih 
1346e3ec7017SPing-Ke Shih static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
1347e3ec7017SPing-Ke Shih 				    struct ieee80211_vif *vif)
1348e3ec7017SPing-Ke Shih {
1349e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
1350e3ec7017SPing-Ke Shih 	struct rtw89_vif_rx_stats_iter_data *iter_data = data;
1351e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = iter_data->rtwdev;
1352e3ec7017SPing-Ke Shih 	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
1353e3ec7017SPing-Ke Shih 	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
1354e3ec7017SPing-Ke Shih 	struct sk_buff *skb = iter_data->skb;
1355e3ec7017SPing-Ke Shih 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1356e3ec7017SPing-Ke Shih 	const u8 *bssid = iter_data->bssid;
1357e3ec7017SPing-Ke Shih 
1358e3ec7017SPing-Ke Shih 	if (!ether_addr_equal(vif->bss_conf.bssid, bssid))
1359e3ec7017SPing-Ke Shih 		return;
1360e3ec7017SPing-Ke Shih 
1361e3ec7017SPing-Ke Shih 	if (ieee80211_is_beacon(hdr->frame_control))
1362e3ec7017SPing-Ke Shih 		pkt_stat->beacon_nr++;
1363e3ec7017SPing-Ke Shih 
1364e3ec7017SPing-Ke Shih 	if (!ether_addr_equal(vif->addr, hdr->addr1))
1365e3ec7017SPing-Ke Shih 		return;
1366e3ec7017SPing-Ke Shih 
1367e3ec7017SPing-Ke Shih 	if (desc_info->data_rate < RTW89_HW_RATE_NR)
1368e3ec7017SPing-Ke Shih 		pkt_stat->rx_rate_cnt[desc_info->data_rate]++;
1369e3ec7017SPing-Ke Shih 
1370e3ec7017SPing-Ke Shih 	rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, false);
1371e3ec7017SPing-Ke Shih }
1372e3ec7017SPing-Ke Shih 
1373e3ec7017SPing-Ke Shih static void rtw89_core_rx_stats(struct rtw89_dev *rtwdev,
1374e3ec7017SPing-Ke Shih 				struct rtw89_rx_phy_ppdu *phy_ppdu,
1375e3ec7017SPing-Ke Shih 				struct rtw89_rx_desc_info *desc_info,
1376e3ec7017SPing-Ke Shih 				struct sk_buff *skb)
1377e3ec7017SPing-Ke Shih {
1378e3ec7017SPing-Ke Shih 	struct rtw89_vif_rx_stats_iter_data iter_data;
1379e3ec7017SPing-Ke Shih 
1380e3ec7017SPing-Ke Shih 	rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, false);
1381e3ec7017SPing-Ke Shih 
1382e3ec7017SPing-Ke Shih 	iter_data.rtwdev = rtwdev;
1383e3ec7017SPing-Ke Shih 	iter_data.phy_ppdu = phy_ppdu;
1384e3ec7017SPing-Ke Shih 	iter_data.desc_info = desc_info;
1385e3ec7017SPing-Ke Shih 	iter_data.skb = skb;
1386e3ec7017SPing-Ke Shih 	iter_data.bssid = get_hdr_bssid((struct ieee80211_hdr *)skb->data);
1387e3ec7017SPing-Ke Shih 	rtw89_iterate_vifs_bh(rtwdev, rtw89_vif_rx_stats_iter, &iter_data);
1388e3ec7017SPing-Ke Shih }
1389e3ec7017SPing-Ke Shih 
1390eb4e52b3SPo Hao Huang static void rtw89_correct_cck_chan(struct rtw89_dev *rtwdev,
1391eb4e52b3SPo Hao Huang 				   struct ieee80211_rx_status *status)
1392eb4e52b3SPo Hao Huang {
1393eb4e52b3SPo Hao Huang 	u16 chan = rtwdev->hal.prev_primary_channel;
1394eb4e52b3SPo Hao Huang 	u8 band = chan <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
1395eb4e52b3SPo Hao Huang 
1396eb4e52b3SPo Hao Huang 	if (status->band != NL80211_BAND_2GHZ &&
1397eb4e52b3SPo Hao Huang 	    status->encoding == RX_ENC_LEGACY &&
1398eb4e52b3SPo Hao Huang 	    status->rate_idx < RTW89_HW_RATE_OFDM6) {
1399eb4e52b3SPo Hao Huang 		status->freq = ieee80211_channel_to_frequency(chan, band);
1400eb4e52b3SPo Hao Huang 		status->band = band;
1401eb4e52b3SPo Hao Huang 	}
1402eb4e52b3SPo Hao Huang }
1403eb4e52b3SPo Hao Huang 
1404eb4e52b3SPo Hao Huang static void rtw89_core_hw_to_sband_rate(struct ieee80211_rx_status *rx_status)
1405eb4e52b3SPo Hao Huang {
1406eb4e52b3SPo Hao Huang 	if (rx_status->band == NL80211_BAND_2GHZ ||
1407eb4e52b3SPo Hao Huang 	    rx_status->encoding != RX_ENC_LEGACY)
1408eb4e52b3SPo Hao Huang 		return;
140989590777SPo Hao Huang 
141089590777SPo Hao Huang 	/* Some control frames' freq(ACKs in this case) are reported wrong due
141189590777SPo Hao Huang 	 * to FW notify timing, set to lowest rate to prevent overflow.
141289590777SPo Hao Huang 	 */
141389590777SPo Hao Huang 	if (rx_status->rate_idx < RTW89_HW_RATE_OFDM6) {
141489590777SPo Hao Huang 		rx_status->rate_idx = 0;
141589590777SPo Hao Huang 		return;
141689590777SPo Hao Huang 	}
141789590777SPo Hao Huang 
1418eb4e52b3SPo Hao Huang 	/* No 4 CCK rates for non-2G */
1419eb4e52b3SPo Hao Huang 	rx_status->rate_idx -= 4;
1420eb4e52b3SPo Hao Huang }
1421eb4e52b3SPo Hao Huang 
1422c1ea345dSPing-Ke Shih static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
1423c1ea345dSPing-Ke Shih 				      struct rtw89_rx_phy_ppdu *phy_ppdu,
1424c1ea345dSPing-Ke Shih 				      struct rtw89_rx_desc_info *desc_info,
1425c1ea345dSPing-Ke Shih 				      struct sk_buff *skb_ppdu,
1426c1ea345dSPing-Ke Shih 				      struct ieee80211_rx_status *rx_status)
1427c1ea345dSPing-Ke Shih {
1428c1ea345dSPing-Ke Shih 	rtw89_core_hw_to_sband_rate(rx_status);
1429c1ea345dSPing-Ke Shih 	rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
1430c83dcd05SPing-Ke Shih 	/* In low power mode, it does RX in thread context. */
1431c83dcd05SPing-Ke Shih 	local_bh_disable();
1432c1ea345dSPing-Ke Shih 	ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi);
1433c83dcd05SPing-Ke Shih 	local_bh_enable();
1434c1ea345dSPing-Ke Shih 	rtwdev->napi_budget_countdown--;
1435c1ea345dSPing-Ke Shih }
1436c1ea345dSPing-Ke Shih 
1437e3ec7017SPing-Ke Shih static void rtw89_core_rx_pending_skb(struct rtw89_dev *rtwdev,
1438e3ec7017SPing-Ke Shih 				      struct rtw89_rx_phy_ppdu *phy_ppdu,
1439e3ec7017SPing-Ke Shih 				      struct rtw89_rx_desc_info *desc_info,
1440e3ec7017SPing-Ke Shih 				      struct sk_buff *skb)
1441e3ec7017SPing-Ke Shih {
1442e3ec7017SPing-Ke Shih 	u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
1443e3ec7017SPing-Ke Shih 	int curr = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band];
1444e3ec7017SPing-Ke Shih 	struct sk_buff *skb_ppdu = NULL, *tmp;
1445e3ec7017SPing-Ke Shih 	struct ieee80211_rx_status *rx_status;
1446e3ec7017SPing-Ke Shih 
1447e3ec7017SPing-Ke Shih 	if (curr > RTW89_MAX_PPDU_CNT)
1448e3ec7017SPing-Ke Shih 		return;
1449e3ec7017SPing-Ke Shih 
1450e3ec7017SPing-Ke Shih 	skb_queue_walk_safe(&rtwdev->ppdu_sts.rx_queue[band], skb_ppdu, tmp) {
1451e3ec7017SPing-Ke Shih 		skb_unlink(skb_ppdu, &rtwdev->ppdu_sts.rx_queue[band]);
1452e3ec7017SPing-Ke Shih 		rx_status = IEEE80211_SKB_RXCB(skb_ppdu);
1453e3ec7017SPing-Ke Shih 		if (rtw89_core_rx_ppdu_match(rtwdev, desc_info, rx_status))
1454e3ec7017SPing-Ke Shih 			rtw89_chip_query_ppdu(rtwdev, phy_ppdu, rx_status);
1455eb4e52b3SPo Hao Huang 		rtw89_correct_cck_chan(rtwdev, rx_status);
1456c1ea345dSPing-Ke Shih 		rtw89_core_rx_to_mac80211(rtwdev, phy_ppdu, desc_info, skb_ppdu, rx_status);
1457e3ec7017SPing-Ke Shih 	}
1458e3ec7017SPing-Ke Shih }
1459e3ec7017SPing-Ke Shih 
1460e3ec7017SPing-Ke Shih static void rtw89_core_rx_process_ppdu_sts(struct rtw89_dev *rtwdev,
1461e3ec7017SPing-Ke Shih 					   struct rtw89_rx_desc_info *desc_info,
1462e3ec7017SPing-Ke Shih 					   struct sk_buff *skb)
1463e3ec7017SPing-Ke Shih {
1464e3ec7017SPing-Ke Shih 	struct rtw89_rx_phy_ppdu phy_ppdu = {.buf = skb->data, .valid = false,
1465e3ec7017SPing-Ke Shih 					     .len = skb->len,
1466e3ec7017SPing-Ke Shih 					     .to_self = desc_info->addr1_match,
1467eb4e52b3SPo Hao Huang 					     .rate = desc_info->data_rate,
1468e3ec7017SPing-Ke Shih 					     .mac_id = desc_info->mac_id};
1469e3ec7017SPing-Ke Shih 	int ret;
1470e3ec7017SPing-Ke Shih 
1471e3ec7017SPing-Ke Shih 	if (desc_info->mac_info_valid)
1472e3ec7017SPing-Ke Shih 		rtw89_core_rx_process_mac_ppdu(rtwdev, skb, &phy_ppdu);
1473e3ec7017SPing-Ke Shih 	ret = rtw89_core_rx_process_phy_ppdu(rtwdev, &phy_ppdu);
1474e3ec7017SPing-Ke Shih 	if (ret)
1475e3ec7017SPing-Ke Shih 		rtw89_debug(rtwdev, RTW89_DBG_TXRX, "process ppdu failed\n");
1476e3ec7017SPing-Ke Shih 
1477e3ec7017SPing-Ke Shih 	rtw89_core_rx_process_phy_sts(rtwdev, &phy_ppdu);
1478e3ec7017SPing-Ke Shih 	rtw89_core_rx_pending_skb(rtwdev, &phy_ppdu, desc_info, skb);
1479e3ec7017SPing-Ke Shih 	dev_kfree_skb_any(skb);
1480e3ec7017SPing-Ke Shih }
1481e3ec7017SPing-Ke Shih 
1482e3ec7017SPing-Ke Shih static void rtw89_core_rx_process_report(struct rtw89_dev *rtwdev,
1483e3ec7017SPing-Ke Shih 					 struct rtw89_rx_desc_info *desc_info,
1484e3ec7017SPing-Ke Shih 					 struct sk_buff *skb)
1485e3ec7017SPing-Ke Shih {
1486e3ec7017SPing-Ke Shih 	switch (desc_info->pkt_type) {
1487e3ec7017SPing-Ke Shih 	case RTW89_CORE_RX_TYPE_C2H:
1488e3ec7017SPing-Ke Shih 		rtw89_fw_c2h_irqsafe(rtwdev, skb);
1489e3ec7017SPing-Ke Shih 		break;
1490e3ec7017SPing-Ke Shih 	case RTW89_CORE_RX_TYPE_PPDU_STAT:
1491e3ec7017SPing-Ke Shih 		rtw89_core_rx_process_ppdu_sts(rtwdev, desc_info, skb);
1492e3ec7017SPing-Ke Shih 		break;
1493e3ec7017SPing-Ke Shih 	default:
1494e3ec7017SPing-Ke Shih 		rtw89_debug(rtwdev, RTW89_DBG_TXRX, "unhandled pkt_type=%d\n",
1495e3ec7017SPing-Ke Shih 			    desc_info->pkt_type);
1496e3ec7017SPing-Ke Shih 		dev_kfree_skb_any(skb);
1497e3ec7017SPing-Ke Shih 		break;
1498e3ec7017SPing-Ke Shih 	}
1499e3ec7017SPing-Ke Shih }
1500e3ec7017SPing-Ke Shih 
1501e3ec7017SPing-Ke Shih void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev,
1502e3ec7017SPing-Ke Shih 			     struct rtw89_rx_desc_info *desc_info,
1503e3ec7017SPing-Ke Shih 			     u8 *data, u32 data_offset)
1504e3ec7017SPing-Ke Shih {
150584fc6999SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
1506e3ec7017SPing-Ke Shih 	struct rtw89_rxdesc_short *rxd_s;
1507e3ec7017SPing-Ke Shih 	struct rtw89_rxdesc_long *rxd_l;
1508e3ec7017SPing-Ke Shih 	u8 shift_len, drv_info_len;
1509e3ec7017SPing-Ke Shih 
1510e3ec7017SPing-Ke Shih 	rxd_s = (struct rtw89_rxdesc_short *)(data + data_offset);
1511e3ec7017SPing-Ke Shih 	desc_info->pkt_size = RTW89_GET_RXWD_PKT_SIZE(rxd_s);
1512e3ec7017SPing-Ke Shih 	desc_info->drv_info_size = RTW89_GET_RXWD_DRV_INFO_SIZE(rxd_s);
1513e3ec7017SPing-Ke Shih 	desc_info->long_rxdesc = RTW89_GET_RXWD_LONG_RXD(rxd_s);
1514e3ec7017SPing-Ke Shih 	desc_info->pkt_type = RTW89_GET_RXWD_RPKT_TYPE(rxd_s);
1515e3ec7017SPing-Ke Shih 	desc_info->mac_info_valid = RTW89_GET_RXWD_MAC_INFO_VALID(rxd_s);
151684fc6999SPing-Ke Shih 	if (chip->chip_id == RTL8852C)
151784fc6999SPing-Ke Shih 		desc_info->bw = RTW89_GET_RXWD_BW_V1(rxd_s);
151884fc6999SPing-Ke Shih 	else
1519e3ec7017SPing-Ke Shih 		desc_info->bw = RTW89_GET_RXWD_BW(rxd_s);
1520e3ec7017SPing-Ke Shih 	desc_info->data_rate = RTW89_GET_RXWD_DATA_RATE(rxd_s);
1521e3ec7017SPing-Ke Shih 	desc_info->gi_ltf = RTW89_GET_RXWD_GI_LTF(rxd_s);
1522e3ec7017SPing-Ke Shih 	desc_info->user_id = RTW89_GET_RXWD_USER_ID(rxd_s);
1523e3ec7017SPing-Ke Shih 	desc_info->sr_en = RTW89_GET_RXWD_SR_EN(rxd_s);
1524e3ec7017SPing-Ke Shih 	desc_info->ppdu_cnt = RTW89_GET_RXWD_PPDU_CNT(rxd_s);
1525e3ec7017SPing-Ke Shih 	desc_info->ppdu_type = RTW89_GET_RXWD_PPDU_TYPE(rxd_s);
1526e3ec7017SPing-Ke Shih 	desc_info->free_run_cnt = RTW89_GET_RXWD_FREE_RUN_CNT(rxd_s);
1527e3ec7017SPing-Ke Shih 	desc_info->icv_err = RTW89_GET_RXWD_ICV_ERR(rxd_s);
1528e3ec7017SPing-Ke Shih 	desc_info->crc32_err = RTW89_GET_RXWD_CRC32_ERR(rxd_s);
1529e3ec7017SPing-Ke Shih 	desc_info->hw_dec = RTW89_GET_RXWD_HW_DEC(rxd_s);
1530e3ec7017SPing-Ke Shih 	desc_info->sw_dec = RTW89_GET_RXWD_SW_DEC(rxd_s);
1531e3ec7017SPing-Ke Shih 	desc_info->addr1_match = RTW89_GET_RXWD_A1_MATCH(rxd_s);
1532e3ec7017SPing-Ke Shih 
1533e3ec7017SPing-Ke Shih 	shift_len = desc_info->shift << 1; /* 2-byte unit */
1534e3ec7017SPing-Ke Shih 	drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */
1535e3ec7017SPing-Ke Shih 	desc_info->offset = data_offset + shift_len + drv_info_len;
1536e3ec7017SPing-Ke Shih 	desc_info->ready = true;
1537e3ec7017SPing-Ke Shih 
1538e3ec7017SPing-Ke Shih 	if (!desc_info->long_rxdesc)
1539e3ec7017SPing-Ke Shih 		return;
1540e3ec7017SPing-Ke Shih 
1541e3ec7017SPing-Ke Shih 	rxd_l = (struct rtw89_rxdesc_long *)(data + data_offset);
1542e3ec7017SPing-Ke Shih 	desc_info->frame_type = RTW89_GET_RXWD_TYPE(rxd_l);
1543e3ec7017SPing-Ke Shih 	desc_info->addr_cam_valid = RTW89_GET_RXWD_ADDR_CAM_VLD(rxd_l);
1544e3ec7017SPing-Ke Shih 	desc_info->addr_cam_id = RTW89_GET_RXWD_ADDR_CAM_ID(rxd_l);
1545e3ec7017SPing-Ke Shih 	desc_info->sec_cam_id = RTW89_GET_RXWD_SEC_CAM_ID(rxd_l);
1546e3ec7017SPing-Ke Shih 	desc_info->mac_id = RTW89_GET_RXWD_MAC_ID(rxd_l);
1547e3ec7017SPing-Ke Shih 	desc_info->rx_pl_id = RTW89_GET_RXWD_RX_PL_ID(rxd_l);
1548e3ec7017SPing-Ke Shih }
1549e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_query_rxdesc);
1550e3ec7017SPing-Ke Shih 
1551e3ec7017SPing-Ke Shih struct rtw89_core_iter_rx_status {
1552e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev;
1553e3ec7017SPing-Ke Shih 	struct ieee80211_rx_status *rx_status;
1554e3ec7017SPing-Ke Shih 	struct rtw89_rx_desc_info *desc_info;
1555e3ec7017SPing-Ke Shih 	u8 mac_id;
1556e3ec7017SPing-Ke Shih };
1557e3ec7017SPing-Ke Shih 
1558e3ec7017SPing-Ke Shih static
1559e3ec7017SPing-Ke Shih void rtw89_core_stats_sta_rx_status_iter(void *data, struct ieee80211_sta *sta)
1560e3ec7017SPing-Ke Shih {
1561e3ec7017SPing-Ke Shih 	struct rtw89_core_iter_rx_status *iter_data =
1562e3ec7017SPing-Ke Shih 				(struct rtw89_core_iter_rx_status *)data;
1563e3ec7017SPing-Ke Shih 	struct ieee80211_rx_status *rx_status = iter_data->rx_status;
1564e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
1565e3ec7017SPing-Ke Shih 	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
1566e3ec7017SPing-Ke Shih 	u8 mac_id = iter_data->mac_id;
1567e3ec7017SPing-Ke Shih 
1568e3ec7017SPing-Ke Shih 	if (mac_id != rtwsta->mac_id)
1569e3ec7017SPing-Ke Shih 		return;
1570e3ec7017SPing-Ke Shih 
1571e3ec7017SPing-Ke Shih 	rtwsta->rx_status = *rx_status;
1572e3ec7017SPing-Ke Shih 	rtwsta->rx_hw_rate = desc_info->data_rate;
1573e3ec7017SPing-Ke Shih }
1574e3ec7017SPing-Ke Shih 
1575e3ec7017SPing-Ke Shih static void rtw89_core_stats_sta_rx_status(struct rtw89_dev *rtwdev,
1576e3ec7017SPing-Ke Shih 					   struct rtw89_rx_desc_info *desc_info,
1577e3ec7017SPing-Ke Shih 					   struct ieee80211_rx_status *rx_status)
1578e3ec7017SPing-Ke Shih {
1579e3ec7017SPing-Ke Shih 	struct rtw89_core_iter_rx_status iter_data;
1580e3ec7017SPing-Ke Shih 
1581e3ec7017SPing-Ke Shih 	if (!desc_info->addr1_match || !desc_info->long_rxdesc)
1582e3ec7017SPing-Ke Shih 		return;
1583e3ec7017SPing-Ke Shih 
1584e3ec7017SPing-Ke Shih 	if (desc_info->frame_type != RTW89_RX_TYPE_DATA)
1585e3ec7017SPing-Ke Shih 		return;
1586e3ec7017SPing-Ke Shih 
1587e3ec7017SPing-Ke Shih 	iter_data.rtwdev = rtwdev;
1588e3ec7017SPing-Ke Shih 	iter_data.rx_status = rx_status;
1589e3ec7017SPing-Ke Shih 	iter_data.desc_info = desc_info;
1590e3ec7017SPing-Ke Shih 	iter_data.mac_id = desc_info->mac_id;
1591e3ec7017SPing-Ke Shih 	ieee80211_iterate_stations_atomic(rtwdev->hw,
1592e3ec7017SPing-Ke Shih 					  rtw89_core_stats_sta_rx_status_iter,
1593e3ec7017SPing-Ke Shih 					  &iter_data);
1594e3ec7017SPing-Ke Shih }
1595e3ec7017SPing-Ke Shih 
1596e3ec7017SPing-Ke Shih static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev,
1597e3ec7017SPing-Ke Shih 					struct rtw89_rx_desc_info *desc_info,
1598e3ec7017SPing-Ke Shih 					struct ieee80211_rx_status *rx_status)
1599e3ec7017SPing-Ke Shih {
1600e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
160189590777SPo Hao Huang 	struct rtw89_hal *hal = &rtwdev->hal;
1602e3ec7017SPing-Ke Shih 	u16 data_rate;
1603e3ec7017SPing-Ke Shih 	u8 data_rate_mode;
1604e3ec7017SPing-Ke Shih 
1605e3ec7017SPing-Ke Shih 	/* currently using single PHY */
1606e3ec7017SPing-Ke Shih 	rx_status->freq = hw->conf.chandef.chan->center_freq;
1607e3ec7017SPing-Ke Shih 	rx_status->band = hw->conf.chandef.chan->band;
1608e3ec7017SPing-Ke Shih 
160911fe4ccdSZong-Zhe Yang 	if (rtwdev->scanning &&
161011fe4ccdSZong-Zhe Yang 	    RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {
1611*a06d2dd7SZong-Zhe Yang 		u8 chan = hal->current_channel;
1612*a06d2dd7SZong-Zhe Yang 		u8 band = hal->current_band_type;
1613*a06d2dd7SZong-Zhe Yang 		enum nl80211_band nl_band;
1614*a06d2dd7SZong-Zhe Yang 
1615*a06d2dd7SZong-Zhe Yang 		nl_band = rtw89_hw_to_nl80211_band(band);
1616*a06d2dd7SZong-Zhe Yang 		rx_status->freq = ieee80211_channel_to_frequency(chan, nl_band);
1617*a06d2dd7SZong-Zhe Yang 		rx_status->band = nl_band;
161889590777SPo Hao Huang 	}
161989590777SPo Hao Huang 
1620e3ec7017SPing-Ke Shih 	if (desc_info->icv_err || desc_info->crc32_err)
1621e3ec7017SPing-Ke Shih 		rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
1622e3ec7017SPing-Ke Shih 
1623e3ec7017SPing-Ke Shih 	if (desc_info->hw_dec &&
1624e3ec7017SPing-Ke Shih 	    !(desc_info->sw_dec || desc_info->icv_err))
1625e3ec7017SPing-Ke Shih 		rx_status->flag |= RX_FLAG_DECRYPTED;
1626e3ec7017SPing-Ke Shih 
1627167044afSPing-Ke Shih 	rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw);
1628e3ec7017SPing-Ke Shih 
1629e3ec7017SPing-Ke Shih 	data_rate = desc_info->data_rate;
1630e3ec7017SPing-Ke Shih 	data_rate_mode = GET_DATA_RATE_MODE(data_rate);
1631e3ec7017SPing-Ke Shih 	if (data_rate_mode == DATA_RATE_MODE_NON_HT) {
1632e3ec7017SPing-Ke Shih 		rx_status->encoding = RX_ENC_LEGACY;
1633e3ec7017SPing-Ke Shih 		rx_status->rate_idx = GET_DATA_RATE_NOT_HT_IDX(data_rate);
1634eb4e52b3SPo Hao Huang 		/* convert rate_idx after we get the correct band */
1635e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_HT) {
1636e3ec7017SPing-Ke Shih 		rx_status->encoding = RX_ENC_HT;
1637e3ec7017SPing-Ke Shih 		rx_status->rate_idx = GET_DATA_RATE_HT_IDX(data_rate);
1638e3ec7017SPing-Ke Shih 		if (desc_info->gi_ltf)
1639e3ec7017SPing-Ke Shih 			rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
1640e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_VHT) {
1641e3ec7017SPing-Ke Shih 		rx_status->encoding = RX_ENC_VHT;
1642e3ec7017SPing-Ke Shih 		rx_status->rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
1643e3ec7017SPing-Ke Shih 		rx_status->nss = GET_DATA_RATE_NSS(data_rate) + 1;
1644e3ec7017SPing-Ke Shih 		if (desc_info->gi_ltf)
1645e3ec7017SPing-Ke Shih 			rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
1646e3ec7017SPing-Ke Shih 	} else if (data_rate_mode == DATA_RATE_MODE_HE) {
1647e3ec7017SPing-Ke Shih 		rx_status->encoding = RX_ENC_HE;
1648e3ec7017SPing-Ke Shih 		rx_status->rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
1649e3ec7017SPing-Ke Shih 		rx_status->nss = GET_DATA_RATE_NSS(data_rate) + 1;
1650e3ec7017SPing-Ke Shih 	} else {
1651e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "invalid RX rate mode %d\n", data_rate_mode);
1652e3ec7017SPing-Ke Shih 	}
1653e3ec7017SPing-Ke Shih 
1654e3ec7017SPing-Ke Shih 	/* he_gi is used to match ppdu, so we always fill it. */
1655e3ec7017SPing-Ke Shih 	rx_status->he_gi = rtw89_rxdesc_to_nl_he_gi(rtwdev, desc_info, true);
1656e3ec7017SPing-Ke Shih 	rx_status->flag |= RX_FLAG_MACTIME_START;
1657e3ec7017SPing-Ke Shih 	rx_status->mactime = desc_info->free_run_cnt;
1658e3ec7017SPing-Ke Shih 
1659e3ec7017SPing-Ke Shih 	rtw89_core_stats_sta_rx_status(rtwdev, desc_info, rx_status);
1660e3ec7017SPing-Ke Shih }
1661e3ec7017SPing-Ke Shih 
1662e3ec7017SPing-Ke Shih static enum rtw89_ps_mode rtw89_update_ps_mode(struct rtw89_dev *rtwdev)
1663e3ec7017SPing-Ke Shih {
1664e3ec7017SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
1665e3ec7017SPing-Ke Shih 
1666e3ec7017SPing-Ke Shih 	if (rtw89_disable_ps_mode || !chip->ps_mode_supported)
1667e3ec7017SPing-Ke Shih 		return RTW89_PS_MODE_NONE;
1668e3ec7017SPing-Ke Shih 
1669e3ec7017SPing-Ke Shih 	if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_PWR_GATED))
1670e3ec7017SPing-Ke Shih 		return RTW89_PS_MODE_PWR_GATED;
1671e3ec7017SPing-Ke Shih 
1672e3ec7017SPing-Ke Shih 	if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_CLK_GATED))
1673e3ec7017SPing-Ke Shih 		return RTW89_PS_MODE_CLK_GATED;
1674e3ec7017SPing-Ke Shih 
1675e3ec7017SPing-Ke Shih 	if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_RFOFF))
1676e3ec7017SPing-Ke Shih 		return RTW89_PS_MODE_RFOFF;
1677e3ec7017SPing-Ke Shih 
1678e3ec7017SPing-Ke Shih 	return RTW89_PS_MODE_NONE;
1679e3ec7017SPing-Ke Shih }
1680e3ec7017SPing-Ke Shih 
1681e3ec7017SPing-Ke Shih static void rtw89_core_flush_ppdu_rx_queue(struct rtw89_dev *rtwdev,
1682e3ec7017SPing-Ke Shih 					   struct rtw89_rx_desc_info *desc_info)
1683e3ec7017SPing-Ke Shih {
1684e3ec7017SPing-Ke Shih 	struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts;
1685e3ec7017SPing-Ke Shih 	u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
1686eb4e52b3SPo Hao Huang 	struct ieee80211_rx_status *rx_status;
1687e3ec7017SPing-Ke Shih 	struct sk_buff *skb_ppdu, *tmp;
1688e3ec7017SPing-Ke Shih 
1689e3ec7017SPing-Ke Shih 	skb_queue_walk_safe(&ppdu_sts->rx_queue[band], skb_ppdu, tmp) {
1690e3ec7017SPing-Ke Shih 		skb_unlink(skb_ppdu, &ppdu_sts->rx_queue[band]);
1691eb4e52b3SPo Hao Huang 		rx_status = IEEE80211_SKB_RXCB(skb_ppdu);
1692c1ea345dSPing-Ke Shih 		rtw89_core_rx_to_mac80211(rtwdev, NULL, desc_info, skb_ppdu, rx_status);
1693e3ec7017SPing-Ke Shih 	}
1694e3ec7017SPing-Ke Shih }
1695e3ec7017SPing-Ke Shih 
1696e3ec7017SPing-Ke Shih void rtw89_core_rx(struct rtw89_dev *rtwdev,
1697e3ec7017SPing-Ke Shih 		   struct rtw89_rx_desc_info *desc_info,
1698e3ec7017SPing-Ke Shih 		   struct sk_buff *skb)
1699e3ec7017SPing-Ke Shih {
1700e3ec7017SPing-Ke Shih 	struct ieee80211_rx_status *rx_status;
1701e3ec7017SPing-Ke Shih 	struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts;
1702e3ec7017SPing-Ke Shih 	u8 ppdu_cnt = desc_info->ppdu_cnt;
1703e3ec7017SPing-Ke Shih 	u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
1704e3ec7017SPing-Ke Shih 
1705e3ec7017SPing-Ke Shih 	if (desc_info->pkt_type != RTW89_CORE_RX_TYPE_WIFI) {
1706e3ec7017SPing-Ke Shih 		rtw89_core_rx_process_report(rtwdev, desc_info, skb);
1707e3ec7017SPing-Ke Shih 		return;
1708e3ec7017SPing-Ke Shih 	}
1709e3ec7017SPing-Ke Shih 
1710e3ec7017SPing-Ke Shih 	if (ppdu_sts->curr_rx_ppdu_cnt[band] != ppdu_cnt) {
1711e3ec7017SPing-Ke Shih 		rtw89_core_flush_ppdu_rx_queue(rtwdev, desc_info);
1712e3ec7017SPing-Ke Shih 		ppdu_sts->curr_rx_ppdu_cnt[band] = ppdu_cnt;
1713e3ec7017SPing-Ke Shih 	}
1714e3ec7017SPing-Ke Shih 
1715e3ec7017SPing-Ke Shih 	rx_status = IEEE80211_SKB_RXCB(skb);
1716e3ec7017SPing-Ke Shih 	memset(rx_status, 0, sizeof(*rx_status));
1717e3ec7017SPing-Ke Shih 	rtw89_core_update_rx_status(rtwdev, desc_info, rx_status);
1718e3ec7017SPing-Ke Shih 	if (desc_info->long_rxdesc &&
1719c1ea345dSPing-Ke Shih 	    BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP)
1720e3ec7017SPing-Ke Shih 		skb_queue_tail(&ppdu_sts->rx_queue[band], skb);
1721c1ea345dSPing-Ke Shih 	else
1722c1ea345dSPing-Ke Shih 		rtw89_core_rx_to_mac80211(rtwdev, NULL, desc_info, skb, rx_status);
1723e3ec7017SPing-Ke Shih }
1724e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_rx);
1725e3ec7017SPing-Ke Shih 
1726e3ec7017SPing-Ke Shih void rtw89_core_napi_start(struct rtw89_dev *rtwdev)
1727e3ec7017SPing-Ke Shih {
1728e3ec7017SPing-Ke Shih 	if (test_and_set_bit(RTW89_FLAG_NAPI_RUNNING, rtwdev->flags))
1729e3ec7017SPing-Ke Shih 		return;
1730e3ec7017SPing-Ke Shih 
1731e3ec7017SPing-Ke Shih 	napi_enable(&rtwdev->napi);
1732e3ec7017SPing-Ke Shih }
1733e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_napi_start);
1734e3ec7017SPing-Ke Shih 
1735e3ec7017SPing-Ke Shih void rtw89_core_napi_stop(struct rtw89_dev *rtwdev)
1736e3ec7017SPing-Ke Shih {
1737e3ec7017SPing-Ke Shih 	if (!test_and_clear_bit(RTW89_FLAG_NAPI_RUNNING, rtwdev->flags))
1738e3ec7017SPing-Ke Shih 		return;
1739e3ec7017SPing-Ke Shih 
1740e3ec7017SPing-Ke Shih 	napi_synchronize(&rtwdev->napi);
1741e3ec7017SPing-Ke Shih 	napi_disable(&rtwdev->napi);
1742e3ec7017SPing-Ke Shih }
1743e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_napi_stop);
1744e3ec7017SPing-Ke Shih 
1745e3ec7017SPing-Ke Shih void rtw89_core_napi_init(struct rtw89_dev *rtwdev)
1746e3ec7017SPing-Ke Shih {
1747e3ec7017SPing-Ke Shih 	init_dummy_netdev(&rtwdev->netdev);
1748e3ec7017SPing-Ke Shih 	netif_napi_add(&rtwdev->netdev, &rtwdev->napi,
1749e3ec7017SPing-Ke Shih 		       rtwdev->hci.ops->napi_poll, NAPI_POLL_WEIGHT);
1750e3ec7017SPing-Ke Shih }
1751e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_napi_init);
1752e3ec7017SPing-Ke Shih 
1753e3ec7017SPing-Ke Shih void rtw89_core_napi_deinit(struct rtw89_dev *rtwdev)
1754e3ec7017SPing-Ke Shih {
1755e3ec7017SPing-Ke Shih 	rtw89_core_napi_stop(rtwdev);
1756e3ec7017SPing-Ke Shih 	netif_napi_del(&rtwdev->napi);
1757e3ec7017SPing-Ke Shih }
1758e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_napi_deinit);
1759e3ec7017SPing-Ke Shih 
1760e3ec7017SPing-Ke Shih static void rtw89_core_ba_work(struct work_struct *work)
1761e3ec7017SPing-Ke Shih {
1762e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev =
1763e3ec7017SPing-Ke Shih 		container_of(work, struct rtw89_dev, ba_work);
1764e3ec7017SPing-Ke Shih 	struct rtw89_txq *rtwtxq, *tmp;
1765e3ec7017SPing-Ke Shih 	int ret;
1766e3ec7017SPing-Ke Shih 
1767e3ec7017SPing-Ke Shih 	spin_lock_bh(&rtwdev->ba_lock);
1768e3ec7017SPing-Ke Shih 	list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->ba_list, list) {
1769e3ec7017SPing-Ke Shih 		struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
1770e3ec7017SPing-Ke Shih 		struct ieee80211_sta *sta = txq->sta;
17715d44f067SPing-Ke Shih 		struct rtw89_sta *rtwsta = sta ? (struct rtw89_sta *)sta->drv_priv : NULL;
1772e3ec7017SPing-Ke Shih 		u8 tid = txq->tid;
1773e3ec7017SPing-Ke Shih 
1774e3ec7017SPing-Ke Shih 		if (!sta) {
1775e3ec7017SPing-Ke Shih 			rtw89_warn(rtwdev, "cannot start BA without sta\n");
1776e3ec7017SPing-Ke Shih 			goto skip_ba_work;
1777e3ec7017SPing-Ke Shih 		}
1778e3ec7017SPing-Ke Shih 
1779e3ec7017SPing-Ke Shih 		if (rtwsta->disassoc) {
1780e3ec7017SPing-Ke Shih 			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
1781e3ec7017SPing-Ke Shih 				    "cannot start BA with disassoc sta\n");
1782e3ec7017SPing-Ke Shih 			goto skip_ba_work;
1783e3ec7017SPing-Ke Shih 		}
1784e3ec7017SPing-Ke Shih 
1785e3ec7017SPing-Ke Shih 		ret = ieee80211_start_tx_ba_session(sta, tid, 0);
1786e3ec7017SPing-Ke Shih 		if (ret) {
1787e3ec7017SPing-Ke Shih 			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
1788e3ec7017SPing-Ke Shih 				    "failed to setup BA session for %pM:%2d: %d\n",
1789e3ec7017SPing-Ke Shih 				    sta->addr, tid, ret);
1790e3ec7017SPing-Ke Shih 			if (ret == -EINVAL)
1791e3ec7017SPing-Ke Shih 				set_bit(RTW89_TXQ_F_BLOCK_BA, &rtwtxq->flags);
1792e3ec7017SPing-Ke Shih 		}
1793e3ec7017SPing-Ke Shih skip_ba_work:
1794e3ec7017SPing-Ke Shih 		list_del_init(&rtwtxq->list);
1795e3ec7017SPing-Ke Shih 	}
1796e3ec7017SPing-Ke Shih 	spin_unlock_bh(&rtwdev->ba_lock);
1797e3ec7017SPing-Ke Shih }
1798e3ec7017SPing-Ke Shih 
1799e3ec7017SPing-Ke Shih static void rtw89_core_free_sta_pending_ba(struct rtw89_dev *rtwdev,
1800e3ec7017SPing-Ke Shih 					   struct ieee80211_sta *sta)
1801e3ec7017SPing-Ke Shih {
1802e3ec7017SPing-Ke Shih 	struct rtw89_txq *rtwtxq, *tmp;
1803e3ec7017SPing-Ke Shih 
1804e3ec7017SPing-Ke Shih 	spin_lock_bh(&rtwdev->ba_lock);
1805e3ec7017SPing-Ke Shih 	list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->ba_list, list) {
1806e3ec7017SPing-Ke Shih 		struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
1807e3ec7017SPing-Ke Shih 
1808e3ec7017SPing-Ke Shih 		if (sta == txq->sta)
1809e3ec7017SPing-Ke Shih 			list_del_init(&rtwtxq->list);
1810e3ec7017SPing-Ke Shih 	}
1811e3ec7017SPing-Ke Shih 	spin_unlock_bh(&rtwdev->ba_lock);
1812e3ec7017SPing-Ke Shih }
1813e3ec7017SPing-Ke Shih 
1814e3ec7017SPing-Ke Shih static void rtw89_core_txq_check_agg(struct rtw89_dev *rtwdev,
1815e3ec7017SPing-Ke Shih 				     struct rtw89_txq *rtwtxq,
1816e3ec7017SPing-Ke Shih 				     struct sk_buff *skb)
1817e3ec7017SPing-Ke Shih {
1818e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
1819e3ec7017SPing-Ke Shih 	struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
1820e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = txq->sta;
18215d44f067SPing-Ke Shih 	struct rtw89_sta *rtwsta = sta ? (struct rtw89_sta *)sta->drv_priv : NULL;
1822e3ec7017SPing-Ke Shih 
1823e3ec7017SPing-Ke Shih 	if (unlikely(skb_get_queue_mapping(skb) == IEEE80211_AC_VO))
1824e3ec7017SPing-Ke Shih 		return;
1825e3ec7017SPing-Ke Shih 
1826e3ec7017SPing-Ke Shih 	if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
1827e3ec7017SPing-Ke Shih 		return;
1828e3ec7017SPing-Ke Shih 
1829e3ec7017SPing-Ke Shih 	if (unlikely(!sta))
1830e3ec7017SPing-Ke Shih 		return;
1831e3ec7017SPing-Ke Shih 
1832e3ec7017SPing-Ke Shih 	if (unlikely(test_bit(RTW89_TXQ_F_BLOCK_BA, &rtwtxq->flags)))
1833e3ec7017SPing-Ke Shih 		return;
1834e3ec7017SPing-Ke Shih 
1835e3ec7017SPing-Ke Shih 	if (test_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags)) {
1836e3ec7017SPing-Ke Shih 		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_AMPDU;
1837e3ec7017SPing-Ke Shih 		return;
1838e3ec7017SPing-Ke Shih 	}
1839e3ec7017SPing-Ke Shih 
1840e3ec7017SPing-Ke Shih 	spin_lock_bh(&rtwdev->ba_lock);
1841e3ec7017SPing-Ke Shih 	if (!rtwsta->disassoc && list_empty(&rtwtxq->list)) {
1842e3ec7017SPing-Ke Shih 		list_add_tail(&rtwtxq->list, &rtwdev->ba_list);
1843e3ec7017SPing-Ke Shih 		ieee80211_queue_work(hw, &rtwdev->ba_work);
1844e3ec7017SPing-Ke Shih 	}
1845e3ec7017SPing-Ke Shih 	spin_unlock_bh(&rtwdev->ba_lock);
1846e3ec7017SPing-Ke Shih }
1847e3ec7017SPing-Ke Shih 
1848e3ec7017SPing-Ke Shih static void rtw89_core_txq_push(struct rtw89_dev *rtwdev,
1849e3ec7017SPing-Ke Shih 				struct rtw89_txq *rtwtxq,
1850e3ec7017SPing-Ke Shih 				unsigned long frame_cnt,
1851e3ec7017SPing-Ke Shih 				unsigned long byte_cnt)
1852e3ec7017SPing-Ke Shih {
1853e3ec7017SPing-Ke Shih 	struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
1854e3ec7017SPing-Ke Shih 	struct ieee80211_vif *vif = txq->vif;
1855e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = txq->sta;
1856e3ec7017SPing-Ke Shih 	struct sk_buff *skb;
1857e3ec7017SPing-Ke Shih 	unsigned long i;
1858e3ec7017SPing-Ke Shih 	int ret;
1859e3ec7017SPing-Ke Shih 
1860f3d825a3SJiri Kosina 	rcu_read_lock();
1861e3ec7017SPing-Ke Shih 	for (i = 0; i < frame_cnt; i++) {
1862e3ec7017SPing-Ke Shih 		skb = ieee80211_tx_dequeue_ni(rtwdev->hw, txq);
1863e3ec7017SPing-Ke Shih 		if (!skb) {
1864e3ec7017SPing-Ke Shih 			rtw89_debug(rtwdev, RTW89_DBG_TXRX, "dequeue a NULL skb\n");
1865f3d825a3SJiri Kosina 			goto out;
1866e3ec7017SPing-Ke Shih 		}
1867e3ec7017SPing-Ke Shih 		rtw89_core_txq_check_agg(rtwdev, rtwtxq, skb);
1868e3ec7017SPing-Ke Shih 		ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, NULL);
1869e3ec7017SPing-Ke Shih 		if (ret) {
1870e3ec7017SPing-Ke Shih 			rtw89_err(rtwdev, "failed to push txq: %d\n", ret);
1871e3ec7017SPing-Ke Shih 			ieee80211_free_txskb(rtwdev->hw, skb);
1872e3ec7017SPing-Ke Shih 			break;
1873e3ec7017SPing-Ke Shih 		}
1874e3ec7017SPing-Ke Shih 	}
1875f3d825a3SJiri Kosina out:
1876f3d825a3SJiri Kosina 	rcu_read_unlock();
1877e3ec7017SPing-Ke Shih }
1878e3ec7017SPing-Ke Shih 
1879e3ec7017SPing-Ke Shih static u32 rtw89_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 tid)
1880e3ec7017SPing-Ke Shih {
1881e3ec7017SPing-Ke Shih 	u8 qsel, ch_dma;
1882e3ec7017SPing-Ke Shih 
1883e3ec7017SPing-Ke Shih 	qsel = rtw89_core_get_qsel(rtwdev, tid);
1884e3ec7017SPing-Ke Shih 	ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
1885e3ec7017SPing-Ke Shih 
1886e3ec7017SPing-Ke Shih 	return rtw89_hci_check_and_reclaim_tx_resource(rtwdev, ch_dma);
1887e3ec7017SPing-Ke Shih }
1888e3ec7017SPing-Ke Shih 
1889e3ec7017SPing-Ke Shih static bool rtw89_core_txq_agg_wait(struct rtw89_dev *rtwdev,
1890e3ec7017SPing-Ke Shih 				    struct ieee80211_txq *txq,
1891e3ec7017SPing-Ke Shih 				    unsigned long *frame_cnt,
1892e3ec7017SPing-Ke Shih 				    bool *sched_txq, bool *reinvoke)
1893e3ec7017SPing-Ke Shih {
1894e3ec7017SPing-Ke Shih 	struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv;
1895e3ec7017SPing-Ke Shih 	struct ieee80211_sta *sta = txq->sta;
18965d44f067SPing-Ke Shih 	struct rtw89_sta *rtwsta = sta ? (struct rtw89_sta *)sta->drv_priv : NULL;
1897e3ec7017SPing-Ke Shih 
1898e3ec7017SPing-Ke Shih 	if (!sta || rtwsta->max_agg_wait <= 0)
1899e3ec7017SPing-Ke Shih 		return false;
1900e3ec7017SPing-Ke Shih 
1901e3ec7017SPing-Ke Shih 	if (rtwdev->stats.tx_tfc_lv <= RTW89_TFC_MID)
1902e3ec7017SPing-Ke Shih 		return false;
1903e3ec7017SPing-Ke Shih 
1904e3ec7017SPing-Ke Shih 	if (*frame_cnt > 1) {
1905e3ec7017SPing-Ke Shih 		*frame_cnt -= 1;
1906e3ec7017SPing-Ke Shih 		*sched_txq = true;
1907e3ec7017SPing-Ke Shih 		*reinvoke = true;
1908e3ec7017SPing-Ke Shih 		rtwtxq->wait_cnt = 1;
1909e3ec7017SPing-Ke Shih 		return false;
1910e3ec7017SPing-Ke Shih 	}
1911e3ec7017SPing-Ke Shih 
1912e3ec7017SPing-Ke Shih 	if (*frame_cnt == 1 && rtwtxq->wait_cnt < rtwsta->max_agg_wait) {
1913e3ec7017SPing-Ke Shih 		*reinvoke = true;
1914e3ec7017SPing-Ke Shih 		rtwtxq->wait_cnt++;
1915e3ec7017SPing-Ke Shih 		return true;
1916e3ec7017SPing-Ke Shih 	}
1917e3ec7017SPing-Ke Shih 
1918e3ec7017SPing-Ke Shih 	rtwtxq->wait_cnt = 0;
1919e3ec7017SPing-Ke Shih 	return false;
1920e3ec7017SPing-Ke Shih }
1921e3ec7017SPing-Ke Shih 
1922e3ec7017SPing-Ke Shih static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinvoke)
1923e3ec7017SPing-Ke Shih {
1924e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
1925e3ec7017SPing-Ke Shih 	struct ieee80211_txq *txq;
1926e3ec7017SPing-Ke Shih 	struct rtw89_txq *rtwtxq;
1927e3ec7017SPing-Ke Shih 	unsigned long frame_cnt;
1928e3ec7017SPing-Ke Shih 	unsigned long byte_cnt;
1929e3ec7017SPing-Ke Shih 	u32 tx_resource;
1930e3ec7017SPing-Ke Shih 	bool sched_txq;
1931e3ec7017SPing-Ke Shih 
1932e3ec7017SPing-Ke Shih 	ieee80211_txq_schedule_start(hw, ac);
1933e3ec7017SPing-Ke Shih 	while ((txq = ieee80211_next_txq(hw, ac))) {
1934e3ec7017SPing-Ke Shih 		rtwtxq = (struct rtw89_txq *)txq->drv_priv;
1935e3ec7017SPing-Ke Shih 		tx_resource = rtw89_check_and_reclaim_tx_resource(rtwdev, txq->tid);
1936e3ec7017SPing-Ke Shih 		sched_txq = false;
1937e3ec7017SPing-Ke Shih 
1938e3ec7017SPing-Ke Shih 		ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt);
1939e3ec7017SPing-Ke Shih 		if (rtw89_core_txq_agg_wait(rtwdev, txq, &frame_cnt, &sched_txq, reinvoke)) {
1940e3ec7017SPing-Ke Shih 			ieee80211_return_txq(hw, txq, true);
1941e3ec7017SPing-Ke Shih 			continue;
1942e3ec7017SPing-Ke Shih 		}
1943e3ec7017SPing-Ke Shih 		frame_cnt = min_t(unsigned long, frame_cnt, tx_resource);
1944e3ec7017SPing-Ke Shih 		rtw89_core_txq_push(rtwdev, rtwtxq, frame_cnt, byte_cnt);
1945e3ec7017SPing-Ke Shih 		ieee80211_return_txq(hw, txq, sched_txq);
1946e3ec7017SPing-Ke Shih 		if (frame_cnt != 0)
1947e3ec7017SPing-Ke Shih 			rtw89_core_tx_kick_off(rtwdev, rtw89_core_get_qsel(rtwdev, txq->tid));
1948e3ec7017SPing-Ke Shih 	}
1949e3ec7017SPing-Ke Shih 	ieee80211_txq_schedule_end(hw, ac);
1950e3ec7017SPing-Ke Shih }
1951e3ec7017SPing-Ke Shih 
195289590777SPo Hao Huang static void rtw89_ips_work(struct work_struct *work)
195389590777SPo Hao Huang {
195489590777SPo Hao Huang 	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
195589590777SPo Hao Huang 						ips_work);
195689590777SPo Hao Huang 	mutex_lock(&rtwdev->mutex);
1957ee20d538SPo Hao Huang 	if (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)
195889590777SPo Hao Huang 		rtw89_enter_ips(rtwdev);
195989590777SPo Hao Huang 	mutex_unlock(&rtwdev->mutex);
196089590777SPo Hao Huang }
196189590777SPo Hao Huang 
1962e3ec7017SPing-Ke Shih static void rtw89_core_txq_work(struct work_struct *w)
1963e3ec7017SPing-Ke Shih {
1964e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev, txq_work);
1965e3ec7017SPing-Ke Shih 	bool reinvoke = false;
1966e3ec7017SPing-Ke Shih 	u8 ac;
1967e3ec7017SPing-Ke Shih 
1968e3ec7017SPing-Ke Shih 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
1969e3ec7017SPing-Ke Shih 		rtw89_core_txq_schedule(rtwdev, ac, &reinvoke);
1970e3ec7017SPing-Ke Shih 
1971e3ec7017SPing-Ke Shih 	if (reinvoke) {
1972e3ec7017SPing-Ke Shih 		/* reinvoke to process the last frame */
1973e3ec7017SPing-Ke Shih 		mod_delayed_work(rtwdev->txq_wq, &rtwdev->txq_reinvoke_work, 1);
1974e3ec7017SPing-Ke Shih 	}
1975e3ec7017SPing-Ke Shih }
1976e3ec7017SPing-Ke Shih 
1977e3ec7017SPing-Ke Shih static void rtw89_core_txq_reinvoke_work(struct work_struct *w)
1978e3ec7017SPing-Ke Shih {
1979e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev,
1980e3ec7017SPing-Ke Shih 						txq_reinvoke_work.work);
1981e3ec7017SPing-Ke Shih 
1982e3ec7017SPing-Ke Shih 	queue_work(rtwdev->txq_wq, &rtwdev->txq_work);
1983e3ec7017SPing-Ke Shih }
1984e3ec7017SPing-Ke Shih 
1985e3ec7017SPing-Ke Shih static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
1986e3ec7017SPing-Ke Shih 						 u32 throughput, u64 cnt)
1987e3ec7017SPing-Ke Shih {
1988e3ec7017SPing-Ke Shih 	if (cnt < 100)
1989e3ec7017SPing-Ke Shih 		return RTW89_TFC_IDLE;
1990e3ec7017SPing-Ke Shih 	if (throughput > 50)
1991e3ec7017SPing-Ke Shih 		return RTW89_TFC_HIGH;
1992e3ec7017SPing-Ke Shih 	if (throughput > 10)
1993e3ec7017SPing-Ke Shih 		return RTW89_TFC_MID;
1994e3ec7017SPing-Ke Shih 	if (throughput > 2)
1995e3ec7017SPing-Ke Shih 		return RTW89_TFC_LOW;
1996e3ec7017SPing-Ke Shih 	return RTW89_TFC_ULTRA_LOW;
1997e3ec7017SPing-Ke Shih }
1998e3ec7017SPing-Ke Shih 
1999e3ec7017SPing-Ke Shih static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
2000e3ec7017SPing-Ke Shih 				     struct rtw89_traffic_stats *stats)
2001e3ec7017SPing-Ke Shih {
2002e3ec7017SPing-Ke Shih 	enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;
2003e3ec7017SPing-Ke Shih 	enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv;
2004e3ec7017SPing-Ke Shih 
2005e3ec7017SPing-Ke Shih 	stats->tx_throughput_raw = (u32)(stats->tx_unicast >> RTW89_TP_SHIFT);
2006e3ec7017SPing-Ke Shih 	stats->rx_throughput_raw = (u32)(stats->rx_unicast >> RTW89_TP_SHIFT);
2007e3ec7017SPing-Ke Shih 
2008e3ec7017SPing-Ke Shih 	ewma_tp_add(&stats->tx_ewma_tp, stats->tx_throughput_raw);
2009e3ec7017SPing-Ke Shih 	ewma_tp_add(&stats->rx_ewma_tp, stats->rx_throughput_raw);
2010e3ec7017SPing-Ke Shih 
2011e3ec7017SPing-Ke Shih 	stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);
2012e3ec7017SPing-Ke Shih 	stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);
2013e3ec7017SPing-Ke Shih 	stats->tx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->tx_throughput,
2014e3ec7017SPing-Ke Shih 						   stats->tx_cnt);
2015e3ec7017SPing-Ke Shih 	stats->rx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->rx_throughput,
2016e3ec7017SPing-Ke Shih 						   stats->rx_cnt);
2017e3ec7017SPing-Ke Shih 	stats->tx_avg_len = stats->tx_cnt ?
2018e3ec7017SPing-Ke Shih 			    DIV_ROUND_DOWN_ULL(stats->tx_unicast, stats->tx_cnt) : 0;
2019e3ec7017SPing-Ke Shih 	stats->rx_avg_len = stats->rx_cnt ?
2020e3ec7017SPing-Ke Shih 			    DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0;
2021e3ec7017SPing-Ke Shih 
2022e3ec7017SPing-Ke Shih 	stats->tx_unicast = 0;
2023e3ec7017SPing-Ke Shih 	stats->rx_unicast = 0;
2024e3ec7017SPing-Ke Shih 	stats->tx_cnt = 0;
2025e3ec7017SPing-Ke Shih 	stats->rx_cnt = 0;
2026e3ec7017SPing-Ke Shih 
2027e3ec7017SPing-Ke Shih 	if (tx_tfc_lv != stats->tx_tfc_lv || rx_tfc_lv != stats->rx_tfc_lv)
2028e3ec7017SPing-Ke Shih 		return true;
2029e3ec7017SPing-Ke Shih 
2030e3ec7017SPing-Ke Shih 	return false;
2031e3ec7017SPing-Ke Shih }
2032e3ec7017SPing-Ke Shih 
2033e3ec7017SPing-Ke Shih static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
2034e3ec7017SPing-Ke Shih {
2035e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif;
2036e3ec7017SPing-Ke Shih 	bool tfc_changed;
2037e3ec7017SPing-Ke Shih 
2038e3ec7017SPing-Ke Shih 	tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats);
2039e3ec7017SPing-Ke Shih 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
2040e3ec7017SPing-Ke Shih 		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats);
2041e3ec7017SPing-Ke Shih 
2042e3ec7017SPing-Ke Shih 	return tfc_changed;
2043e3ec7017SPing-Ke Shih }
2044e3ec7017SPing-Ke Shih 
2045e3ec7017SPing-Ke Shih static void rtw89_vif_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
2046e3ec7017SPing-Ke Shih {
2047e3ec7017SPing-Ke Shih 	if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
2048e3ec7017SPing-Ke Shih 		return;
2049e3ec7017SPing-Ke Shih 
2050e3ec7017SPing-Ke Shih 	if (rtwvif->stats.tx_tfc_lv == RTW89_TFC_IDLE &&
2051e3ec7017SPing-Ke Shih 	    rtwvif->stats.rx_tfc_lv == RTW89_TFC_IDLE)
2052e3ec7017SPing-Ke Shih 		rtw89_enter_lps(rtwdev, rtwvif->mac_id);
2053e3ec7017SPing-Ke Shih }
2054e3ec7017SPing-Ke Shih 
2055e3ec7017SPing-Ke Shih static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
2056e3ec7017SPing-Ke Shih {
2057e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif;
2058e3ec7017SPing-Ke Shih 
2059e3ec7017SPing-Ke Shih 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
2060e3ec7017SPing-Ke Shih 		rtw89_vif_enter_lps(rtwdev, rtwvif);
2061e3ec7017SPing-Ke Shih }
2062e3ec7017SPing-Ke Shih 
2063e3ec7017SPing-Ke Shih void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
2064e3ec7017SPing-Ke Shih 			      struct rtw89_traffic_stats *stats)
2065e3ec7017SPing-Ke Shih {
2066e3ec7017SPing-Ke Shih 	stats->tx_unicast = 0;
2067e3ec7017SPing-Ke Shih 	stats->rx_unicast = 0;
2068e3ec7017SPing-Ke Shih 	stats->tx_cnt = 0;
2069e3ec7017SPing-Ke Shih 	stats->rx_cnt = 0;
2070e3ec7017SPing-Ke Shih 	ewma_tp_init(&stats->tx_ewma_tp);
2071e3ec7017SPing-Ke Shih 	ewma_tp_init(&stats->rx_ewma_tp);
2072e3ec7017SPing-Ke Shih }
2073e3ec7017SPing-Ke Shih 
2074e3ec7017SPing-Ke Shih static void rtw89_track_work(struct work_struct *work)
2075e3ec7017SPing-Ke Shih {
2076e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
2077e3ec7017SPing-Ke Shih 						track_work.work);
2078e3ec7017SPing-Ke Shih 	bool tfc_changed;
2079e3ec7017SPing-Ke Shih 
2080e3ec7017SPing-Ke Shih 	mutex_lock(&rtwdev->mutex);
2081e3ec7017SPing-Ke Shih 
2082e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
2083e3ec7017SPing-Ke Shih 		goto out;
2084e3ec7017SPing-Ke Shih 
2085e3ec7017SPing-Ke Shih 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
2086e3ec7017SPing-Ke Shih 				     RTW89_TRACK_WORK_PERIOD);
2087e3ec7017SPing-Ke Shih 
2088e3ec7017SPing-Ke Shih 	tfc_changed = rtw89_traffic_stats_track(rtwdev);
2089e3ec7017SPing-Ke Shih 	if (rtwdev->scanning)
2090e3ec7017SPing-Ke Shih 		goto out;
2091e3ec7017SPing-Ke Shih 
2092e3ec7017SPing-Ke Shih 	rtw89_leave_lps(rtwdev);
2093e3ec7017SPing-Ke Shih 
2094e3ec7017SPing-Ke Shih 	if (tfc_changed) {
2095e3ec7017SPing-Ke Shih 		rtw89_hci_recalc_int_mit(rtwdev);
2096e3ec7017SPing-Ke Shih 		rtw89_btc_ntfy_wl_sta(rtwdev);
2097e3ec7017SPing-Ke Shih 	}
2098e3ec7017SPing-Ke Shih 	rtw89_mac_bf_monitor_track(rtwdev);
2099e3ec7017SPing-Ke Shih 	rtw89_phy_stat_track(rtwdev);
2100e3ec7017SPing-Ke Shih 	rtw89_phy_env_monitor_track(rtwdev);
2101e3ec7017SPing-Ke Shih 	rtw89_phy_dig(rtwdev);
2102e3ec7017SPing-Ke Shih 	rtw89_chip_rfk_track(rtwdev);
2103e3ec7017SPing-Ke Shih 	rtw89_phy_ra_update(rtwdev);
2104e3ec7017SPing-Ke Shih 	rtw89_phy_cfo_track(rtwdev);
2105e3ec7017SPing-Ke Shih 
2106e3ec7017SPing-Ke Shih 	if (rtwdev->lps_enabled && !rtwdev->btc.lps)
2107e3ec7017SPing-Ke Shih 		rtw89_enter_lps_track(rtwdev);
2108e3ec7017SPing-Ke Shih 
2109e3ec7017SPing-Ke Shih out:
2110e3ec7017SPing-Ke Shih 	mutex_unlock(&rtwdev->mutex);
2111e3ec7017SPing-Ke Shih }
2112e3ec7017SPing-Ke Shih 
2113e3ec7017SPing-Ke Shih u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size)
2114e3ec7017SPing-Ke Shih {
2115e3ec7017SPing-Ke Shih 	unsigned long bit;
2116e3ec7017SPing-Ke Shih 
2117e3ec7017SPing-Ke Shih 	bit = find_first_zero_bit(addr, size);
2118e3ec7017SPing-Ke Shih 	if (bit < size)
2119e3ec7017SPing-Ke Shih 		set_bit(bit, addr);
2120e3ec7017SPing-Ke Shih 
2121e3ec7017SPing-Ke Shih 	return bit;
2122e3ec7017SPing-Ke Shih }
2123e3ec7017SPing-Ke Shih 
2124e3ec7017SPing-Ke Shih void rtw89_core_release_bit_map(unsigned long *addr, u8 bit)
2125e3ec7017SPing-Ke Shih {
2126e3ec7017SPing-Ke Shih 	clear_bit(bit, addr);
2127e3ec7017SPing-Ke Shih }
2128e3ec7017SPing-Ke Shih 
2129e3ec7017SPing-Ke Shih void rtw89_core_release_all_bits_map(unsigned long *addr, unsigned int nbits)
2130e3ec7017SPing-Ke Shih {
2131e3ec7017SPing-Ke Shih 	bitmap_zero(addr, nbits);
2132e3ec7017SPing-Ke Shih }
2133e3ec7017SPing-Ke Shih 
21343ffbb5a8SPing-Ke Shih int rtw89_core_acquire_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx)
21353ffbb5a8SPing-Ke Shih {
21363ffbb5a8SPing-Ke Shih 	struct rtw89_ba_cam_entry *entry;
21373ffbb5a8SPing-Ke Shih 	u8 idx;
21383ffbb5a8SPing-Ke Shih 
21393ffbb5a8SPing-Ke Shih 	idx = rtw89_core_acquire_bit_map(rtwsta->ba_cam_map, RTW89_BA_CAM_NUM);
21403ffbb5a8SPing-Ke Shih 	if (idx == RTW89_BA_CAM_NUM) {
21413ffbb5a8SPing-Ke Shih 		/* allocate a static BA CAM to tid=0, so replace the existing
21423ffbb5a8SPing-Ke Shih 		 * one if BA CAM is full. Hardware will process the original tid
21433ffbb5a8SPing-Ke Shih 		 * automatically.
21443ffbb5a8SPing-Ke Shih 		 */
21453ffbb5a8SPing-Ke Shih 		if (tid != 0)
21463ffbb5a8SPing-Ke Shih 			return -ENOSPC;
21473ffbb5a8SPing-Ke Shih 
21483ffbb5a8SPing-Ke Shih 		idx = 0;
21493ffbb5a8SPing-Ke Shih 	}
21503ffbb5a8SPing-Ke Shih 
21513ffbb5a8SPing-Ke Shih 	entry = &rtwsta->ba_cam_entry[idx];
21523ffbb5a8SPing-Ke Shih 	entry->tid = tid;
21533ffbb5a8SPing-Ke Shih 	*cam_idx = idx;
21543ffbb5a8SPing-Ke Shih 
21553ffbb5a8SPing-Ke Shih 	return 0;
21563ffbb5a8SPing-Ke Shih }
21573ffbb5a8SPing-Ke Shih 
21583ffbb5a8SPing-Ke Shih int rtw89_core_release_sta_ba_entry(struct rtw89_sta *rtwsta, u8 tid, u8 *cam_idx)
21593ffbb5a8SPing-Ke Shih {
21603ffbb5a8SPing-Ke Shih 	struct rtw89_ba_cam_entry *entry;
21613ffbb5a8SPing-Ke Shih 	int i;
21623ffbb5a8SPing-Ke Shih 
21633ffbb5a8SPing-Ke Shih 	for (i = 0; i < RTW89_BA_CAM_NUM; i++) {
21643ffbb5a8SPing-Ke Shih 		if (!test_bit(i, rtwsta->ba_cam_map))
21653ffbb5a8SPing-Ke Shih 			continue;
21663ffbb5a8SPing-Ke Shih 
21673ffbb5a8SPing-Ke Shih 		entry = &rtwsta->ba_cam_entry[i];
21683ffbb5a8SPing-Ke Shih 		if (entry->tid != tid)
21693ffbb5a8SPing-Ke Shih 			continue;
21703ffbb5a8SPing-Ke Shih 
21713ffbb5a8SPing-Ke Shih 		rtw89_core_release_bit_map(rtwsta->ba_cam_map, i);
21723ffbb5a8SPing-Ke Shih 		*cam_idx = i;
21733ffbb5a8SPing-Ke Shih 		return 0;
21743ffbb5a8SPing-Ke Shih 	}
21753ffbb5a8SPing-Ke Shih 
21763ffbb5a8SPing-Ke Shih 	return -ENOENT;
21773ffbb5a8SPing-Ke Shih }
21783ffbb5a8SPing-Ke Shih 
2179e3ec7017SPing-Ke Shih #define RTW89_TYPE_MAPPING(_type)	\
2180e3ec7017SPing-Ke Shih 	case NL80211_IFTYPE_ ## _type:	\
2181e3ec7017SPing-Ke Shih 		rtwvif->wifi_role = RTW89_WIFI_ROLE_ ## _type;	\
2182e3ec7017SPing-Ke Shih 		break
2183e3ec7017SPing-Ke Shih void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc)
2184e3ec7017SPing-Ke Shih {
2185e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
2186e3ec7017SPing-Ke Shih 
2187e3ec7017SPing-Ke Shih 	switch (vif->type) {
2188e3ec7017SPing-Ke Shih 	RTW89_TYPE_MAPPING(ADHOC);
2189e3ec7017SPing-Ke Shih 	RTW89_TYPE_MAPPING(STATION);
2190e3ec7017SPing-Ke Shih 	RTW89_TYPE_MAPPING(AP);
2191e3ec7017SPing-Ke Shih 	RTW89_TYPE_MAPPING(MONITOR);
2192e3ec7017SPing-Ke Shih 	RTW89_TYPE_MAPPING(MESH_POINT);
2193e3ec7017SPing-Ke Shih 	default:
2194e3ec7017SPing-Ke Shih 		WARN_ON(1);
2195e3ec7017SPing-Ke Shih 		break;
2196e3ec7017SPing-Ke Shih 	}
2197e3ec7017SPing-Ke Shih 
2198e3ec7017SPing-Ke Shih 	switch (vif->type) {
2199e3ec7017SPing-Ke Shih 	case NL80211_IFTYPE_AP:
2200e3ec7017SPing-Ke Shih 	case NL80211_IFTYPE_MESH_POINT:
2201e3ec7017SPing-Ke Shih 		rtwvif->net_type = RTW89_NET_TYPE_AP_MODE;
2202e3ec7017SPing-Ke Shih 		rtwvif->self_role = RTW89_SELF_ROLE_AP;
2203e3ec7017SPing-Ke Shih 		break;
2204e3ec7017SPing-Ke Shih 	case NL80211_IFTYPE_ADHOC:
2205e3ec7017SPing-Ke Shih 		rtwvif->net_type = RTW89_NET_TYPE_AD_HOC;
2206e3ec7017SPing-Ke Shih 		rtwvif->self_role = RTW89_SELF_ROLE_CLIENT;
2207e3ec7017SPing-Ke Shih 		break;
2208e3ec7017SPing-Ke Shih 	case NL80211_IFTYPE_STATION:
2209e3ec7017SPing-Ke Shih 		if (assoc) {
2210e3ec7017SPing-Ke Shih 			rtwvif->net_type = RTW89_NET_TYPE_INFRA;
2211e3ec7017SPing-Ke Shih 			rtwvif->trigger = vif->bss_conf.he_support;
2212e3ec7017SPing-Ke Shih 		} else {
2213e3ec7017SPing-Ke Shih 			rtwvif->net_type = RTW89_NET_TYPE_NO_LINK;
2214e3ec7017SPing-Ke Shih 			rtwvif->trigger = false;
2215e3ec7017SPing-Ke Shih 		}
2216e3ec7017SPing-Ke Shih 		rtwvif->self_role = RTW89_SELF_ROLE_CLIENT;
2217e3ec7017SPing-Ke Shih 		rtwvif->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL;
2218e3ec7017SPing-Ke Shih 		break;
2219e3ec7017SPing-Ke Shih 	default:
2220e3ec7017SPing-Ke Shih 		WARN_ON(1);
2221e3ec7017SPing-Ke Shih 		break;
2222e3ec7017SPing-Ke Shih 	}
2223e3ec7017SPing-Ke Shih }
2224e3ec7017SPing-Ke Shih 
2225e3ec7017SPing-Ke Shih int rtw89_core_sta_add(struct rtw89_dev *rtwdev,
2226e3ec7017SPing-Ke Shih 		       struct ieee80211_vif *vif,
2227e3ec7017SPing-Ke Shih 		       struct ieee80211_sta *sta)
2228e3ec7017SPing-Ke Shih {
2229e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
2230e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
2231e3ec7017SPing-Ke Shih 	int i;
2232e3ec7017SPing-Ke Shih 
2233e3ec7017SPing-Ke Shih 	rtwsta->rtwvif = rtwvif;
2234e3ec7017SPing-Ke Shih 	rtwsta->prev_rssi = 0;
2235e3ec7017SPing-Ke Shih 
2236e3ec7017SPing-Ke Shih 	for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
2237e3ec7017SPing-Ke Shih 		rtw89_core_txq_init(rtwdev, sta->txq[i]);
2238e3ec7017SPing-Ke Shih 
2239e3ec7017SPing-Ke Shih 	ewma_rssi_init(&rtwsta->avg_rssi);
2240e3ec7017SPing-Ke Shih 
2241e3ec7017SPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_STATION) {
224240822e07SPing-Ke Shih 		/* for station mode, assign the mac_id from itself */
224340822e07SPing-Ke Shih 		rtwsta->mac_id = rtwvif->mac_id;
2244e3ec7017SPing-Ke Shih 		rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
2245e3ec7017SPing-Ke Shih 					 BTC_ROLE_MSTS_STA_CONN_START);
2246e3ec7017SPing-Ke Shih 		rtw89_chip_rfk_channel(rtwdev);
2247c7df64c1SPing-Ke Shih 	} else if (vif->type == NL80211_IFTYPE_AP) {
2248c7df64c1SPing-Ke Shih 		rtwsta->mac_id = rtw89_core_acquire_bit_map(rtwdev->mac_id_map,
2249c7df64c1SPing-Ke Shih 							    RTW89_MAX_MAC_ID_NUM);
2250e3ec7017SPing-Ke Shih 	}
2251e3ec7017SPing-Ke Shih 
2252e3ec7017SPing-Ke Shih 	return 0;
2253e3ec7017SPing-Ke Shih }
2254e3ec7017SPing-Ke Shih 
2255e3ec7017SPing-Ke Shih int rtw89_core_sta_disassoc(struct rtw89_dev *rtwdev,
2256e3ec7017SPing-Ke Shih 			    struct ieee80211_vif *vif,
2257e3ec7017SPing-Ke Shih 			    struct ieee80211_sta *sta)
2258e3ec7017SPing-Ke Shih {
2259e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
2260e3ec7017SPing-Ke Shih 
2261e3ec7017SPing-Ke Shih 	rtwdev->total_sta_assoc--;
2262e3ec7017SPing-Ke Shih 	rtwsta->disassoc = true;
2263e3ec7017SPing-Ke Shih 
2264e3ec7017SPing-Ke Shih 	return 0;
2265e3ec7017SPing-Ke Shih }
2266e3ec7017SPing-Ke Shih 
2267e3ec7017SPing-Ke Shih int rtw89_core_sta_disconnect(struct rtw89_dev *rtwdev,
2268e3ec7017SPing-Ke Shih 			      struct ieee80211_vif *vif,
2269e3ec7017SPing-Ke Shih 			      struct ieee80211_sta *sta)
2270e3ec7017SPing-Ke Shih {
2271e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
227240822e07SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
2273e3ec7017SPing-Ke Shih 	int ret;
2274e3ec7017SPing-Ke Shih 
2275e3ec7017SPing-Ke Shih 	rtw89_mac_bf_monitor_calc(rtwdev, sta, true);
2276e3ec7017SPing-Ke Shih 	rtw89_mac_bf_disassoc(rtwdev, vif, sta);
2277e3ec7017SPing-Ke Shih 	rtw89_core_free_sta_pending_ba(rtwdev, sta);
22781b73e77dSPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_AP)
22791b73e77dSPing-Ke Shih 		rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam);
2280e3ec7017SPing-Ke Shih 
2281fd7ee4c8SPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_STATION)
2282e3ec7017SPing-Ke Shih 		rtw89_vif_type_mapping(vif, false);
2283e3ec7017SPing-Ke Shih 
2284e3ec7017SPing-Ke Shih 	ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta);
2285e3ec7017SPing-Ke Shih 	if (ret) {
2286e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c cmac table\n");
2287e3ec7017SPing-Ke Shih 		return ret;
2288e3ec7017SPing-Ke Shih 	}
2289e3ec7017SPing-Ke Shih 
2290742c470bSPing-Ke Shih 	ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, true);
2291e3ec7017SPing-Ke Shih 	if (ret) {
2292e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c join info\n");
2293e3ec7017SPing-Ke Shih 		return ret;
2294e3ec7017SPing-Ke Shih 	}
2295e3ec7017SPing-Ke Shih 
22961b73e77dSPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_AP) {
22971b73e77dSPing-Ke Shih 		ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta, RTW89_ROLE_REMOVE);
22981b73e77dSPing-Ke Shih 		if (ret) {
22991b73e77dSPing-Ke Shih 			rtw89_warn(rtwdev, "failed to send h2c role info\n");
23001b73e77dSPing-Ke Shih 			return ret;
23011b73e77dSPing-Ke Shih 		}
23021b73e77dSPing-Ke Shih 	}
23031b73e77dSPing-Ke Shih 
2304e3ec7017SPing-Ke Shih 	/* update cam aid mac_id net_type */
23051b73e77dSPing-Ke Shih 	ret = rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL);
2306e3ec7017SPing-Ke Shih 	if (ret) {
2307e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c cam\n");
2308e3ec7017SPing-Ke Shih 		return ret;
2309e3ec7017SPing-Ke Shih 	}
2310e3ec7017SPing-Ke Shih 
2311e3ec7017SPing-Ke Shih 	return ret;
2312e3ec7017SPing-Ke Shih }
2313e3ec7017SPing-Ke Shih 
2314e3ec7017SPing-Ke Shih int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev,
2315e3ec7017SPing-Ke Shih 			 struct ieee80211_vif *vif,
2316e3ec7017SPing-Ke Shih 			 struct ieee80211_sta *sta)
2317e3ec7017SPing-Ke Shih {
2318e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
2319e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
2320e3ec7017SPing-Ke Shih 	int ret;
2321e3ec7017SPing-Ke Shih 
23221b73e77dSPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_AP) {
23231b73e77dSPing-Ke Shih 		ret = rtw89_mac_set_macid_pause(rtwdev, rtwsta->mac_id, false);
23241b73e77dSPing-Ke Shih 		if (ret) {
23251b73e77dSPing-Ke Shih 			rtw89_warn(rtwdev, "failed to send h2c macid pause\n");
23261b73e77dSPing-Ke Shih 			return ret;
23271b73e77dSPing-Ke Shih 		}
23281b73e77dSPing-Ke Shih 
23291b73e77dSPing-Ke Shih 		ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif, rtwsta, RTW89_ROLE_CREATE);
23301b73e77dSPing-Ke Shih 		if (ret) {
23311b73e77dSPing-Ke Shih 			rtw89_warn(rtwdev, "failed to send h2c role info\n");
23321b73e77dSPing-Ke Shih 			return ret;
23331b73e77dSPing-Ke Shih 		}
23341b73e77dSPing-Ke Shih 
23351b73e77dSPing-Ke Shih 		ret = rtw89_cam_init_addr_cam(rtwdev, &rtwsta->addr_cam, &rtwvif->bssid_cam);
23361b73e77dSPing-Ke Shih 		if (ret) {
23371b73e77dSPing-Ke Shih 			rtw89_warn(rtwdev, "failed to send h2c init addr cam\n");
23381b73e77dSPing-Ke Shih 			return ret;
23391b73e77dSPing-Ke Shih 		}
23401b73e77dSPing-Ke Shih 	}
2341e3ec7017SPing-Ke Shih 
2342e3ec7017SPing-Ke Shih 	ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta);
2343e3ec7017SPing-Ke Shih 	if (ret) {
2344e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c cmac table\n");
2345e3ec7017SPing-Ke Shih 		return ret;
2346e3ec7017SPing-Ke Shih 	}
2347e3ec7017SPing-Ke Shih 
2348742c470bSPing-Ke Shih 	ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, rtwsta, false);
2349e3ec7017SPing-Ke Shih 	if (ret) {
2350e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c join info\n");
2351e3ec7017SPing-Ke Shih 		return ret;
2352e3ec7017SPing-Ke Shih 	}
2353e3ec7017SPing-Ke Shih 
2354e3ec7017SPing-Ke Shih 	/* update cam aid mac_id net_type */
235540822e07SPing-Ke Shih 	rtw89_fw_h2c_cam(rtwdev, rtwvif, rtwsta, NULL);
2356e3ec7017SPing-Ke Shih 	if (ret) {
2357e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c cam\n");
2358e3ec7017SPing-Ke Shih 		return ret;
2359e3ec7017SPing-Ke Shih 	}
2360e3ec7017SPing-Ke Shih 
2361e3ec7017SPing-Ke Shih 	ret = rtw89_fw_h2c_general_pkt(rtwdev, rtwsta->mac_id);
2362e3ec7017SPing-Ke Shih 	if (ret) {
2363e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "failed to send h2c general packet\n");
2364e3ec7017SPing-Ke Shih 		return ret;
2365e3ec7017SPing-Ke Shih 	}
2366e3ec7017SPing-Ke Shih 
2367e3ec7017SPing-Ke Shih 	rtwdev->total_sta_assoc++;
2368e3ec7017SPing-Ke Shih 	rtw89_phy_ra_assoc(rtwdev, sta);
2369e3ec7017SPing-Ke Shih 	rtw89_mac_bf_assoc(rtwdev, vif, sta);
2370e3ec7017SPing-Ke Shih 	rtw89_mac_bf_monitor_calc(rtwdev, sta, false);
2371e3ec7017SPing-Ke Shih 
2372e3ec7017SPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_STATION) {
2373e3ec7017SPing-Ke Shih 		rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
2374e3ec7017SPing-Ke Shih 					 BTC_ROLE_MSTS_STA_CONN_END);
2375e3ec7017SPing-Ke Shih 		rtw89_core_get_no_ul_ofdma_htc(rtwdev, &rtwsta->htc_template);
2376e3ec7017SPing-Ke Shih 	}
2377e3ec7017SPing-Ke Shih 
2378e3ec7017SPing-Ke Shih 	return ret;
2379e3ec7017SPing-Ke Shih }
2380e3ec7017SPing-Ke Shih 
2381e3ec7017SPing-Ke Shih int rtw89_core_sta_remove(struct rtw89_dev *rtwdev,
2382e3ec7017SPing-Ke Shih 			  struct ieee80211_vif *vif,
2383e3ec7017SPing-Ke Shih 			  struct ieee80211_sta *sta)
2384e3ec7017SPing-Ke Shih {
2385e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
2386e3ec7017SPing-Ke Shih 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
2387e3ec7017SPing-Ke Shih 
2388e3ec7017SPing-Ke Shih 	if (vif->type == NL80211_IFTYPE_STATION)
2389e3ec7017SPing-Ke Shih 		rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
2390e3ec7017SPing-Ke Shih 					 BTC_ROLE_MSTS_STA_DIS_CONN);
2391c7df64c1SPing-Ke Shih 	else if (vif->type == NL80211_IFTYPE_AP)
2392c7df64c1SPing-Ke Shih 		rtw89_core_release_bit_map(rtwdev->mac_id_map, rtwsta->mac_id);
2393e3ec7017SPing-Ke Shih 
2394e3ec7017SPing-Ke Shih 	return 0;
2395e3ec7017SPing-Ke Shih }
2396e3ec7017SPing-Ke Shih 
2397e3ec7017SPing-Ke Shih static void rtw89_init_ht_cap(struct rtw89_dev *rtwdev,
2398e3ec7017SPing-Ke Shih 			      struct ieee80211_sta_ht_cap *ht_cap)
2399e3ec7017SPing-Ke Shih {
2400e3ec7017SPing-Ke Shih 	static const __le16 highest[RF_PATH_MAX] = {
2401e3ec7017SPing-Ke Shih 		cpu_to_le16(150), cpu_to_le16(300), cpu_to_le16(450), cpu_to_le16(600),
2402e3ec7017SPing-Ke Shih 	};
2403e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
2404e3ec7017SPing-Ke Shih 	u8 nss = hal->rx_nss;
2405e3ec7017SPing-Ke Shih 	int i;
2406e3ec7017SPing-Ke Shih 
2407e3ec7017SPing-Ke Shih 	ht_cap->ht_supported = true;
2408e3ec7017SPing-Ke Shih 	ht_cap->cap = 0;
2409e3ec7017SPing-Ke Shih 	ht_cap->cap |= IEEE80211_HT_CAP_SGI_20 |
2410e3ec7017SPing-Ke Shih 		       IEEE80211_HT_CAP_MAX_AMSDU |
2411e3ec7017SPing-Ke Shih 		       IEEE80211_HT_CAP_TX_STBC |
2412e3ec7017SPing-Ke Shih 		       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
2413e3ec7017SPing-Ke Shih 	ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
2414e3ec7017SPing-Ke Shih 	ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
2415e3ec7017SPing-Ke Shih 		       IEEE80211_HT_CAP_DSSSCCK40 |
2416e3ec7017SPing-Ke Shih 		       IEEE80211_HT_CAP_SGI_40;
2417e3ec7017SPing-Ke Shih 	ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
2418e3ec7017SPing-Ke Shih 	ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
2419e3ec7017SPing-Ke Shih 	ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
2420e3ec7017SPing-Ke Shih 	for (i = 0; i < nss; i++)
2421e3ec7017SPing-Ke Shih 		ht_cap->mcs.rx_mask[i] = 0xFF;
2422e3ec7017SPing-Ke Shih 	ht_cap->mcs.rx_mask[4] = 0x01;
2423e3ec7017SPing-Ke Shih 	ht_cap->mcs.rx_highest = highest[nss - 1];
2424e3ec7017SPing-Ke Shih }
2425e3ec7017SPing-Ke Shih 
2426e3ec7017SPing-Ke Shih static void rtw89_init_vht_cap(struct rtw89_dev *rtwdev,
2427e3ec7017SPing-Ke Shih 			       struct ieee80211_sta_vht_cap *vht_cap)
2428e3ec7017SPing-Ke Shih {
2429d221270aSPing-Ke Shih 	static const __le16 highest_bw80[RF_PATH_MAX] = {
2430e3ec7017SPing-Ke Shih 		cpu_to_le16(433), cpu_to_le16(867), cpu_to_le16(1300), cpu_to_le16(1733),
2431e3ec7017SPing-Ke Shih 	};
2432d221270aSPing-Ke Shih 	static const __le16 highest_bw160[RF_PATH_MAX] = {
2433d221270aSPing-Ke Shih 		cpu_to_le16(867), cpu_to_le16(1733), cpu_to_le16(2600), cpu_to_le16(3467),
2434d221270aSPing-Ke Shih 	};
2435d221270aSPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
2436d221270aSPing-Ke Shih 	const __le16 *highest = chip->support_bw160 ? highest_bw160 : highest_bw80;
2437e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
2438e3ec7017SPing-Ke Shih 	u16 tx_mcs_map = 0, rx_mcs_map = 0;
2439e3ec7017SPing-Ke Shih 	u8 sts_cap = 3;
2440e3ec7017SPing-Ke Shih 	int i;
2441e3ec7017SPing-Ke Shih 
2442e3ec7017SPing-Ke Shih 	for (i = 0; i < 8; i++) {
2443e3ec7017SPing-Ke Shih 		if (i < hal->tx_nss)
2444e3ec7017SPing-Ke Shih 			tx_mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
2445e3ec7017SPing-Ke Shih 		else
2446e3ec7017SPing-Ke Shih 			tx_mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
2447e3ec7017SPing-Ke Shih 		if (i < hal->rx_nss)
2448e3ec7017SPing-Ke Shih 			rx_mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
2449e3ec7017SPing-Ke Shih 		else
2450e3ec7017SPing-Ke Shih 			rx_mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
2451e3ec7017SPing-Ke Shih 	}
2452e3ec7017SPing-Ke Shih 
2453e3ec7017SPing-Ke Shih 	vht_cap->vht_supported = true;
2454e3ec7017SPing-Ke Shih 	vht_cap->cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
2455e3ec7017SPing-Ke Shih 		       IEEE80211_VHT_CAP_SHORT_GI_80 |
2456e3ec7017SPing-Ke Shih 		       IEEE80211_VHT_CAP_RXSTBC_1 |
2457e3ec7017SPing-Ke Shih 		       IEEE80211_VHT_CAP_HTC_VHT |
2458e3ec7017SPing-Ke Shih 		       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
2459e3ec7017SPing-Ke Shih 		       0;
2460e3ec7017SPing-Ke Shih 	vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
2461e3ec7017SPing-Ke Shih 	vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
2462e3ec7017SPing-Ke Shih 	vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
2463e3ec7017SPing-Ke Shih 			IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
2464e3ec7017SPing-Ke Shih 	vht_cap->cap |= sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
2465d221270aSPing-Ke Shih 	if (chip->support_bw160)
2466d221270aSPing-Ke Shih 		vht_cap->cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
2467d221270aSPing-Ke Shih 				IEEE80211_VHT_CAP_SHORT_GI_160;
2468e3ec7017SPing-Ke Shih 	vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rx_mcs_map);
2469e3ec7017SPing-Ke Shih 	vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(tx_mcs_map);
2470e3ec7017SPing-Ke Shih 	vht_cap->vht_mcs.rx_highest = highest[hal->rx_nss - 1];
2471e3ec7017SPing-Ke Shih 	vht_cap->vht_mcs.tx_highest = highest[hal->tx_nss - 1];
2472e3ec7017SPing-Ke Shih }
2473e3ec7017SPing-Ke Shih 
2474e3ec7017SPing-Ke Shih #define RTW89_SBAND_IFTYPES_NR 2
2475e3ec7017SPing-Ke Shih 
2476e3ec7017SPing-Ke Shih static void rtw89_init_he_cap(struct rtw89_dev *rtwdev,
2477e3ec7017SPing-Ke Shih 			      enum nl80211_band band,
2478e3ec7017SPing-Ke Shih 			      struct ieee80211_supported_band *sband)
2479e3ec7017SPing-Ke Shih {
2480e3ec7017SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
2481e3ec7017SPing-Ke Shih 	struct rtw89_hal *hal = &rtwdev->hal;
2482e3ec7017SPing-Ke Shih 	struct ieee80211_sband_iftype_data *iftype_data;
2483e3ec7017SPing-Ke Shih 	bool no_ng16 = (chip->chip_id == RTL8852A && hal->cv == CHIP_CBV) ||
2484e3ec7017SPing-Ke Shih 		       (chip->chip_id == RTL8852B && hal->cv == CHIP_CAV);
2485e3ec7017SPing-Ke Shih 	u16 mcs_map = 0;
2486e3ec7017SPing-Ke Shih 	int i;
2487e3ec7017SPing-Ke Shih 	int nss = hal->rx_nss;
2488e3ec7017SPing-Ke Shih 	int idx = 0;
2489e3ec7017SPing-Ke Shih 
2490e3ec7017SPing-Ke Shih 	iftype_data = kcalloc(RTW89_SBAND_IFTYPES_NR, sizeof(*iftype_data), GFP_KERNEL);
2491e3ec7017SPing-Ke Shih 	if (!iftype_data)
2492e3ec7017SPing-Ke Shih 		return;
2493e3ec7017SPing-Ke Shih 
2494e3ec7017SPing-Ke Shih 	for (i = 0; i < 8; i++) {
2495e3ec7017SPing-Ke Shih 		if (i < nss)
2496e3ec7017SPing-Ke Shih 			mcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
2497e3ec7017SPing-Ke Shih 		else
2498e3ec7017SPing-Ke Shih 			mcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
2499e3ec7017SPing-Ke Shih 	}
2500e3ec7017SPing-Ke Shih 
2501e3ec7017SPing-Ke Shih 	for (i = 0; i < NUM_NL80211_IFTYPES; i++) {
2502e3ec7017SPing-Ke Shih 		struct ieee80211_sta_he_cap *he_cap;
2503e3ec7017SPing-Ke Shih 		u8 *mac_cap_info;
2504e3ec7017SPing-Ke Shih 		u8 *phy_cap_info;
2505e3ec7017SPing-Ke Shih 
2506e3ec7017SPing-Ke Shih 		switch (i) {
2507e3ec7017SPing-Ke Shih 		case NL80211_IFTYPE_STATION:
2508e3ec7017SPing-Ke Shih 		case NL80211_IFTYPE_AP:
2509e3ec7017SPing-Ke Shih 			break;
2510e3ec7017SPing-Ke Shih 		default:
2511e3ec7017SPing-Ke Shih 			continue;
2512e3ec7017SPing-Ke Shih 		}
2513e3ec7017SPing-Ke Shih 
2514e3ec7017SPing-Ke Shih 		if (idx >= RTW89_SBAND_IFTYPES_NR) {
2515e3ec7017SPing-Ke Shih 			rtw89_warn(rtwdev, "run out of iftype_data\n");
2516e3ec7017SPing-Ke Shih 			break;
2517e3ec7017SPing-Ke Shih 		}
2518e3ec7017SPing-Ke Shih 
2519e3ec7017SPing-Ke Shih 		iftype_data[idx].types_mask = BIT(i);
2520e3ec7017SPing-Ke Shih 		he_cap = &iftype_data[idx].he_cap;
2521e3ec7017SPing-Ke Shih 		mac_cap_info = he_cap->he_cap_elem.mac_cap_info;
2522e3ec7017SPing-Ke Shih 		phy_cap_info = he_cap->he_cap_elem.phy_cap_info;
2523e3ec7017SPing-Ke Shih 
2524e3ec7017SPing-Ke Shih 		he_cap->has_he = true;
2525e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_AP)
2526e3ec7017SPing-Ke Shih 			mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE;
2527e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_STATION)
2528e3ec7017SPing-Ke Shih 			mac_cap_info[1] = IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
2529e3ec7017SPing-Ke Shih 		mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_ALL_ACK |
2530e3ec7017SPing-Ke Shih 				  IEEE80211_HE_MAC_CAP2_BSR;
2531e3ec7017SPing-Ke Shih 		mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2;
2532e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_AP)
2533e3ec7017SPing-Ke Shih 			mac_cap_info[3] |= IEEE80211_HE_MAC_CAP3_OMI_CONTROL;
2534e3ec7017SPing-Ke Shih 		mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_OPS |
2535e3ec7017SPing-Ke Shih 				  IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
2536e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_STATION)
2537e3ec7017SPing-Ke Shih 			mac_cap_info[5] = IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX;
2538517eed92SJohannes Berg 		if (band == NL80211_BAND_2GHZ) {
2539517eed92SJohannes Berg 			phy_cap_info[0] =
2540517eed92SJohannes Berg 				IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
2541517eed92SJohannes Berg 		} else {
2542517eed92SJohannes Berg 			phy_cap_info[0] =
2543e3ec7017SPing-Ke Shih 				IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
2544d221270aSPing-Ke Shih 			if (chip->support_bw160)
2545d221270aSPing-Ke Shih 				phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
2546517eed92SJohannes Berg 		}
2547e3ec7017SPing-Ke Shih 		phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
2548e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
2549e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
2550e3ec7017SPing-Ke Shih 		phy_cap_info[2] = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
2551e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
2552e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
2553e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP2_DOPPLER_TX;
2554e3ec7017SPing-Ke Shih 		phy_cap_info[3] = IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM;
2555e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_STATION)
2556e3ec7017SPing-Ke Shih 			phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM |
2557e3ec7017SPing-Ke Shih 					   IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2;
2558e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_AP)
2559e3ec7017SPing-Ke Shih 			phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
2560e3ec7017SPing-Ke Shih 		phy_cap_info[4] = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
2561e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
2562e3ec7017SPing-Ke Shih 		phy_cap_info[5] = no_ng16 ? 0 :
2563e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
2564e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
2565e3ec7017SPing-Ke Shih 		phy_cap_info[6] = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
2566e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
2567e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
2568e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE;
2569e3ec7017SPing-Ke Shih 		phy_cap_info[7] = IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
2570e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
2571e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP7_MAX_NC_1;
2572e3ec7017SPing-Ke Shih 		phy_cap_info[8] = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
2573e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI |
2574e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996;
2575d221270aSPing-Ke Shih 		if (chip->support_bw160)
2576d221270aSPing-Ke Shih 			phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
2577d221270aSPing-Ke Shih 					   IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU;
2578e3ec7017SPing-Ke Shih 		phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
2579e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU |
2580e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
2581e3ec7017SPing-Ke Shih 				  IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
258275c5bd68SMiri Korenblit 				  u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
258375c5bd68SMiri Korenblit 						 IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
2584e3ec7017SPing-Ke Shih 		if (i == NL80211_IFTYPE_STATION)
2585e3ec7017SPing-Ke Shih 			phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU;
2586e3ec7017SPing-Ke Shih 		he_cap->he_mcs_nss_supp.rx_mcs_80 = cpu_to_le16(mcs_map);
2587e3ec7017SPing-Ke Shih 		he_cap->he_mcs_nss_supp.tx_mcs_80 = cpu_to_le16(mcs_map);
2588d221270aSPing-Ke Shih 		if (chip->support_bw160) {
2589d221270aSPing-Ke Shih 			he_cap->he_mcs_nss_supp.rx_mcs_160 = cpu_to_le16(mcs_map);
2590d221270aSPing-Ke Shih 			he_cap->he_mcs_nss_supp.tx_mcs_160 = cpu_to_le16(mcs_map);
2591d221270aSPing-Ke Shih 		}
2592e3ec7017SPing-Ke Shih 
259312b604d4SPing-Ke Shih 		if (band == NL80211_BAND_6GHZ) {
259412b604d4SPing-Ke Shih 			__le16 capa;
259512b604d4SPing-Ke Shih 
259612b604d4SPing-Ke Shih 			capa = le16_encode_bits(IEEE80211_HT_MPDU_DENSITY_NONE,
259712b604d4SPing-Ke Shih 						IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) |
259812b604d4SPing-Ke Shih 			       le16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K,
259912b604d4SPing-Ke Shih 						IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) |
260012b604d4SPing-Ke Shih 			       le16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454,
260112b604d4SPing-Ke Shih 						IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN);
260212b604d4SPing-Ke Shih 			iftype_data[idx].he_6ghz_capa.capa = capa;
260312b604d4SPing-Ke Shih 		}
260412b604d4SPing-Ke Shih 
2605e3ec7017SPing-Ke Shih 		idx++;
2606e3ec7017SPing-Ke Shih 	}
2607e3ec7017SPing-Ke Shih 
2608e3ec7017SPing-Ke Shih 	sband->iftype_data = iftype_data;
2609e3ec7017SPing-Ke Shih 	sband->n_iftype_data = idx;
2610e3ec7017SPing-Ke Shih }
2611e3ec7017SPing-Ke Shih 
2612e3ec7017SPing-Ke Shih static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev)
2613e3ec7017SPing-Ke Shih {
2614e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
2615e3ec7017SPing-Ke Shih 	struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL;
26160237f65aSZong-Zhe Yang 	struct ieee80211_supported_band *sband_6ghz = NULL;
2617e3ec7017SPing-Ke Shih 	u32 size = sizeof(struct ieee80211_supported_band);
26180237f65aSZong-Zhe Yang 	u8 support_bands = rtwdev->chip->support_bands;
2619e3ec7017SPing-Ke Shih 
26200237f65aSZong-Zhe Yang 	if (support_bands & BIT(NL80211_BAND_2GHZ)) {
2621e3ec7017SPing-Ke Shih 		sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL);
2622e3ec7017SPing-Ke Shih 		if (!sband_2ghz)
2623e3ec7017SPing-Ke Shih 			goto err;
2624e3ec7017SPing-Ke Shih 		rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap);
2625e3ec7017SPing-Ke Shih 		rtw89_init_he_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz);
2626e3ec7017SPing-Ke Shih 		hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz;
26270237f65aSZong-Zhe Yang 	}
2628e3ec7017SPing-Ke Shih 
26290237f65aSZong-Zhe Yang 	if (support_bands & BIT(NL80211_BAND_5GHZ)) {
2630e3ec7017SPing-Ke Shih 		sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL);
2631e3ec7017SPing-Ke Shih 		if (!sband_5ghz)
2632e3ec7017SPing-Ke Shih 			goto err;
2633e3ec7017SPing-Ke Shih 		rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap);
2634e3ec7017SPing-Ke Shih 		rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap);
2635e3ec7017SPing-Ke Shih 		rtw89_init_he_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz);
2636e3ec7017SPing-Ke Shih 		hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz;
26370237f65aSZong-Zhe Yang 	}
26380237f65aSZong-Zhe Yang 
26390237f65aSZong-Zhe Yang 	if (support_bands & BIT(NL80211_BAND_6GHZ)) {
26400237f65aSZong-Zhe Yang 		sband_6ghz = kmemdup(&rtw89_sband_6ghz, size, GFP_KERNEL);
26410237f65aSZong-Zhe Yang 		if (!sband_6ghz)
26420237f65aSZong-Zhe Yang 			goto err;
26430237f65aSZong-Zhe Yang 		rtw89_init_he_cap(rtwdev, NL80211_BAND_6GHZ, sband_6ghz);
26440237f65aSZong-Zhe Yang 		hw->wiphy->bands[NL80211_BAND_6GHZ] = sband_6ghz;
26450237f65aSZong-Zhe Yang 	}
2646e3ec7017SPing-Ke Shih 
2647e3ec7017SPing-Ke Shih 	return 0;
2648e3ec7017SPing-Ke Shih 
2649e3ec7017SPing-Ke Shih err:
2650e3ec7017SPing-Ke Shih 	hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
2651e3ec7017SPing-Ke Shih 	hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
26520237f65aSZong-Zhe Yang 	hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
2653e3ec7017SPing-Ke Shih 	if (sband_2ghz)
2654e3ec7017SPing-Ke Shih 		kfree(sband_2ghz->iftype_data);
2655e3ec7017SPing-Ke Shih 	if (sband_5ghz)
2656e3ec7017SPing-Ke Shih 		kfree(sband_5ghz->iftype_data);
26570237f65aSZong-Zhe Yang 	if (sband_6ghz)
26580237f65aSZong-Zhe Yang 		kfree(sband_6ghz->iftype_data);
2659e3ec7017SPing-Ke Shih 	kfree(sband_2ghz);
2660e3ec7017SPing-Ke Shih 	kfree(sband_5ghz);
26610237f65aSZong-Zhe Yang 	kfree(sband_6ghz);
2662e3ec7017SPing-Ke Shih 	return -ENOMEM;
2663e3ec7017SPing-Ke Shih }
2664e3ec7017SPing-Ke Shih 
2665e3ec7017SPing-Ke Shih static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev)
2666e3ec7017SPing-Ke Shih {
2667e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
2668e3ec7017SPing-Ke Shih 
2669e3ec7017SPing-Ke Shih 	kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data);
2670e3ec7017SPing-Ke Shih 	kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data);
26710237f65aSZong-Zhe Yang 	if (hw->wiphy->bands[NL80211_BAND_6GHZ])
26720237f65aSZong-Zhe Yang 		kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data);
2673e3ec7017SPing-Ke Shih 	kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]);
2674e3ec7017SPing-Ke Shih 	kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);
26750237f65aSZong-Zhe Yang 	kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]);
2676e3ec7017SPing-Ke Shih 	hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
2677e3ec7017SPing-Ke Shih 	hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
26780237f65aSZong-Zhe Yang 	hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;
2679e3ec7017SPing-Ke Shih }
2680e3ec7017SPing-Ke Shih 
2681e3ec7017SPing-Ke Shih static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev)
2682e3ec7017SPing-Ke Shih {
2683e3ec7017SPing-Ke Shih 	int i;
2684e3ec7017SPing-Ke Shih 
2685e3ec7017SPing-Ke Shih 	for (i = 0; i < RTW89_PHY_MAX; i++)
2686e3ec7017SPing-Ke Shih 		skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]);
2687e3ec7017SPing-Ke Shih 	for (i = 0; i < RTW89_PHY_MAX; i++)
2688e3ec7017SPing-Ke Shih 		rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX;
2689e3ec7017SPing-Ke Shih }
2690e3ec7017SPing-Ke Shih 
2691d62816b4SPing-Ke Shih void rtw89_core_update_beacon_work(struct work_struct *work)
2692d62816b4SPing-Ke Shih {
2693d62816b4SPing-Ke Shih 	struct rtw89_dev *rtwdev;
2694d62816b4SPing-Ke Shih 	struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif,
2695d62816b4SPing-Ke Shih 						update_beacon_work);
2696d62816b4SPing-Ke Shih 
2697d62816b4SPing-Ke Shih 	if (rtwvif->net_type != RTW89_NET_TYPE_AP_MODE)
2698d62816b4SPing-Ke Shih 		return;
2699d62816b4SPing-Ke Shih 
2700d62816b4SPing-Ke Shih 	rtwdev = rtwvif->rtwdev;
2701d62816b4SPing-Ke Shih 	mutex_lock(&rtwdev->mutex);
2702d62816b4SPing-Ke Shih 	rtw89_fw_h2c_update_beacon(rtwdev, rtwvif);
2703d62816b4SPing-Ke Shih 	mutex_unlock(&rtwdev->mutex);
2704d62816b4SPing-Ke Shih }
2705d62816b4SPing-Ke Shih 
2706e3ec7017SPing-Ke Shih int rtw89_core_start(struct rtw89_dev *rtwdev)
2707e3ec7017SPing-Ke Shih {
2708e3ec7017SPing-Ke Shih 	int ret;
2709e3ec7017SPing-Ke Shih 
2710e3ec7017SPing-Ke Shih 	rtwdev->mac.qta_mode = RTW89_QTA_SCC;
2711e3ec7017SPing-Ke Shih 	ret = rtw89_mac_init(rtwdev);
2712e3ec7017SPing-Ke Shih 	if (ret) {
2713e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret);
2714e3ec7017SPing-Ke Shih 		return ret;
2715e3ec7017SPing-Ke Shih 	}
2716e3ec7017SPing-Ke Shih 
2717e3ec7017SPing-Ke Shih 	rtw89_btc_ntfy_poweron(rtwdev);
2718e3ec7017SPing-Ke Shih 
2719e3ec7017SPing-Ke Shih 	/* efuse process */
2720e3ec7017SPing-Ke Shih 
2721e3ec7017SPing-Ke Shih 	/* pre-config BB/RF, BB reset/RFC reset */
272261ebeecbSPing-Ke Shih 	rtw89_chip_disable_bb_rf(rtwdev);
272361ebeecbSPing-Ke Shih 	ret = rtw89_chip_enable_bb_rf(rtwdev);
272461ebeecbSPing-Ke Shih 	if (ret)
272561ebeecbSPing-Ke Shih 		return ret;
272661ebeecbSPing-Ke Shih 
2727e3ec7017SPing-Ke Shih 	rtw89_phy_init_bb_reg(rtwdev);
2728e3ec7017SPing-Ke Shih 	rtw89_phy_init_rf_reg(rtwdev);
2729e3ec7017SPing-Ke Shih 
2730e3ec7017SPing-Ke Shih 	rtw89_btc_ntfy_init(rtwdev, BTC_MODE_NORMAL);
2731e3ec7017SPing-Ke Shih 
2732e3ec7017SPing-Ke Shih 	rtw89_phy_dm_init(rtwdev);
2733e3ec7017SPing-Ke Shih 
2734e3ec7017SPing-Ke Shih 	rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, true);
2735e3ec7017SPing-Ke Shih 	rtw89_mac_update_rts_threshold(rtwdev, RTW89_MAC_0);
2736e3ec7017SPing-Ke Shih 
2737e3ec7017SPing-Ke Shih 	ret = rtw89_hci_start(rtwdev);
2738e3ec7017SPing-Ke Shih 	if (ret) {
2739e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to start hci\n");
2740e3ec7017SPing-Ke Shih 		return ret;
2741e3ec7017SPing-Ke Shih 	}
2742e3ec7017SPing-Ke Shih 
2743e3ec7017SPing-Ke Shih 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
2744e3ec7017SPing-Ke Shih 				     RTW89_TRACK_WORK_PERIOD);
2745e3ec7017SPing-Ke Shih 
2746e3ec7017SPing-Ke Shih 	set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
2747e3ec7017SPing-Ke Shih 
2748e3ec7017SPing-Ke Shih 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
2749e3ec7017SPing-Ke Shih 	rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.fw_log_enable);
2750e3ec7017SPing-Ke Shih 
2751e3ec7017SPing-Ke Shih 	return 0;
2752e3ec7017SPing-Ke Shih }
2753e3ec7017SPing-Ke Shih 
2754e3ec7017SPing-Ke Shih void rtw89_core_stop(struct rtw89_dev *rtwdev)
2755e3ec7017SPing-Ke Shih {
2756e3ec7017SPing-Ke Shih 	struct rtw89_btc *btc = &rtwdev->btc;
2757e3ec7017SPing-Ke Shih 
2758e3ec7017SPing-Ke Shih 	/* Prvent to stop twice; enter_ips and ops_stop */
2759e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
2760e3ec7017SPing-Ke Shih 		return;
2761e3ec7017SPing-Ke Shih 
2762e3ec7017SPing-Ke Shih 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_OFF);
2763e3ec7017SPing-Ke Shih 
2764e3ec7017SPing-Ke Shih 	clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
2765e3ec7017SPing-Ke Shih 
2766e3ec7017SPing-Ke Shih 	mutex_unlock(&rtwdev->mutex);
2767e3ec7017SPing-Ke Shih 
2768e3ec7017SPing-Ke Shih 	cancel_work_sync(&rtwdev->c2h_work);
2769e3ec7017SPing-Ke Shih 	cancel_work_sync(&btc->eapol_notify_work);
2770e3ec7017SPing-Ke Shih 	cancel_work_sync(&btc->arp_notify_work);
2771e3ec7017SPing-Ke Shih 	cancel_work_sync(&btc->dhcp_notify_work);
2772e3ec7017SPing-Ke Shih 	cancel_work_sync(&btc->icmp_notify_work);
2773e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work);
2774e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->track_work);
2775e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->coex_act1_work);
2776e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->coex_bt_devinfo_work);
2777e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work);
2778e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&rtwdev->cfo_track_work);
2779e3ec7017SPing-Ke Shih 
2780e3ec7017SPing-Ke Shih 	mutex_lock(&rtwdev->mutex);
2781e3ec7017SPing-Ke Shih 
2782e3ec7017SPing-Ke Shih 	rtw89_btc_ntfy_poweroff(rtwdev);
2783e3ec7017SPing-Ke Shih 	rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true);
2784e3ec7017SPing-Ke Shih 	rtw89_mac_flush_txq(rtwdev, BIT(rtwdev->hw->queues) - 1, true);
2785e3ec7017SPing-Ke Shih 	rtw89_hci_stop(rtwdev);
2786e3ec7017SPing-Ke Shih 	rtw89_hci_deinit(rtwdev);
2787e3ec7017SPing-Ke Shih 	rtw89_mac_pwr_off(rtwdev);
2788e3ec7017SPing-Ke Shih 	rtw89_hci_reset(rtwdev);
2789e3ec7017SPing-Ke Shih }
2790e3ec7017SPing-Ke Shih 
2791e3ec7017SPing-Ke Shih int rtw89_core_init(struct rtw89_dev *rtwdev)
2792e3ec7017SPing-Ke Shih {
2793e3ec7017SPing-Ke Shih 	struct rtw89_btc *btc = &rtwdev->btc;
2794e3ec7017SPing-Ke Shih 	int ret;
279589590777SPo Hao Huang 	u8 band;
2796e3ec7017SPing-Ke Shih 
2797e3ec7017SPing-Ke Shih 	INIT_LIST_HEAD(&rtwdev->ba_list);
2798e3ec7017SPing-Ke Shih 	INIT_LIST_HEAD(&rtwdev->rtwvifs_list);
2799e3ec7017SPing-Ke Shih 	INIT_LIST_HEAD(&rtwdev->early_h2c_list);
280089590777SPo Hao Huang 	for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
280189590777SPo Hao Huang 		if (!(rtwdev->chip->support_bands & BIT(band)))
280289590777SPo Hao Huang 			continue;
280389590777SPo Hao Huang 		INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]);
280489590777SPo Hao Huang 	}
2805e3ec7017SPing-Ke Shih 	INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
2806e3ec7017SPing-Ke Shih 	INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
2807e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
2808e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work);
2809e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work);
2810e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);
2811e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);
2812e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);
2813e3ec7017SPing-Ke Shih 	rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0);
2814e3ec7017SPing-Ke Shih 	spin_lock_init(&rtwdev->ba_lock);
28157bfd05ffSChin-Yen Lee 	spin_lock_init(&rtwdev->rpwm_lock);
2816e3ec7017SPing-Ke Shih 	mutex_init(&rtwdev->mutex);
2817e3ec7017SPing-Ke Shih 	mutex_init(&rtwdev->rf_mutex);
2818e3ec7017SPing-Ke Shih 	rtwdev->total_sta_assoc = 0;
2819e3ec7017SPing-Ke Shih 
2820e3ec7017SPing-Ke Shih 	INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work);
282189590777SPo Hao Huang 	INIT_WORK(&rtwdev->ips_work, rtw89_ips_work);
2822e3ec7017SPing-Ke Shih 	skb_queue_head_init(&rtwdev->c2h_queue);
2823e3ec7017SPing-Ke Shih 	rtw89_core_ppdu_sts_init(rtwdev);
2824e3ec7017SPing-Ke Shih 	rtw89_traffic_stats_init(rtwdev, &rtwdev->stats);
2825e3ec7017SPing-Ke Shih 
2826e3ec7017SPing-Ke Shih 	rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev);
2827e3ec7017SPing-Ke Shih 	rtwdev->hal.rx_fltr = DEFAULT_AX_RX_FLTR;
2828e3ec7017SPing-Ke Shih 
2829e3ec7017SPing-Ke Shih 	INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work);
2830e3ec7017SPing-Ke Shih 	INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work);
2831e3ec7017SPing-Ke Shih 	INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work);
2832e3ec7017SPing-Ke Shih 	INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work);
2833e3ec7017SPing-Ke Shih 
2834e3ec7017SPing-Ke Shih 	ret = rtw89_load_firmware(rtwdev);
2835e3ec7017SPing-Ke Shih 	if (ret) {
2836e3ec7017SPing-Ke Shih 		rtw89_warn(rtwdev, "no firmware loaded\n");
2837e3ec7017SPing-Ke Shih 		return ret;
2838e3ec7017SPing-Ke Shih 	}
2839e3ec7017SPing-Ke Shih 	rtw89_ser_init(rtwdev);
2840e3ec7017SPing-Ke Shih 
2841e3ec7017SPing-Ke Shih 	return 0;
2842e3ec7017SPing-Ke Shih }
2843e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_init);
2844e3ec7017SPing-Ke Shih 
2845e3ec7017SPing-Ke Shih void rtw89_core_deinit(struct rtw89_dev *rtwdev)
2846e3ec7017SPing-Ke Shih {
2847e3ec7017SPing-Ke Shih 	rtw89_ser_deinit(rtwdev);
2848e3ec7017SPing-Ke Shih 	rtw89_unload_firmware(rtwdev);
2849e3ec7017SPing-Ke Shih 	rtw89_fw_free_all_early_h2c(rtwdev);
2850e3ec7017SPing-Ke Shih 
2851e3ec7017SPing-Ke Shih 	destroy_workqueue(rtwdev->txq_wq);
2852e3ec7017SPing-Ke Shih 	mutex_destroy(&rtwdev->rf_mutex);
2853e3ec7017SPing-Ke Shih 	mutex_destroy(&rtwdev->mutex);
2854e3ec7017SPing-Ke Shih }
2855e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_deinit);
2856e3ec7017SPing-Ke Shih 
285789590777SPo Hao Huang void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
285889590777SPo Hao Huang 			   const u8 *mac_addr, bool hw_scan)
285989590777SPo Hao Huang {
286089590777SPo Hao Huang 	struct rtw89_hal *hal = &rtwdev->hal;
286189590777SPo Hao Huang 
286289590777SPo Hao Huang 	rtwdev->scanning = true;
286389590777SPo Hao Huang 	rtw89_leave_lps(rtwdev);
2864ee20d538SPo Hao Huang 	if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE))
286589590777SPo Hao Huang 		rtw89_leave_ips(rtwdev);
286689590777SPo Hao Huang 
286789590777SPo Hao Huang 	ether_addr_copy(rtwvif->mac_addr, mac_addr);
286889590777SPo Hao Huang 	rtw89_btc_ntfy_scan_start(rtwdev, RTW89_PHY_0, hal->current_band_type);
286989590777SPo Hao Huang 	rtw89_chip_rfk_scan(rtwdev, true);
287089590777SPo Hao Huang 	rtw89_hci_recalc_int_mit(rtwdev);
287189590777SPo Hao Huang 
287289590777SPo Hao Huang 	rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, mac_addr);
287389590777SPo Hao Huang }
287489590777SPo Hao Huang 
287589590777SPo Hao Huang void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
287689590777SPo Hao Huang 			      struct ieee80211_vif *vif, bool hw_scan)
287789590777SPo Hao Huang {
287889590777SPo Hao Huang 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
287989590777SPo Hao Huang 
288089590777SPo Hao Huang 	ether_addr_copy(rtwvif->mac_addr, vif->addr);
288189590777SPo Hao Huang 	rtw89_fw_h2c_cam(rtwdev, rtwvif, NULL, NULL);
288289590777SPo Hao Huang 
288389590777SPo Hao Huang 	rtw89_chip_rfk_scan(rtwdev, false);
288489590777SPo Hao Huang 	rtw89_btc_ntfy_scan_finish(rtwdev, RTW89_PHY_0);
288589590777SPo Hao Huang 
288689590777SPo Hao Huang 	rtwdev->scanning = false;
288789590777SPo Hao Huang 	rtwdev->dig.bypass_dig = true;
2888ee20d538SPo Hao Huang 	if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE))
288989590777SPo Hao Huang 		ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work);
289089590777SPo Hao Huang }
289189590777SPo Hao Huang 
2892e3ec7017SPing-Ke Shih static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev)
2893e3ec7017SPing-Ke Shih {
2894a8bdac12SPing-Ke Shih 	const struct rtw89_chip_info *chip = rtwdev->chip;
2895e3ec7017SPing-Ke Shih 	u8 cv;
2896e3ec7017SPing-Ke Shih 
2897e3ec7017SPing-Ke Shih 	cv = rtw89_read32_mask(rtwdev, R_AX_SYS_CFG1, B_AX_CHIP_VER_MASK);
2898a8bdac12SPing-Ke Shih 	if (chip->chip_id == RTL8852A && cv <= CHIP_CBV) {
2899e3ec7017SPing-Ke Shih 		if (rtw89_read32(rtwdev, R_AX_GPIO0_7_FUNC_SEL) == RTW89_R32_DEAD)
2900e3ec7017SPing-Ke Shih 			cv = CHIP_CAV;
2901e3ec7017SPing-Ke Shih 		else
2902e3ec7017SPing-Ke Shih 			cv = CHIP_CBV;
2903e3ec7017SPing-Ke Shih 	}
2904e3ec7017SPing-Ke Shih 
2905e3ec7017SPing-Ke Shih 	rtwdev->hal.cv = cv;
2906e3ec7017SPing-Ke Shih }
2907e3ec7017SPing-Ke Shih 
29081c2423deSJohnson Lin static void rtw89_core_setup_phycap(struct rtw89_dev *rtwdev)
29091c2423deSJohnson Lin {
29101c2423deSJohnson Lin 	rtwdev->hal.support_cckpd =
29111c2423deSJohnson Lin 		!(rtwdev->chip->chip_id == RTL8852A && rtwdev->hal.cv <= CHIP_CBV) &&
29121c2423deSJohnson Lin 		!(rtwdev->chip->chip_id == RTL8852B && rtwdev->hal.cv <= CHIP_CAV);
29131e6f0d2aSJohnson Lin 	rtwdev->hal.support_igi =
29141e6f0d2aSJohnson Lin 		rtwdev->chip->chip_id == RTL8852A && rtwdev->hal.cv <= CHIP_CBV;
29151c2423deSJohnson Lin }
29161c2423deSJohnson Lin 
2917e3ec7017SPing-Ke Shih static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
2918e3ec7017SPing-Ke Shih {
2919e3ec7017SPing-Ke Shih 	int ret;
2920e3ec7017SPing-Ke Shih 
2921e3ec7017SPing-Ke Shih 	ret = rtw89_mac_partial_init(rtwdev);
2922e3ec7017SPing-Ke Shih 	if (ret)
2923e3ec7017SPing-Ke Shih 		return ret;
2924e3ec7017SPing-Ke Shih 
2925e3ec7017SPing-Ke Shih 	ret = rtw89_parse_efuse_map(rtwdev);
2926e3ec7017SPing-Ke Shih 	if (ret)
2927e3ec7017SPing-Ke Shih 		return ret;
2928e3ec7017SPing-Ke Shih 
2929e3ec7017SPing-Ke Shih 	ret = rtw89_parse_phycap_map(rtwdev);
2930e3ec7017SPing-Ke Shih 	if (ret)
2931e3ec7017SPing-Ke Shih 		return ret;
2932e3ec7017SPing-Ke Shih 
2933e3ec7017SPing-Ke Shih 	ret = rtw89_mac_setup_phycap(rtwdev);
2934e3ec7017SPing-Ke Shih 	if (ret)
2935e3ec7017SPing-Ke Shih 		return ret;
2936e3ec7017SPing-Ke Shih 
29371c2423deSJohnson Lin 	rtw89_core_setup_phycap(rtwdev);
29381c2423deSJohnson Lin 
2939e3ec7017SPing-Ke Shih 	rtw89_mac_pwr_off(rtwdev);
2940e3ec7017SPing-Ke Shih 
2941e3ec7017SPing-Ke Shih 	return 0;
2942e3ec7017SPing-Ke Shih }
2943e3ec7017SPing-Ke Shih 
2944e3ec7017SPing-Ke Shih static int rtw89_chip_board_info_setup(struct rtw89_dev *rtwdev)
2945e3ec7017SPing-Ke Shih {
2946e3ec7017SPing-Ke Shih 	rtw89_chip_fem_setup(rtwdev);
2947e3ec7017SPing-Ke Shih 
2948e3ec7017SPing-Ke Shih 	return 0;
2949e3ec7017SPing-Ke Shih }
2950e3ec7017SPing-Ke Shih 
2951e3ec7017SPing-Ke Shih int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
2952e3ec7017SPing-Ke Shih {
2953e3ec7017SPing-Ke Shih 	int ret;
2954e3ec7017SPing-Ke Shih 
2955e3ec7017SPing-Ke Shih 	rtw89_read_chip_ver(rtwdev);
2956e3ec7017SPing-Ke Shih 
2957e3ec7017SPing-Ke Shih 	ret = rtw89_wait_firmware_completion(rtwdev);
2958e3ec7017SPing-Ke Shih 	if (ret) {
2959e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to wait firmware completion\n");
2960e3ec7017SPing-Ke Shih 		return ret;
2961e3ec7017SPing-Ke Shih 	}
2962e3ec7017SPing-Ke Shih 
2963e3ec7017SPing-Ke Shih 	ret = rtw89_fw_recognize(rtwdev);
2964e3ec7017SPing-Ke Shih 	if (ret) {
2965e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to recognize firmware\n");
2966e3ec7017SPing-Ke Shih 		return ret;
2967e3ec7017SPing-Ke Shih 	}
2968e3ec7017SPing-Ke Shih 
2969e3ec7017SPing-Ke Shih 	ret = rtw89_chip_efuse_info_setup(rtwdev);
2970e3ec7017SPing-Ke Shih 	if (ret)
2971e3ec7017SPing-Ke Shih 		return ret;
2972e3ec7017SPing-Ke Shih 
2973e3ec7017SPing-Ke Shih 	ret = rtw89_chip_board_info_setup(rtwdev);
2974e3ec7017SPing-Ke Shih 	if (ret)
2975e3ec7017SPing-Ke Shih 		return ret;
2976e3ec7017SPing-Ke Shih 
2977e3ec7017SPing-Ke Shih 	return 0;
2978e3ec7017SPing-Ke Shih }
2979e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_chip_info_setup);
2980e3ec7017SPing-Ke Shih 
2981e3ec7017SPing-Ke Shih static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
2982e3ec7017SPing-Ke Shih {
2983e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
2984e3ec7017SPing-Ke Shih 	struct rtw89_efuse *efuse = &rtwdev->efuse;
2985e3ec7017SPing-Ke Shih 	int ret;
2986e3ec7017SPing-Ke Shih 	int tx_headroom = IEEE80211_HT_CTL_LEN;
2987e3ec7017SPing-Ke Shih 
2988e3ec7017SPing-Ke Shih 	hw->vif_data_size = sizeof(struct rtw89_vif);
2989e3ec7017SPing-Ke Shih 	hw->sta_data_size = sizeof(struct rtw89_sta);
2990e3ec7017SPing-Ke Shih 	hw->txq_data_size = sizeof(struct rtw89_txq);
2991e3ec7017SPing-Ke Shih 
2992e3ec7017SPing-Ke Shih 	SET_IEEE80211_PERM_ADDR(hw, efuse->addr);
2993e3ec7017SPing-Ke Shih 
2994e3ec7017SPing-Ke Shih 	hw->extra_tx_headroom = tx_headroom;
2995e3ec7017SPing-Ke Shih 	hw->queues = IEEE80211_NUM_ACS;
2996e3ec7017SPing-Ke Shih 	hw->max_rx_aggregation_subframes = RTW89_MAX_RX_AGG_NUM;
2997e3ec7017SPing-Ke Shih 	hw->max_tx_aggregation_subframes = RTW89_MAX_TX_AGG_NUM;
2998e3ec7017SPing-Ke Shih 
2999e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, SIGNAL_DBM);
3000e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
3001e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, MFP_CAPABLE);
3002e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
3003e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
3004e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, RX_INCLUDES_FCS);
3005e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, TX_AMSDU);
3006e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
3007e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
3008e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, SUPPORTS_PS);
3009e3ec7017SPing-Ke Shih 	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
301089590777SPo Hao Huang 	ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
3011e3ec7017SPing-Ke Shih 
3012b478ff6bSPing-Ke Shih 	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3013b478ff6bSPing-Ke Shih 				     BIT(NL80211_IFTYPE_AP);
3014e3ec7017SPing-Ke Shih 	hw->wiphy->available_antennas_tx = BIT(rtwdev->chip->rf_path_num) - 1;
3015e3ec7017SPing-Ke Shih 	hw->wiphy->available_antennas_rx = BIT(rtwdev->chip->rf_path_num) - 1;
3016e3ec7017SPing-Ke Shih 
3017e3ec7017SPing-Ke Shih 	hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
3018e3ec7017SPing-Ke Shih 
301989590777SPo Hao Huang 	hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID;
302089590777SPo Hao Huang 	hw->wiphy->max_scan_ie_len = RTW89_SCANOFLD_MAX_IE_LEN;
302189590777SPo Hao Huang 
3022e3ec7017SPing-Ke Shih 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
3023e3ec7017SPing-Ke Shih 
3024e3ec7017SPing-Ke Shih 	ret = rtw89_core_set_supported_band(rtwdev);
3025e3ec7017SPing-Ke Shih 	if (ret) {
3026e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to set supported band\n");
3027e3ec7017SPing-Ke Shih 		return ret;
3028e3ec7017SPing-Ke Shih 	}
3029e3ec7017SPing-Ke Shih 
3030e3ec7017SPing-Ke Shih 	hw->wiphy->reg_notifier = rtw89_regd_notifier;
3031e3ec7017SPing-Ke Shih 	hw->wiphy->sar_capa = &rtw89_sar_capa;
3032e3ec7017SPing-Ke Shih 
3033e3ec7017SPing-Ke Shih 	ret = ieee80211_register_hw(hw);
3034e3ec7017SPing-Ke Shih 	if (ret) {
3035e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to register hw\n");
3036e3ec7017SPing-Ke Shih 		goto err;
3037e3ec7017SPing-Ke Shih 	}
3038e3ec7017SPing-Ke Shih 
3039e3ec7017SPing-Ke Shih 	ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier);
3040e3ec7017SPing-Ke Shih 	if (ret) {
3041e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to init regd\n");
3042e3ec7017SPing-Ke Shih 		goto err;
3043e3ec7017SPing-Ke Shih 	}
3044e3ec7017SPing-Ke Shih 
3045e3ec7017SPing-Ke Shih 	return 0;
3046e3ec7017SPing-Ke Shih 
3047e3ec7017SPing-Ke Shih err:
3048e3ec7017SPing-Ke Shih 	return ret;
3049e3ec7017SPing-Ke Shih }
3050e3ec7017SPing-Ke Shih 
3051e3ec7017SPing-Ke Shih static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev)
3052e3ec7017SPing-Ke Shih {
3053e3ec7017SPing-Ke Shih 	struct ieee80211_hw *hw = rtwdev->hw;
3054e3ec7017SPing-Ke Shih 
3055e3ec7017SPing-Ke Shih 	ieee80211_unregister_hw(hw);
3056e3ec7017SPing-Ke Shih 	rtw89_core_clr_supported_band(rtwdev);
3057e3ec7017SPing-Ke Shih }
3058e3ec7017SPing-Ke Shih 
3059e3ec7017SPing-Ke Shih int rtw89_core_register(struct rtw89_dev *rtwdev)
3060e3ec7017SPing-Ke Shih {
3061e3ec7017SPing-Ke Shih 	int ret;
3062e3ec7017SPing-Ke Shih 
3063e3ec7017SPing-Ke Shih 	ret = rtw89_core_register_hw(rtwdev);
3064e3ec7017SPing-Ke Shih 	if (ret) {
3065e3ec7017SPing-Ke Shih 		rtw89_err(rtwdev, "failed to register core hw\n");
3066e3ec7017SPing-Ke Shih 		return ret;
3067e3ec7017SPing-Ke Shih 	}
3068e3ec7017SPing-Ke Shih 
3069e3ec7017SPing-Ke Shih 	rtw89_debugfs_init(rtwdev);
3070e3ec7017SPing-Ke Shih 
3071e3ec7017SPing-Ke Shih 	return 0;
3072e3ec7017SPing-Ke Shih }
3073e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_register);
3074e3ec7017SPing-Ke Shih 
3075e3ec7017SPing-Ke Shih void rtw89_core_unregister(struct rtw89_dev *rtwdev)
3076e3ec7017SPing-Ke Shih {
3077e3ec7017SPing-Ke Shih 	rtw89_core_unregister_hw(rtwdev);
3078e3ec7017SPing-Ke Shih }
3079e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_core_unregister);
3080e3ec7017SPing-Ke Shih 
3081e3ec7017SPing-Ke Shih MODULE_AUTHOR("Realtek Corporation");
3082e3ec7017SPing-Ke Shih MODULE_DESCRIPTION("Realtek 802.11ax wireless core module");
3083e3ec7017SPing-Ke Shih MODULE_LICENSE("Dual BSD/GPL");
3084