xref: /linux/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: ISC
2 /* Copyright (C) 2020 MediaTek Inc. */
3 
4 #include "mt76_connac.h"
5 
6 int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm)
7 {
8 	struct mt76_dev *dev = phy->dev;
9 
10 	if (mt76_is_usb(dev))
11 		return 0;
12 
13 	cancel_delayed_work_sync(&pm->ps_work);
14 	if (!test_bit(MT76_STATE_PM, &phy->state))
15 		return 0;
16 
17 	if (pm->suspended)
18 		return 0;
19 
20 	queue_work(dev->wq, &pm->wake_work);
21 	if (!wait_event_timeout(pm->wait,
22 				!test_bit(MT76_STATE_PM, &phy->state),
23 				3 * HZ)) {
24 		ieee80211_wake_queues(phy->hw);
25 		return -ETIMEDOUT;
26 	}
27 
28 	return 0;
29 }
30 EXPORT_SYMBOL_GPL(mt76_connac_pm_wake);
31 
32 void mt76_connac_power_save_sched(struct mt76_phy *phy,
33 				  struct mt76_connac_pm *pm)
34 {
35 	struct mt76_dev *dev = phy->dev;
36 
37 	if (mt76_is_usb(dev))
38 		return;
39 
40 	if (!pm->enable)
41 		return;
42 
43 	if (pm->suspended)
44 		return;
45 
46 	pm->last_activity = jiffies;
47 
48 	if (!test_bit(MT76_STATE_PM, &phy->state)) {
49 		cancel_delayed_work(&phy->mac_work);
50 		queue_delayed_work(dev->wq, &pm->ps_work, pm->idle_timeout);
51 	}
52 }
53 EXPORT_SYMBOL_GPL(mt76_connac_power_save_sched);
54 
55 void mt76_connac_free_pending_tx_skbs(struct mt76_connac_pm *pm,
56 				      struct mt76_wcid *wcid)
57 {
58 	int i;
59 
60 	spin_lock_bh(&pm->txq_lock);
61 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
62 		if (wcid && pm->tx_q[i].wcid != wcid)
63 			continue;
64 
65 		dev_kfree_skb(pm->tx_q[i].skb);
66 		pm->tx_q[i].skb = NULL;
67 	}
68 	spin_unlock_bh(&pm->txq_lock);
69 }
70 EXPORT_SYMBOL_GPL(mt76_connac_free_pending_tx_skbs);
71 
72 void mt76_connac_pm_queue_skb(struct ieee80211_hw *hw,
73 			      struct mt76_connac_pm *pm,
74 			      struct mt76_wcid *wcid,
75 			      struct sk_buff *skb)
76 {
77 	int qid = skb_get_queue_mapping(skb);
78 	struct mt76_phy *phy = hw->priv;
79 
80 	spin_lock_bh(&pm->txq_lock);
81 	if (!pm->tx_q[qid].skb) {
82 		ieee80211_stop_queues(hw);
83 		pm->tx_q[qid].wcid = wcid;
84 		pm->tx_q[qid].skb = skb;
85 		queue_work(phy->dev->wq, &pm->wake_work);
86 	} else {
87 		dev_kfree_skb(skb);
88 	}
89 	spin_unlock_bh(&pm->txq_lock);
90 }
91 EXPORT_SYMBOL_GPL(mt76_connac_pm_queue_skb);
92 
93 void mt76_connac_pm_dequeue_skbs(struct mt76_phy *phy,
94 				 struct mt76_connac_pm *pm)
95 {
96 	int i;
97 
98 	spin_lock_bh(&pm->txq_lock);
99 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
100 		struct mt76_wcid *wcid = pm->tx_q[i].wcid;
101 		struct ieee80211_sta *sta = NULL;
102 
103 		if (!pm->tx_q[i].skb)
104 			continue;
105 
106 		if (wcid && wcid->sta)
107 			sta = container_of((void *)wcid, struct ieee80211_sta,
108 					   drv_priv);
109 
110 		mt76_tx(phy, sta, wcid, pm->tx_q[i].skb);
111 		pm->tx_q[i].skb = NULL;
112 	}
113 	spin_unlock_bh(&pm->txq_lock);
114 
115 	mt76_worker_schedule(&phy->dev->tx_worker);
116 }
117 EXPORT_SYMBOL_GPL(mt76_connac_pm_dequeue_skbs);
118