xref: /linux/drivers/net/wireless/mediatek/mt76/mt792x_core.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1c74df1c0SLorenzo Bianconi // SPDX-License-Identifier: ISC
2c74df1c0SLorenzo Bianconi /* Copyright (C) 2023 MediaTek Inc. */
3c74df1c0SLorenzo Bianconi 
4c74df1c0SLorenzo Bianconi #include <linux/module.h>
5e8a264ccSLorenzo Bianconi #include <linux/firmware.h>
6c74df1c0SLorenzo Bianconi 
7c74df1c0SLorenzo Bianconi #include "mt792x.h"
8c693f2f0SLorenzo Bianconi #include "dma.h"
9c74df1c0SLorenzo Bianconi 
10e8a264ccSLorenzo Bianconi static const struct ieee80211_iface_limit if_limits[] = {
11e8a264ccSLorenzo Bianconi 	{
12e8a264ccSLorenzo Bianconi 		.max = MT792x_MAX_INTERFACES,
13e8a264ccSLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_STATION)
14e8a264ccSLorenzo Bianconi 	},
15e8a264ccSLorenzo Bianconi 	{
16e8a264ccSLorenzo Bianconi 		.max = 1,
17e8a264ccSLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_AP)
18e8a264ccSLorenzo Bianconi 	}
19e8a264ccSLorenzo Bianconi };
20e8a264ccSLorenzo Bianconi 
21e8a264ccSLorenzo Bianconi static const struct ieee80211_iface_combination if_comb[] = {
22e8a264ccSLorenzo Bianconi 	{
23e8a264ccSLorenzo Bianconi 		.limits = if_limits,
24e8a264ccSLorenzo Bianconi 		.n_limits = ARRAY_SIZE(if_limits),
25e8a264ccSLorenzo Bianconi 		.max_interfaces = MT792x_MAX_INTERFACES,
26e8a264ccSLorenzo Bianconi 		.num_different_channels = 1,
27e8a264ccSLorenzo Bianconi 		.beacon_int_infra_match = true,
28e8a264ccSLorenzo Bianconi 	},
29e8a264ccSLorenzo Bianconi };
30e8a264ccSLorenzo Bianconi 
31e8a264ccSLorenzo Bianconi static const struct ieee80211_iface_limit if_limits_chanctx[] = {
32e8a264ccSLorenzo Bianconi 	{
33e8a264ccSLorenzo Bianconi 		.max = 2,
34e8a264ccSLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_STATION) |
35e8a264ccSLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_CLIENT)
36e8a264ccSLorenzo Bianconi 	},
37e8a264ccSLorenzo Bianconi 	{
38e8a264ccSLorenzo Bianconi 		.max = 1,
39e8a264ccSLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_AP) |
40e8a264ccSLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_GO)
41e8a264ccSLorenzo Bianconi 	}
42e8a264ccSLorenzo Bianconi };
43e8a264ccSLorenzo Bianconi 
44e8a264ccSLorenzo Bianconi static const struct ieee80211_iface_combination if_comb_chanctx[] = {
45e8a264ccSLorenzo Bianconi 	{
46e8a264ccSLorenzo Bianconi 		.limits = if_limits_chanctx,
47e8a264ccSLorenzo Bianconi 		.n_limits = ARRAY_SIZE(if_limits_chanctx),
48e8a264ccSLorenzo Bianconi 		.max_interfaces = 2,
49e8a264ccSLorenzo Bianconi 		.num_different_channels = 2,
50e8a264ccSLorenzo Bianconi 		.beacon_int_infra_match = false,
51e8a264ccSLorenzo Bianconi 	}
52e8a264ccSLorenzo Bianconi };
53e8a264ccSLorenzo Bianconi 
mt792x_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)54c74df1c0SLorenzo Bianconi void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
55c74df1c0SLorenzo Bianconi 	       struct sk_buff *skb)
56c74df1c0SLorenzo Bianconi {
57c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
58c74df1c0SLorenzo Bianconi 	struct mt76_phy *mphy = hw->priv;
59c74df1c0SLorenzo Bianconi 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
60c74df1c0SLorenzo Bianconi 	struct ieee80211_vif *vif = info->control.vif;
61c74df1c0SLorenzo Bianconi 	struct mt76_wcid *wcid = &dev->mt76.global_wcid;
62c74df1c0SLorenzo Bianconi 	int qid;
63c74df1c0SLorenzo Bianconi 
64c74df1c0SLorenzo Bianconi 	if (control->sta) {
65c74df1c0SLorenzo Bianconi 		struct mt792x_sta *sta;
66c74df1c0SLorenzo Bianconi 
67c74df1c0SLorenzo Bianconi 		sta = (struct mt792x_sta *)control->sta->drv_priv;
68c74df1c0SLorenzo Bianconi 		wcid = &sta->wcid;
69c74df1c0SLorenzo Bianconi 	}
70c74df1c0SLorenzo Bianconi 
71c74df1c0SLorenzo Bianconi 	if (vif && !control->sta) {
72c74df1c0SLorenzo Bianconi 		struct mt792x_vif *mvif;
73c74df1c0SLorenzo Bianconi 
74c74df1c0SLorenzo Bianconi 		mvif = (struct mt792x_vif *)vif->drv_priv;
75c74df1c0SLorenzo Bianconi 		wcid = &mvif->sta.wcid;
76c74df1c0SLorenzo Bianconi 	}
77c74df1c0SLorenzo Bianconi 
78c74df1c0SLorenzo Bianconi 	if (mt76_connac_pm_ref(mphy, &dev->pm)) {
79c74df1c0SLorenzo Bianconi 		mt76_tx(mphy, control->sta, wcid, skb);
80c74df1c0SLorenzo Bianconi 		mt76_connac_pm_unref(mphy, &dev->pm);
81c74df1c0SLorenzo Bianconi 		return;
82c74df1c0SLorenzo Bianconi 	}
83c74df1c0SLorenzo Bianconi 
84c74df1c0SLorenzo Bianconi 	qid = skb_get_queue_mapping(skb);
85c74df1c0SLorenzo Bianconi 	if (qid >= MT_TXQ_PSD) {
86c74df1c0SLorenzo Bianconi 		qid = IEEE80211_AC_BE;
87c74df1c0SLorenzo Bianconi 		skb_set_queue_mapping(skb, qid);
88c74df1c0SLorenzo Bianconi 	}
89c74df1c0SLorenzo Bianconi 
90c74df1c0SLorenzo Bianconi 	mt76_connac_pm_queue_skb(hw, &dev->pm, wcid, skb);
91c74df1c0SLorenzo Bianconi }
92c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_tx);
93c74df1c0SLorenzo Bianconi 
mt792x_stop(struct ieee80211_hw * hw)945ab7d466SLorenzo Bianconi void mt792x_stop(struct ieee80211_hw *hw)
955ab7d466SLorenzo Bianconi {
965ab7d466SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
975ab7d466SLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
985ab7d466SLorenzo Bianconi 
995ab7d466SLorenzo Bianconi 	cancel_delayed_work_sync(&phy->mt76->mac_work);
1005ab7d466SLorenzo Bianconi 
1015ab7d466SLorenzo Bianconi 	cancel_delayed_work_sync(&dev->pm.ps_work);
1025ab7d466SLorenzo Bianconi 	cancel_work_sync(&dev->pm.wake_work);
1035ab7d466SLorenzo Bianconi 	cancel_work_sync(&dev->reset_work);
1045ab7d466SLorenzo Bianconi 	mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
1055ab7d466SLorenzo Bianconi 
1065ab7d466SLorenzo Bianconi 	if (is_mt7921(&dev->mt76)) {
1075ab7d466SLorenzo Bianconi 		mt792x_mutex_acquire(dev);
1085ab7d466SLorenzo Bianconi 		mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false);
1095ab7d466SLorenzo Bianconi 		mt792x_mutex_release(dev);
1105ab7d466SLorenzo Bianconi 	}
1115ab7d466SLorenzo Bianconi 
1125ab7d466SLorenzo Bianconi 	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
1135ab7d466SLorenzo Bianconi }
1145ab7d466SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_stop);
1155ab7d466SLorenzo Bianconi 
mt792x_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)116c74df1c0SLorenzo Bianconi void mt792x_remove_interface(struct ieee80211_hw *hw,
117c74df1c0SLorenzo Bianconi 			     struct ieee80211_vif *vif)
118c74df1c0SLorenzo Bianconi {
119c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
120c74df1c0SLorenzo Bianconi 	struct mt792x_sta *msta = &mvif->sta;
121c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
122c74df1c0SLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
123c74df1c0SLorenzo Bianconi 	int idx = msta->wcid.idx;
124c74df1c0SLorenzo Bianconi 
125c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(dev);
126c74df1c0SLorenzo Bianconi 	mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->wcid);
127c74df1c0SLorenzo Bianconi 	mt76_connac_mcu_uni_add_dev(&dev->mphy, vif, &mvif->sta.wcid, false);
128c74df1c0SLorenzo Bianconi 
129c74df1c0SLorenzo Bianconi 	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
130c74df1c0SLorenzo Bianconi 
131c74df1c0SLorenzo Bianconi 	dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
132c74df1c0SLorenzo Bianconi 	phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
133c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(dev);
134c74df1c0SLorenzo Bianconi 
135c74df1c0SLorenzo Bianconi 	spin_lock_bh(&dev->mt76.sta_poll_lock);
136c74df1c0SLorenzo Bianconi 	if (!list_empty(&msta->wcid.poll_list))
137c74df1c0SLorenzo Bianconi 		list_del_init(&msta->wcid.poll_list);
138c74df1c0SLorenzo Bianconi 	spin_unlock_bh(&dev->mt76.sta_poll_lock);
139c74df1c0SLorenzo Bianconi 
1400335c034SFelix Fietkau 	mt76_wcid_cleanup(&dev->mt76, &msta->wcid);
141c74df1c0SLorenzo Bianconi }
142c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_remove_interface);
143c74df1c0SLorenzo Bianconi 
mt792x_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,u16 queue,const struct ieee80211_tx_queue_params * params)144c74df1c0SLorenzo Bianconi int mt792x_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
145c74df1c0SLorenzo Bianconi 		   unsigned int link_id, u16 queue,
146c74df1c0SLorenzo Bianconi 		   const struct ieee80211_tx_queue_params *params)
147c74df1c0SLorenzo Bianconi {
148c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
149c74df1c0SLorenzo Bianconi 
150c74df1c0SLorenzo Bianconi 	/* no need to update right away, we'll get BSS_CHANGED_QOS */
151c74df1c0SLorenzo Bianconi 	queue = mt76_connac_lmac_mapping(queue);
152c74df1c0SLorenzo Bianconi 	mvif->queue_params[queue] = *params;
153c74df1c0SLorenzo Bianconi 
154c74df1c0SLorenzo Bianconi 	return 0;
155c74df1c0SLorenzo Bianconi }
156c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_conf_tx);
157c74df1c0SLorenzo Bianconi 
mt792x_get_stats(struct ieee80211_hw * hw,struct ieee80211_low_level_stats * stats)158c74df1c0SLorenzo Bianconi int mt792x_get_stats(struct ieee80211_hw *hw,
159c74df1c0SLorenzo Bianconi 		     struct ieee80211_low_level_stats *stats)
160c74df1c0SLorenzo Bianconi {
161c74df1c0SLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
162c74df1c0SLorenzo Bianconi 	struct mt76_mib_stats *mib = &phy->mib;
163c74df1c0SLorenzo Bianconi 
164c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(phy->dev);
165c74df1c0SLorenzo Bianconi 
166c74df1c0SLorenzo Bianconi 	stats->dot11RTSSuccessCount = mib->rts_cnt;
167c74df1c0SLorenzo Bianconi 	stats->dot11RTSFailureCount = mib->rts_retries_cnt;
168c74df1c0SLorenzo Bianconi 	stats->dot11FCSErrorCount = mib->fcs_err_cnt;
169c74df1c0SLorenzo Bianconi 	stats->dot11ACKFailureCount = mib->ack_fail_cnt;
170c74df1c0SLorenzo Bianconi 
171c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(phy->dev);
172c74df1c0SLorenzo Bianconi 
173c74df1c0SLorenzo Bianconi 	return 0;
174c74df1c0SLorenzo Bianconi }
175c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_stats);
176c74df1c0SLorenzo Bianconi 
mt792x_get_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif)177c74df1c0SLorenzo Bianconi u64 mt792x_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
178c74df1c0SLorenzo Bianconi {
179c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
180c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
181c74df1c0SLorenzo Bianconi 	u8 omac_idx = mvif->mt76.omac_idx;
182c74df1c0SLorenzo Bianconi 	union {
183c74df1c0SLorenzo Bianconi 		u64 t64;
184c74df1c0SLorenzo Bianconi 		u32 t32[2];
185c74df1c0SLorenzo Bianconi 	} tsf;
186c74df1c0SLorenzo Bianconi 	u16 n;
187c74df1c0SLorenzo Bianconi 
188c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(dev);
189c74df1c0SLorenzo Bianconi 
190c74df1c0SLorenzo Bianconi 	n = omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : omac_idx;
191c74df1c0SLorenzo Bianconi 	/* TSF software read */
192c74df1c0SLorenzo Bianconi 	mt76_set(dev, MT_LPON_TCR(0, n), MT_LPON_TCR_SW_MODE);
193c74df1c0SLorenzo Bianconi 	tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(0));
194c74df1c0SLorenzo Bianconi 	tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(0));
195c74df1c0SLorenzo Bianconi 
196c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(dev);
197c74df1c0SLorenzo Bianconi 
198c74df1c0SLorenzo Bianconi 	return tsf.t64;
199c74df1c0SLorenzo Bianconi }
200c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_tsf);
201c74df1c0SLorenzo Bianconi 
mt792x_set_tsf(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u64 timestamp)202c74df1c0SLorenzo Bianconi void mt792x_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
203c74df1c0SLorenzo Bianconi 		    u64 timestamp)
204c74df1c0SLorenzo Bianconi {
205c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
206c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
207c74df1c0SLorenzo Bianconi 	u8 omac_idx = mvif->mt76.omac_idx;
208c74df1c0SLorenzo Bianconi 	union {
209c74df1c0SLorenzo Bianconi 		u64 t64;
210c74df1c0SLorenzo Bianconi 		u32 t32[2];
211c74df1c0SLorenzo Bianconi 	} tsf = { .t64 = timestamp, };
212c74df1c0SLorenzo Bianconi 	u16 n;
213c74df1c0SLorenzo Bianconi 
214c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(dev);
215c74df1c0SLorenzo Bianconi 
216c74df1c0SLorenzo Bianconi 	n = omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : omac_idx;
217c74df1c0SLorenzo Bianconi 	mt76_wr(dev, MT_LPON_UTTR0(0), tsf.t32[0]);
218c74df1c0SLorenzo Bianconi 	mt76_wr(dev, MT_LPON_UTTR1(0), tsf.t32[1]);
219c74df1c0SLorenzo Bianconi 	/* TSF software overwrite */
220c74df1c0SLorenzo Bianconi 	mt76_set(dev, MT_LPON_TCR(0, n), MT_LPON_TCR_SW_WRITE);
221c74df1c0SLorenzo Bianconi 
222c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(dev);
223c74df1c0SLorenzo Bianconi }
224c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_set_tsf);
225c74df1c0SLorenzo Bianconi 
mt792x_tx_worker(struct mt76_worker * w)226c74df1c0SLorenzo Bianconi void mt792x_tx_worker(struct mt76_worker *w)
227c74df1c0SLorenzo Bianconi {
228c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = container_of(w, struct mt792x_dev,
229c74df1c0SLorenzo Bianconi 					      mt76.tx_worker);
230c74df1c0SLorenzo Bianconi 
231c74df1c0SLorenzo Bianconi 	if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
232c74df1c0SLorenzo Bianconi 		queue_work(dev->mt76.wq, &dev->pm.wake_work);
233c74df1c0SLorenzo Bianconi 		return;
234c74df1c0SLorenzo Bianconi 	}
235c74df1c0SLorenzo Bianconi 
236c74df1c0SLorenzo Bianconi 	mt76_txq_schedule_all(&dev->mphy);
237c74df1c0SLorenzo Bianconi 	mt76_connac_pm_unref(&dev->mphy, &dev->pm);
238c74df1c0SLorenzo Bianconi }
239c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_tx_worker);
240c74df1c0SLorenzo Bianconi 
mt792x_roc_timer(struct timer_list * timer)241c74df1c0SLorenzo Bianconi void mt792x_roc_timer(struct timer_list *timer)
242c74df1c0SLorenzo Bianconi {
243c74df1c0SLorenzo Bianconi 	struct mt792x_phy *phy = from_timer(phy, timer, roc_timer);
244c74df1c0SLorenzo Bianconi 
245c74df1c0SLorenzo Bianconi 	ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
246c74df1c0SLorenzo Bianconi }
247c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_roc_timer);
248c74df1c0SLorenzo Bianconi 
mt792x_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 queues,bool drop)249c74df1c0SLorenzo Bianconi void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
250c74df1c0SLorenzo Bianconi 		  u32 queues, bool drop)
251c74df1c0SLorenzo Bianconi {
252c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
253c74df1c0SLorenzo Bianconi 
254c74df1c0SLorenzo Bianconi 	wait_event_timeout(dev->mt76.tx_wait,
255c74df1c0SLorenzo Bianconi 			   !mt76_has_tx_pending(&dev->mphy), HZ / 2);
256c74df1c0SLorenzo Bianconi }
257c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_flush);
258c74df1c0SLorenzo Bianconi 
mt792x_assign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,struct ieee80211_chanctx_conf * ctx)259c74df1c0SLorenzo Bianconi int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw,
260c74df1c0SLorenzo Bianconi 			      struct ieee80211_vif *vif,
261c74df1c0SLorenzo Bianconi 			      struct ieee80211_bss_conf *link_conf,
262c74df1c0SLorenzo Bianconi 			      struct ieee80211_chanctx_conf *ctx)
263c74df1c0SLorenzo Bianconi {
264c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
265c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
266c74df1c0SLorenzo Bianconi 
267c74df1c0SLorenzo Bianconi 	mutex_lock(&dev->mt76.mutex);
268f5020655SSean Wang 	mvif->mt76.ctx = ctx;
269c74df1c0SLorenzo Bianconi 	mutex_unlock(&dev->mt76.mutex);
270c74df1c0SLorenzo Bianconi 
271c74df1c0SLorenzo Bianconi 	return 0;
272c74df1c0SLorenzo Bianconi }
273c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_assign_vif_chanctx);
274c74df1c0SLorenzo Bianconi 
mt792x_unassign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,struct ieee80211_chanctx_conf * ctx)275c74df1c0SLorenzo Bianconi void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw,
276c74df1c0SLorenzo Bianconi 				 struct ieee80211_vif *vif,
277c74df1c0SLorenzo Bianconi 				 struct ieee80211_bss_conf *link_conf,
278c74df1c0SLorenzo Bianconi 				 struct ieee80211_chanctx_conf *ctx)
279c74df1c0SLorenzo Bianconi {
280c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
281c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
282c74df1c0SLorenzo Bianconi 
283c74df1c0SLorenzo Bianconi 	mutex_lock(&dev->mt76.mutex);
284f5020655SSean Wang 	mvif->mt76.ctx = NULL;
285c74df1c0SLorenzo Bianconi 	mutex_unlock(&dev->mt76.mutex);
286c74df1c0SLorenzo Bianconi }
287c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_unassign_vif_chanctx);
288c74df1c0SLorenzo Bianconi 
mt792x_set_wakeup(struct ieee80211_hw * hw,bool enabled)289c74df1c0SLorenzo Bianconi void mt792x_set_wakeup(struct ieee80211_hw *hw, bool enabled)
290c74df1c0SLorenzo Bianconi {
291c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
292c74df1c0SLorenzo Bianconi 	struct mt76_dev *mdev = &dev->mt76;
293c74df1c0SLorenzo Bianconi 
294c74df1c0SLorenzo Bianconi 	device_set_wakeup_enable(mdev->dev, enabled);
295c74df1c0SLorenzo Bianconi }
296c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_set_wakeup);
297c74df1c0SLorenzo Bianconi 
298c74df1c0SLorenzo Bianconi static const char mt792x_gstrings_stats[][ETH_GSTRING_LEN] = {
299c74df1c0SLorenzo Bianconi 	/* tx counters */
300c74df1c0SLorenzo Bianconi 	"tx_ampdu_cnt",
301c74df1c0SLorenzo Bianconi 	"tx_mpdu_attempts",
302c74df1c0SLorenzo Bianconi 	"tx_mpdu_success",
303c74df1c0SLorenzo Bianconi 	"tx_pkt_ebf_cnt",
304c74df1c0SLorenzo Bianconi 	"tx_pkt_ibf_cnt",
305c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:0-1",
306c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:2-10",
307c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:11-19",
308c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:20-28",
309c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:29-37",
310c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:38-46",
311c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:47-55",
312c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:56-79",
313c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:80-103",
314c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:104-127",
315c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:128-151",
316c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:152-175",
317c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:176-199",
318c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:200-223",
319c74df1c0SLorenzo Bianconi 	"tx_ampdu_len:224-247",
320c74df1c0SLorenzo Bianconi 	"ba_miss_count",
321c74df1c0SLorenzo Bianconi 	"tx_beamformer_ppdu_iBF",
322c74df1c0SLorenzo Bianconi 	"tx_beamformer_ppdu_eBF",
323c74df1c0SLorenzo Bianconi 	"tx_beamformer_rx_feedback_all",
324c74df1c0SLorenzo Bianconi 	"tx_beamformer_rx_feedback_he",
325c74df1c0SLorenzo Bianconi 	"tx_beamformer_rx_feedback_vht",
326c74df1c0SLorenzo Bianconi 	"tx_beamformer_rx_feedback_ht",
327c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_1",
328c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_2",
329c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_3",
330c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_4",
331c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_5",
332c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_6",
333c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_7",
334c74df1c0SLorenzo Bianconi 	"tx_msdu_pack_8",
335c74df1c0SLorenzo Bianconi 	/* rx counters */
336c74df1c0SLorenzo Bianconi 	"rx_mpdu_cnt",
337c74df1c0SLorenzo Bianconi 	"rx_ampdu_cnt",
338c74df1c0SLorenzo Bianconi 	"rx_ampdu_bytes_cnt",
339c74df1c0SLorenzo Bianconi 	"rx_ba_cnt",
340c74df1c0SLorenzo Bianconi 	/* per vif counters */
341c74df1c0SLorenzo Bianconi 	"v_tx_mode_cck",
342c74df1c0SLorenzo Bianconi 	"v_tx_mode_ofdm",
343c74df1c0SLorenzo Bianconi 	"v_tx_mode_ht",
344c74df1c0SLorenzo Bianconi 	"v_tx_mode_ht_gf",
345c74df1c0SLorenzo Bianconi 	"v_tx_mode_vht",
346c74df1c0SLorenzo Bianconi 	"v_tx_mode_he_su",
347c74df1c0SLorenzo Bianconi 	"v_tx_mode_he_ext_su",
348c74df1c0SLorenzo Bianconi 	"v_tx_mode_he_tb",
349c74df1c0SLorenzo Bianconi 	"v_tx_mode_he_mu",
350c74df1c0SLorenzo Bianconi 	"v_tx_mode_eht_su",
351c74df1c0SLorenzo Bianconi 	"v_tx_mode_eht_trig",
352c74df1c0SLorenzo Bianconi 	"v_tx_mode_eht_mu",
353c74df1c0SLorenzo Bianconi 	"v_tx_bw_20",
354c74df1c0SLorenzo Bianconi 	"v_tx_bw_40",
355c74df1c0SLorenzo Bianconi 	"v_tx_bw_80",
356c74df1c0SLorenzo Bianconi 	"v_tx_bw_160",
357*7b4f9cd6SGen Xu 	"v_tx_bw_320",
358c74df1c0SLorenzo Bianconi 	"v_tx_mcs_0",
359c74df1c0SLorenzo Bianconi 	"v_tx_mcs_1",
360c74df1c0SLorenzo Bianconi 	"v_tx_mcs_2",
361c74df1c0SLorenzo Bianconi 	"v_tx_mcs_3",
362c74df1c0SLorenzo Bianconi 	"v_tx_mcs_4",
363c74df1c0SLorenzo Bianconi 	"v_tx_mcs_5",
364c74df1c0SLorenzo Bianconi 	"v_tx_mcs_6",
365c74df1c0SLorenzo Bianconi 	"v_tx_mcs_7",
366c74df1c0SLorenzo Bianconi 	"v_tx_mcs_8",
367c74df1c0SLorenzo Bianconi 	"v_tx_mcs_9",
368c74df1c0SLorenzo Bianconi 	"v_tx_mcs_10",
369c74df1c0SLorenzo Bianconi 	"v_tx_mcs_11",
370c74df1c0SLorenzo Bianconi 	"v_tx_mcs_12",
371c74df1c0SLorenzo Bianconi 	"v_tx_mcs_13",
372c74df1c0SLorenzo Bianconi 	"v_tx_nss_1",
373c74df1c0SLorenzo Bianconi 	"v_tx_nss_2",
374c74df1c0SLorenzo Bianconi 	"v_tx_nss_3",
375c74df1c0SLorenzo Bianconi 	"v_tx_nss_4",
376c74df1c0SLorenzo Bianconi };
377c74df1c0SLorenzo Bianconi 
mt792x_get_et_strings(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 sset,u8 * data)378c74df1c0SLorenzo Bianconi void mt792x_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
379c74df1c0SLorenzo Bianconi 			   u32 sset, u8 *data)
380c74df1c0SLorenzo Bianconi {
381c74df1c0SLorenzo Bianconi 	if (sset != ETH_SS_STATS)
382c74df1c0SLorenzo Bianconi 		return;
383c74df1c0SLorenzo Bianconi 
38403f0e11dSDmitry Antipov 	memcpy(data, mt792x_gstrings_stats, sizeof(mt792x_gstrings_stats));
385c74df1c0SLorenzo Bianconi 
386c74df1c0SLorenzo Bianconi 	data += sizeof(mt792x_gstrings_stats);
387c74df1c0SLorenzo Bianconi 	page_pool_ethtool_stats_get_strings(data);
388c74df1c0SLorenzo Bianconi }
389c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_et_strings);
390c74df1c0SLorenzo Bianconi 
mt792x_get_et_sset_count(struct ieee80211_hw * hw,struct ieee80211_vif * vif,int sset)391c74df1c0SLorenzo Bianconi int mt792x_get_et_sset_count(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
392c74df1c0SLorenzo Bianconi 			     int sset)
393c74df1c0SLorenzo Bianconi {
394c74df1c0SLorenzo Bianconi 	if (sset != ETH_SS_STATS)
395c74df1c0SLorenzo Bianconi 		return 0;
396c74df1c0SLorenzo Bianconi 
397c74df1c0SLorenzo Bianconi 	return ARRAY_SIZE(mt792x_gstrings_stats) +
398c74df1c0SLorenzo Bianconi 	       page_pool_ethtool_stats_get_count();
399c74df1c0SLorenzo Bianconi }
400c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_et_sset_count);
401c74df1c0SLorenzo Bianconi 
402c74df1c0SLorenzo Bianconi static void
mt792x_ethtool_worker(void * wi_data,struct ieee80211_sta * sta)403c74df1c0SLorenzo Bianconi mt792x_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
404c74df1c0SLorenzo Bianconi {
405c74df1c0SLorenzo Bianconi 	struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
406c74df1c0SLorenzo Bianconi 	struct mt76_ethtool_worker_info *wi = wi_data;
407c74df1c0SLorenzo Bianconi 
408c74df1c0SLorenzo Bianconi 	if (msta->vif->mt76.idx != wi->idx)
409c74df1c0SLorenzo Bianconi 		return;
410c74df1c0SLorenzo Bianconi 
411c74df1c0SLorenzo Bianconi 	mt76_ethtool_worker(wi, &msta->wcid.stats, true);
412c74df1c0SLorenzo Bianconi }
413c74df1c0SLorenzo Bianconi 
mt792x_get_et_stats(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ethtool_stats * stats,u64 * data)414c74df1c0SLorenzo Bianconi void mt792x_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
415c74df1c0SLorenzo Bianconi 			 struct ethtool_stats *stats, u64 *data)
416c74df1c0SLorenzo Bianconi {
417c74df1c0SLorenzo Bianconi 	struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
418c74df1c0SLorenzo Bianconi 	int stats_size = ARRAY_SIZE(mt792x_gstrings_stats);
419c74df1c0SLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
420c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = phy->dev;
421c74df1c0SLorenzo Bianconi 	struct mt76_mib_stats *mib = &phy->mib;
422c74df1c0SLorenzo Bianconi 	struct mt76_ethtool_worker_info wi = {
423c74df1c0SLorenzo Bianconi 		.data = data,
424c74df1c0SLorenzo Bianconi 		.idx = mvif->mt76.idx,
425c74df1c0SLorenzo Bianconi 	};
426c74df1c0SLorenzo Bianconi 	int i, ei = 0;
427c74df1c0SLorenzo Bianconi 
428c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(dev);
429c74df1c0SLorenzo Bianconi 
430c74df1c0SLorenzo Bianconi 	mt792x_mac_update_mib_stats(phy);
431c74df1c0SLorenzo Bianconi 
432c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_ampdu_cnt;
433c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_mpdu_attempts_cnt;
434c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_mpdu_success_cnt;
435c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_pkt_ebf_cnt;
436c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_pkt_ibf_cnt;
437c74df1c0SLorenzo Bianconi 
438c74df1c0SLorenzo Bianconi 	/* Tx ampdu stat */
439c74df1c0SLorenzo Bianconi 	for (i = 0; i < 15; i++)
440c74df1c0SLorenzo Bianconi 		data[ei++] = phy->mt76->aggr_stats[i];
441c74df1c0SLorenzo Bianconi 
442c74df1c0SLorenzo Bianconi 	data[ei++] = phy->mib.ba_miss_cnt;
443c74df1c0SLorenzo Bianconi 
444c74df1c0SLorenzo Bianconi 	/* Tx Beamformer monitor */
445c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_ibf_ppdu_cnt;
446c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_ebf_ppdu_cnt;
447c74df1c0SLorenzo Bianconi 
448c74df1c0SLorenzo Bianconi 	/* Tx Beamformer Rx feedback monitor */
449c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_rx_fb_all_cnt;
450c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_rx_fb_he_cnt;
451c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_rx_fb_vht_cnt;
452c74df1c0SLorenzo Bianconi 	data[ei++] = mib->tx_bf_rx_fb_ht_cnt;
453c74df1c0SLorenzo Bianconi 
454c74df1c0SLorenzo Bianconi 	/* Tx amsdu info (pack-count histogram) */
455c74df1c0SLorenzo Bianconi 	for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++)
456c74df1c0SLorenzo Bianconi 		data[ei++] = mib->tx_amsdu[i];
457c74df1c0SLorenzo Bianconi 
458c74df1c0SLorenzo Bianconi 	/* rx counters */
459c74df1c0SLorenzo Bianconi 	data[ei++] = mib->rx_mpdu_cnt;
460c74df1c0SLorenzo Bianconi 	data[ei++] = mib->rx_ampdu_cnt;
461c74df1c0SLorenzo Bianconi 	data[ei++] = mib->rx_ampdu_bytes_cnt;
462c74df1c0SLorenzo Bianconi 	data[ei++] = mib->rx_ba_cnt;
463c74df1c0SLorenzo Bianconi 
464c74df1c0SLorenzo Bianconi 	/* Add values for all stations owned by this vif */
465c74df1c0SLorenzo Bianconi 	wi.initial_stat_idx = ei;
466c74df1c0SLorenzo Bianconi 	ieee80211_iterate_stations_atomic(hw, mt792x_ethtool_worker, &wi);
467c74df1c0SLorenzo Bianconi 
468c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(dev);
469c74df1c0SLorenzo Bianconi 
470c74df1c0SLorenzo Bianconi 	if (!wi.sta_count)
471c74df1c0SLorenzo Bianconi 		return;
472c74df1c0SLorenzo Bianconi 
473c74df1c0SLorenzo Bianconi 	ei += wi.worker_stat_count;
474c74df1c0SLorenzo Bianconi 
475c74df1c0SLorenzo Bianconi 	mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei);
476c74df1c0SLorenzo Bianconi 	stats_size += page_pool_ethtool_stats_get_count();
477c74df1c0SLorenzo Bianconi 
478c74df1c0SLorenzo Bianconi 	if (ei != stats_size)
479c74df1c0SLorenzo Bianconi 		dev_err(dev->mt76.dev, "ei: %d  SSTATS_LEN: %d", ei,
480c74df1c0SLorenzo Bianconi 			stats_size);
481c74df1c0SLorenzo Bianconi }
482c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_et_stats);
483c74df1c0SLorenzo Bianconi 
mt792x_sta_statistics(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct station_info * sinfo)484c74df1c0SLorenzo Bianconi void mt792x_sta_statistics(struct ieee80211_hw *hw,
485c74df1c0SLorenzo Bianconi 			   struct ieee80211_vif *vif,
486c74df1c0SLorenzo Bianconi 			   struct ieee80211_sta *sta,
487c74df1c0SLorenzo Bianconi 			   struct station_info *sinfo)
488c74df1c0SLorenzo Bianconi {
489c74df1c0SLorenzo Bianconi 	struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
490c74df1c0SLorenzo Bianconi 	struct rate_info *txrate = &msta->wcid.rate;
491c74df1c0SLorenzo Bianconi 
492c74df1c0SLorenzo Bianconi 	if (!txrate->legacy && !txrate->flags)
493c74df1c0SLorenzo Bianconi 		return;
494c74df1c0SLorenzo Bianconi 
495c74df1c0SLorenzo Bianconi 	if (txrate->legacy) {
496c74df1c0SLorenzo Bianconi 		sinfo->txrate.legacy = txrate->legacy;
497c74df1c0SLorenzo Bianconi 	} else {
498c74df1c0SLorenzo Bianconi 		sinfo->txrate.mcs = txrate->mcs;
499c74df1c0SLorenzo Bianconi 		sinfo->txrate.nss = txrate->nss;
500c74df1c0SLorenzo Bianconi 		sinfo->txrate.bw = txrate->bw;
501c74df1c0SLorenzo Bianconi 		sinfo->txrate.he_gi = txrate->he_gi;
502c74df1c0SLorenzo Bianconi 		sinfo->txrate.he_dcm = txrate->he_dcm;
503c74df1c0SLorenzo Bianconi 		sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc;
504c74df1c0SLorenzo Bianconi 	}
505c74df1c0SLorenzo Bianconi 	sinfo->tx_failed = msta->wcid.stats.tx_failed;
506c74df1c0SLorenzo Bianconi 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
507c74df1c0SLorenzo Bianconi 
508c74df1c0SLorenzo Bianconi 	sinfo->tx_retries = msta->wcid.stats.tx_retries;
509c74df1c0SLorenzo Bianconi 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
510c74df1c0SLorenzo Bianconi 
511c74df1c0SLorenzo Bianconi 	sinfo->txrate.flags = txrate->flags;
512c74df1c0SLorenzo Bianconi 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
513c74df1c0SLorenzo Bianconi 
514c74df1c0SLorenzo Bianconi 	sinfo->ack_signal = (s8)msta->ack_signal;
515c74df1c0SLorenzo Bianconi 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
516c74df1c0SLorenzo Bianconi 
517c74df1c0SLorenzo Bianconi 	sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal);
518c74df1c0SLorenzo Bianconi 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
519c74df1c0SLorenzo Bianconi }
520c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_sta_statistics);
521c74df1c0SLorenzo Bianconi 
mt792x_set_coverage_class(struct ieee80211_hw * hw,s16 coverage_class)522c74df1c0SLorenzo Bianconi void mt792x_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
523c74df1c0SLorenzo Bianconi {
524c74df1c0SLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
525c74df1c0SLorenzo Bianconi 	struct mt792x_dev *dev = phy->dev;
526c74df1c0SLorenzo Bianconi 
527c74df1c0SLorenzo Bianconi 	mt792x_mutex_acquire(dev);
528c74df1c0SLorenzo Bianconi 
529c74df1c0SLorenzo Bianconi 	phy->coverage_class = max_t(s16, coverage_class, 0);
530c74df1c0SLorenzo Bianconi 	mt792x_mac_set_timeing(phy);
531c74df1c0SLorenzo Bianconi 
532c74df1c0SLorenzo Bianconi 	mt792x_mutex_release(dev);
533c74df1c0SLorenzo Bianconi }
534c74df1c0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_set_coverage_class);
535c74df1c0SLorenzo Bianconi 
mt792x_init_wiphy(struct ieee80211_hw * hw)536e8a264ccSLorenzo Bianconi int mt792x_init_wiphy(struct ieee80211_hw *hw)
537e8a264ccSLorenzo Bianconi {
538e8a264ccSLorenzo Bianconi 	struct mt792x_phy *phy = mt792x_hw_phy(hw);
539e8a264ccSLorenzo Bianconi 	struct mt792x_dev *dev = phy->dev;
540e8a264ccSLorenzo Bianconi 	struct wiphy *wiphy = hw->wiphy;
541e8a264ccSLorenzo Bianconi 
542e8a264ccSLorenzo Bianconi 	hw->queues = 4;
543e8a264ccSLorenzo Bianconi 	if (dev->has_eht) {
544e8a264ccSLorenzo Bianconi 		hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT;
545e8a264ccSLorenzo Bianconi 		hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT;
546e8a264ccSLorenzo Bianconi 	} else {
547e8a264ccSLorenzo Bianconi 		hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
548e8a264ccSLorenzo Bianconi 		hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
549e8a264ccSLorenzo Bianconi 	}
550e8a264ccSLorenzo Bianconi 	hw->netdev_features = NETIF_F_RXCSUM;
551e8a264ccSLorenzo Bianconi 
552e8a264ccSLorenzo Bianconi 	hw->radiotap_timestamp.units_pos =
553e8a264ccSLorenzo Bianconi 		IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
554e8a264ccSLorenzo Bianconi 
555e8a264ccSLorenzo Bianconi 	phy->slottime = 9;
556e8a264ccSLorenzo Bianconi 
557e8a264ccSLorenzo Bianconi 	hw->sta_data_size = sizeof(struct mt792x_sta);
558e8a264ccSLorenzo Bianconi 	hw->vif_data_size = sizeof(struct mt792x_vif);
559e8a264ccSLorenzo Bianconi 
560e8a264ccSLorenzo Bianconi 	if (dev->fw_features & MT792x_FW_CAP_CNM) {
561e8a264ccSLorenzo Bianconi 		wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
562e8a264ccSLorenzo Bianconi 		wiphy->iface_combinations = if_comb_chanctx;
563e8a264ccSLorenzo Bianconi 		wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx);
564e8a264ccSLorenzo Bianconi 	} else {
565e8a264ccSLorenzo Bianconi 		wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
566e8a264ccSLorenzo Bianconi 		wiphy->iface_combinations = if_comb;
567e8a264ccSLorenzo Bianconi 		wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
568e8a264ccSLorenzo Bianconi 	}
569e8a264ccSLorenzo Bianconi 	wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP |
570e8a264ccSLorenzo Bianconi 			  WIPHY_FLAG_4ADDR_STATION);
571e8a264ccSLorenzo Bianconi 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
572e8a264ccSLorenzo Bianconi 				 BIT(NL80211_IFTYPE_AP) |
573e8a264ccSLorenzo Bianconi 				 BIT(NL80211_IFTYPE_P2P_CLIENT) |
574e8a264ccSLorenzo Bianconi 				 BIT(NL80211_IFTYPE_P2P_GO);
575e8a264ccSLorenzo Bianconi 	wiphy->max_remain_on_channel_duration = 5000;
576e8a264ccSLorenzo Bianconi 	wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN;
577e8a264ccSLorenzo Bianconi 	wiphy->max_scan_ssids = 4;
578e8a264ccSLorenzo Bianconi 	wiphy->max_sched_scan_plan_interval =
579e8a264ccSLorenzo Bianconi 		MT76_CONNAC_MAX_TIME_SCHED_SCAN_INTERVAL;
580e8a264ccSLorenzo Bianconi 	wiphy->max_sched_scan_ie_len = IEEE80211_MAX_DATA_LEN;
581e8a264ccSLorenzo Bianconi 	wiphy->max_sched_scan_ssids = MT76_CONNAC_MAX_SCHED_SCAN_SSID;
582e8a264ccSLorenzo Bianconi 	wiphy->max_match_sets = MT76_CONNAC_MAX_SCAN_MATCH;
583e8a264ccSLorenzo Bianconi 	wiphy->max_sched_scan_reqs = 1;
584e8a264ccSLorenzo Bianconi 	wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH |
585e8a264ccSLorenzo Bianconi 			WIPHY_FLAG_SPLIT_SCAN_6GHZ;
586e8a264ccSLorenzo Bianconi 
587e8a264ccSLorenzo Bianconi 	wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
588e8a264ccSLorenzo Bianconi 			   NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
589e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
590e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
591e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT);
592e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT);
593e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE);
594e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
595e8a264ccSLorenzo Bianconi 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
596e8a264ccSLorenzo Bianconi 
597e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
598e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
599e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
600e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
601e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, WANT_MONITOR_VIF);
602e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_PS);
603e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
604e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
605e8a264ccSLorenzo Bianconi 	ieee80211_hw_set(hw, CONNECTION_MONITOR);
606e8a264ccSLorenzo Bianconi 
607e8a264ccSLorenzo Bianconi 	if (dev->pm.enable)
608e8a264ccSLorenzo Bianconi 		ieee80211_hw_set(hw, CONNECTION_MONITOR);
609e8a264ccSLorenzo Bianconi 
610e8a264ccSLorenzo Bianconi 	hw->max_tx_fragments = 4;
611e8a264ccSLorenzo Bianconi 
612e8a264ccSLorenzo Bianconi 	return 0;
613e8a264ccSLorenzo Bianconi }
614e8a264ccSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_init_wiphy);
615e8a264ccSLorenzo Bianconi 
616e8a264ccSLorenzo Bianconi static u8
mt792x_get_offload_capability(struct device * dev,const char * fw_wm)617e8a264ccSLorenzo Bianconi mt792x_get_offload_capability(struct device *dev, const char *fw_wm)
618e8a264ccSLorenzo Bianconi {
619e8a264ccSLorenzo Bianconi 	const struct mt76_connac2_fw_trailer *hdr;
620e8a264ccSLorenzo Bianconi 	struct mt792x_realease_info *rel_info;
621e8a264ccSLorenzo Bianconi 	const struct firmware *fw;
622e8a264ccSLorenzo Bianconi 	int ret, i, offset = 0;
623e8a264ccSLorenzo Bianconi 	const u8 *data, *end;
624e8a264ccSLorenzo Bianconi 	u8 offload_caps = 0;
625e8a264ccSLorenzo Bianconi 
626e8a264ccSLorenzo Bianconi 	ret = request_firmware(&fw, fw_wm, dev);
627e8a264ccSLorenzo Bianconi 	if (ret)
628e8a264ccSLorenzo Bianconi 		return ret;
629e8a264ccSLorenzo Bianconi 
630e8a264ccSLorenzo Bianconi 	if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
631e8a264ccSLorenzo Bianconi 		dev_err(dev, "Invalid firmware\n");
632e8a264ccSLorenzo Bianconi 		goto out;
633e8a264ccSLorenzo Bianconi 	}
634e8a264ccSLorenzo Bianconi 
635e8a264ccSLorenzo Bianconi 	data = fw->data;
636e8a264ccSLorenzo Bianconi 	hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
637e8a264ccSLorenzo Bianconi 
638e8a264ccSLorenzo Bianconi 	for (i = 0; i < hdr->n_region; i++) {
639e8a264ccSLorenzo Bianconi 		const struct mt76_connac2_fw_region *region;
640e8a264ccSLorenzo Bianconi 
641e8a264ccSLorenzo Bianconi 		region = (const void *)((const u8 *)hdr -
642e8a264ccSLorenzo Bianconi 					(hdr->n_region - i) * sizeof(*region));
643e8a264ccSLorenzo Bianconi 		offset += le32_to_cpu(region->len);
644e8a264ccSLorenzo Bianconi 	}
645e8a264ccSLorenzo Bianconi 
646e8a264ccSLorenzo Bianconi 	data += offset + 16;
647e8a264ccSLorenzo Bianconi 	rel_info = (struct mt792x_realease_info *)data;
648e8a264ccSLorenzo Bianconi 	data += sizeof(*rel_info);
649e8a264ccSLorenzo Bianconi 	end = data + le16_to_cpu(rel_info->len);
650e8a264ccSLorenzo Bianconi 
651e8a264ccSLorenzo Bianconi 	while (data < end) {
652e8a264ccSLorenzo Bianconi 		rel_info = (struct mt792x_realease_info *)data;
653e8a264ccSLorenzo Bianconi 		data += sizeof(*rel_info);
654e8a264ccSLorenzo Bianconi 
655e8a264ccSLorenzo Bianconi 		if (rel_info->tag == MT792x_FW_TAG_FEATURE) {
656e8a264ccSLorenzo Bianconi 			struct mt792x_fw_features *features;
657e8a264ccSLorenzo Bianconi 
658e8a264ccSLorenzo Bianconi 			features = (struct mt792x_fw_features *)data;
659e8a264ccSLorenzo Bianconi 			offload_caps = features->data;
660e8a264ccSLorenzo Bianconi 			break;
661e8a264ccSLorenzo Bianconi 		}
662e8a264ccSLorenzo Bianconi 
663e8a264ccSLorenzo Bianconi 		data += le16_to_cpu(rel_info->len) + rel_info->pad_len;
664e8a264ccSLorenzo Bianconi 	}
665e8a264ccSLorenzo Bianconi 
666e8a264ccSLorenzo Bianconi out:
667e8a264ccSLorenzo Bianconi 	release_firmware(fw);
668e8a264ccSLorenzo Bianconi 
669e8a264ccSLorenzo Bianconi 	return offload_caps;
670e8a264ccSLorenzo Bianconi }
671e8a264ccSLorenzo Bianconi 
672e8a264ccSLorenzo Bianconi struct ieee80211_ops *
mt792x_get_mac80211_ops(struct device * dev,const struct ieee80211_ops * mac80211_ops,void * drv_data,u8 * fw_features)673e8a264ccSLorenzo Bianconi mt792x_get_mac80211_ops(struct device *dev,
674e8a264ccSLorenzo Bianconi 			const struct ieee80211_ops *mac80211_ops,
675e8a264ccSLorenzo Bianconi 			void *drv_data, u8 *fw_features)
676e8a264ccSLorenzo Bianconi {
677e8a264ccSLorenzo Bianconi 	struct ieee80211_ops *ops;
678e8a264ccSLorenzo Bianconi 
679e8a264ccSLorenzo Bianconi 	ops = devm_kmemdup(dev, mac80211_ops, sizeof(struct ieee80211_ops),
680e8a264ccSLorenzo Bianconi 			   GFP_KERNEL);
681e8a264ccSLorenzo Bianconi 	if (!ops)
682e8a264ccSLorenzo Bianconi 		return NULL;
683e8a264ccSLorenzo Bianconi 
684e8a264ccSLorenzo Bianconi 	*fw_features = mt792x_get_offload_capability(dev, drv_data);
685e8a264ccSLorenzo Bianconi 	if (!(*fw_features & MT792x_FW_CAP_CNM)) {
686e8a264ccSLorenzo Bianconi 		ops->remain_on_channel = NULL;
687e8a264ccSLorenzo Bianconi 		ops->cancel_remain_on_channel = NULL;
6880a44dfc0SJohannes Berg 		ops->add_chanctx = ieee80211_emulate_add_chanctx;
6890a44dfc0SJohannes Berg 		ops->remove_chanctx = ieee80211_emulate_remove_chanctx;
6900a44dfc0SJohannes Berg 		ops->change_chanctx = ieee80211_emulate_change_chanctx;
6910a44dfc0SJohannes Berg 		ops->switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx;
692e8a264ccSLorenzo Bianconi 		ops->assign_vif_chanctx = NULL;
693e8a264ccSLorenzo Bianconi 		ops->unassign_vif_chanctx = NULL;
694e8a264ccSLorenzo Bianconi 		ops->mgd_prepare_tx = NULL;
695e8a264ccSLorenzo Bianconi 		ops->mgd_complete_tx = NULL;
696e8a264ccSLorenzo Bianconi 	}
697e8a264ccSLorenzo Bianconi 	return ops;
698e8a264ccSLorenzo Bianconi }
699e8a264ccSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_get_mac80211_ops);
700e8a264ccSLorenzo Bianconi 
mt792x_init_wcid(struct mt792x_dev * dev)701e8a264ccSLorenzo Bianconi int mt792x_init_wcid(struct mt792x_dev *dev)
702e8a264ccSLorenzo Bianconi {
703e8a264ccSLorenzo Bianconi 	int idx;
704e8a264ccSLorenzo Bianconi 
705e8a264ccSLorenzo Bianconi 	/* Beacon and mgmt frames should occupy wcid 0 */
706e8a264ccSLorenzo Bianconi 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
707e8a264ccSLorenzo Bianconi 	if (idx)
708e8a264ccSLorenzo Bianconi 		return -ENOSPC;
709e8a264ccSLorenzo Bianconi 
710e8a264ccSLorenzo Bianconi 	dev->mt76.global_wcid.idx = idx;
711e8a264ccSLorenzo Bianconi 	dev->mt76.global_wcid.hw_key_idx = -1;
712e8a264ccSLorenzo Bianconi 	dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET;
713e8a264ccSLorenzo Bianconi 	rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid);
714e8a264ccSLorenzo Bianconi 
715e8a264ccSLorenzo Bianconi 	return 0;
716e8a264ccSLorenzo Bianconi }
717e8a264ccSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_init_wcid);
718e8a264ccSLorenzo Bianconi 
mt792x_mcu_drv_pmctrl(struct mt792x_dev * dev)719c21a7f9fSLorenzo Bianconi int mt792x_mcu_drv_pmctrl(struct mt792x_dev *dev)
720c21a7f9fSLorenzo Bianconi {
721c21a7f9fSLorenzo Bianconi 	struct mt76_phy *mphy = &dev->mt76.phy;
722c21a7f9fSLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
723c21a7f9fSLorenzo Bianconi 	int err = 0;
724c21a7f9fSLorenzo Bianconi 
725c21a7f9fSLorenzo Bianconi 	mutex_lock(&pm->mutex);
726c21a7f9fSLorenzo Bianconi 
727c21a7f9fSLorenzo Bianconi 	if (!test_bit(MT76_STATE_PM, &mphy->state))
728c21a7f9fSLorenzo Bianconi 		goto out;
729c21a7f9fSLorenzo Bianconi 
730c21a7f9fSLorenzo Bianconi 	err = __mt792x_mcu_drv_pmctrl(dev);
731c21a7f9fSLorenzo Bianconi out:
732c21a7f9fSLorenzo Bianconi 	mutex_unlock(&pm->mutex);
733c21a7f9fSLorenzo Bianconi 
734c21a7f9fSLorenzo Bianconi 	if (err)
735c21a7f9fSLorenzo Bianconi 		mt792x_reset(&dev->mt76);
736c21a7f9fSLorenzo Bianconi 
737c21a7f9fSLorenzo Bianconi 	return err;
738c21a7f9fSLorenzo Bianconi }
739c21a7f9fSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_mcu_drv_pmctrl);
740c21a7f9fSLorenzo Bianconi 
mt792x_mcu_fw_pmctrl(struct mt792x_dev * dev)741c21a7f9fSLorenzo Bianconi int mt792x_mcu_fw_pmctrl(struct mt792x_dev *dev)
742c21a7f9fSLorenzo Bianconi {
743c21a7f9fSLorenzo Bianconi 	struct mt76_phy *mphy = &dev->mt76.phy;
744c21a7f9fSLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
745c21a7f9fSLorenzo Bianconi 	int err = 0;
746c21a7f9fSLorenzo Bianconi 
747c21a7f9fSLorenzo Bianconi 	mutex_lock(&pm->mutex);
748c21a7f9fSLorenzo Bianconi 
749c21a7f9fSLorenzo Bianconi 	if (mt76_connac_skip_fw_pmctrl(mphy, pm))
750c21a7f9fSLorenzo Bianconi 		goto out;
751c21a7f9fSLorenzo Bianconi 
752c21a7f9fSLorenzo Bianconi 	err = __mt792x_mcu_fw_pmctrl(dev);
753c21a7f9fSLorenzo Bianconi out:
754c21a7f9fSLorenzo Bianconi 	mutex_unlock(&pm->mutex);
755c21a7f9fSLorenzo Bianconi 
756c21a7f9fSLorenzo Bianconi 	if (err)
757c21a7f9fSLorenzo Bianconi 		mt792x_reset(&dev->mt76);
758c21a7f9fSLorenzo Bianconi 
759c21a7f9fSLorenzo Bianconi 	return err;
760c21a7f9fSLorenzo Bianconi }
761c21a7f9fSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_mcu_fw_pmctrl);
762c21a7f9fSLorenzo Bianconi 
__mt792xe_mcu_drv_pmctrl(struct mt792x_dev * dev)7631c025496SLorenzo Bianconi int __mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev)
7641c025496SLorenzo Bianconi {
7651c025496SLorenzo Bianconi 	int i, err = 0;
7661c025496SLorenzo Bianconi 
7671c025496SLorenzo Bianconi 	for (i = 0; i < MT792x_DRV_OWN_RETRY_COUNT; i++) {
7681c025496SLorenzo Bianconi 		mt76_wr(dev, MT_CONN_ON_LPCTL, PCIE_LPCR_HOST_CLR_OWN);
7691c025496SLorenzo Bianconi 		if (mt76_poll_msec_tick(dev, MT_CONN_ON_LPCTL,
7701c025496SLorenzo Bianconi 					PCIE_LPCR_HOST_OWN_SYNC, 0, 50, 1))
7711c025496SLorenzo Bianconi 			break;
7721c025496SLorenzo Bianconi 	}
7731c025496SLorenzo Bianconi 
7741c025496SLorenzo Bianconi 	if (i == MT792x_DRV_OWN_RETRY_COUNT) {
7751c025496SLorenzo Bianconi 		dev_err(dev->mt76.dev, "driver own failed\n");
7761c025496SLorenzo Bianconi 		err = -EIO;
7771c025496SLorenzo Bianconi 	}
7781c025496SLorenzo Bianconi 
7791c025496SLorenzo Bianconi 	return err;
7801c025496SLorenzo Bianconi }
7811c025496SLorenzo Bianconi EXPORT_SYMBOL_GPL(__mt792xe_mcu_drv_pmctrl);
7821c025496SLorenzo Bianconi 
mt792xe_mcu_drv_pmctrl(struct mt792x_dev * dev)7831c025496SLorenzo Bianconi int mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev)
7841c025496SLorenzo Bianconi {
7851c025496SLorenzo Bianconi 	struct mt76_phy *mphy = &dev->mt76.phy;
7861c025496SLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
7871c025496SLorenzo Bianconi 	int err;
7881c025496SLorenzo Bianconi 
7891c025496SLorenzo Bianconi 	err = __mt792xe_mcu_drv_pmctrl(dev);
7901c025496SLorenzo Bianconi 	if (err < 0)
7911c025496SLorenzo Bianconi 		goto out;
7921c025496SLorenzo Bianconi 
7931c025496SLorenzo Bianconi 	mt792x_wpdma_reinit_cond(dev);
7941c025496SLorenzo Bianconi 	clear_bit(MT76_STATE_PM, &mphy->state);
7951c025496SLorenzo Bianconi 
7961c025496SLorenzo Bianconi 	pm->stats.last_wake_event = jiffies;
7971c025496SLorenzo Bianconi 	pm->stats.doze_time += pm->stats.last_wake_event -
7981c025496SLorenzo Bianconi 			       pm->stats.last_doze_event;
7991c025496SLorenzo Bianconi out:
8001c025496SLorenzo Bianconi 	return err;
8011c025496SLorenzo Bianconi }
8021c025496SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792xe_mcu_drv_pmctrl);
8031c025496SLorenzo Bianconi 
mt792xe_mcu_fw_pmctrl(struct mt792x_dev * dev)8041c025496SLorenzo Bianconi int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev)
8051c025496SLorenzo Bianconi {
8061c025496SLorenzo Bianconi 	struct mt76_phy *mphy = &dev->mt76.phy;
8071c025496SLorenzo Bianconi 	struct mt76_connac_pm *pm = &dev->pm;
8081c025496SLorenzo Bianconi 	int i;
8091c025496SLorenzo Bianconi 
8101c025496SLorenzo Bianconi 	for (i = 0; i < MT792x_DRV_OWN_RETRY_COUNT; i++) {
8111c025496SLorenzo Bianconi 		mt76_wr(dev, MT_CONN_ON_LPCTL, PCIE_LPCR_HOST_SET_OWN);
8121c025496SLorenzo Bianconi 		if (mt76_poll_msec_tick(dev, MT_CONN_ON_LPCTL,
8131c025496SLorenzo Bianconi 					PCIE_LPCR_HOST_OWN_SYNC, 4, 50, 1))
8141c025496SLorenzo Bianconi 			break;
8151c025496SLorenzo Bianconi 	}
8161c025496SLorenzo Bianconi 
8171c025496SLorenzo Bianconi 	if (i == MT792x_DRV_OWN_RETRY_COUNT) {
8181c025496SLorenzo Bianconi 		dev_err(dev->mt76.dev, "firmware own failed\n");
8191c025496SLorenzo Bianconi 		clear_bit(MT76_STATE_PM, &mphy->state);
8201c025496SLorenzo Bianconi 		return -EIO;
8211c025496SLorenzo Bianconi 	}
8221c025496SLorenzo Bianconi 
8231c025496SLorenzo Bianconi 	pm->stats.last_doze_event = jiffies;
8241c025496SLorenzo Bianconi 	pm->stats.awake_time += pm->stats.last_doze_event -
8251c025496SLorenzo Bianconi 				pm->stats.last_wake_event;
8261c025496SLorenzo Bianconi 
8271c025496SLorenzo Bianconi 	return 0;
8281c025496SLorenzo Bianconi }
8291c025496SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792xe_mcu_fw_pmctrl);
8301c025496SLorenzo Bianconi 
mt792x_load_firmware(struct mt792x_dev * dev)831583204aeSLorenzo Bianconi int mt792x_load_firmware(struct mt792x_dev *dev)
832583204aeSLorenzo Bianconi {
833583204aeSLorenzo Bianconi 	int ret;
834583204aeSLorenzo Bianconi 
835583204aeSLorenzo Bianconi 	ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev));
836583204aeSLorenzo Bianconi 	if (ret)
837583204aeSLorenzo Bianconi 		return ret;
838583204aeSLorenzo Bianconi 
839583204aeSLorenzo Bianconi 	if (mt76_is_sdio(&dev->mt76)) {
840583204aeSLorenzo Bianconi 		/* activate again */
841583204aeSLorenzo Bianconi 		ret = __mt792x_mcu_fw_pmctrl(dev);
842583204aeSLorenzo Bianconi 		if (!ret)
843583204aeSLorenzo Bianconi 			ret = __mt792x_mcu_drv_pmctrl(dev);
844583204aeSLorenzo Bianconi 	}
845583204aeSLorenzo Bianconi 
846583204aeSLorenzo Bianconi 	ret = mt76_connac2_load_ram(&dev->mt76, mt792x_ram_name(dev), NULL);
847583204aeSLorenzo Bianconi 	if (ret)
848583204aeSLorenzo Bianconi 		return ret;
849583204aeSLorenzo Bianconi 
850583204aeSLorenzo Bianconi 	if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY,
851583204aeSLorenzo Bianconi 			    MT_TOP_MISC2_FW_N9_RDY, 1500)) {
852583204aeSLorenzo Bianconi 		dev_err(dev->mt76.dev, "Timeout for initializing firmware\n");
853583204aeSLorenzo Bianconi 
854583204aeSLorenzo Bianconi 		return -EIO;
855583204aeSLorenzo Bianconi 	}
856583204aeSLorenzo Bianconi 
857583204aeSLorenzo Bianconi #ifdef CONFIG_PM
858583204aeSLorenzo Bianconi 	dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support;
859583204aeSLorenzo Bianconi #endif /* CONFIG_PM */
860583204aeSLorenzo Bianconi 
861583204aeSLorenzo Bianconi 	dev_dbg(dev->mt76.dev, "Firmware init done\n");
862583204aeSLorenzo Bianconi 
863583204aeSLorenzo Bianconi 	return 0;
864583204aeSLorenzo Bianconi }
865583204aeSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt792x_load_firmware);
866583204aeSLorenzo Bianconi 
867f3f8f050SBreno Leitao MODULE_DESCRIPTION("MediaTek MT792x core driver");
868c74df1c0SLorenzo Bianconi MODULE_LICENSE("Dual BSD/GPL");
869c74df1c0SLorenzo Bianconi MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
870