xref: /linux/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c (revision 02680c23d7b3febe45ea3d4f9818c2b2dc89020a)
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 (!pm->enable)
11 		return 0;
12 
13 	if (!mt76_is_mmio(dev))
14 		return 0;
15 
16 	cancel_delayed_work_sync(&pm->ps_work);
17 	if (!test_bit(MT76_STATE_PM, &phy->state))
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_mmio(dev))
38 		return;
39 
40 	if (!pm->enable)
41 		return;
42 
43 	pm->last_activity = jiffies;
44 
45 	if (!test_bit(MT76_STATE_PM, &phy->state)) {
46 		cancel_delayed_work(&phy->mac_work);
47 		queue_delayed_work(dev->wq, &pm->ps_work, pm->idle_timeout);
48 	}
49 }
50 EXPORT_SYMBOL_GPL(mt76_connac_power_save_sched);
51 
52 void mt76_connac_free_pending_tx_skbs(struct mt76_connac_pm *pm,
53 				      struct mt76_wcid *wcid)
54 {
55 	int i;
56 
57 	spin_lock_bh(&pm->txq_lock);
58 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
59 		if (wcid && pm->tx_q[i].wcid != wcid)
60 			continue;
61 
62 		dev_kfree_skb(pm->tx_q[i].skb);
63 		pm->tx_q[i].skb = NULL;
64 	}
65 	spin_unlock_bh(&pm->txq_lock);
66 }
67 EXPORT_SYMBOL_GPL(mt76_connac_free_pending_tx_skbs);
68 
69 void mt76_connac_pm_queue_skb(struct ieee80211_hw *hw,
70 			      struct mt76_connac_pm *pm,
71 			      struct mt76_wcid *wcid,
72 			      struct sk_buff *skb)
73 {
74 	int qid = skb_get_queue_mapping(skb);
75 	struct mt76_phy *phy = hw->priv;
76 
77 	spin_lock_bh(&pm->txq_lock);
78 	if (!pm->tx_q[qid].skb) {
79 		ieee80211_stop_queues(hw);
80 		pm->tx_q[qid].wcid = wcid;
81 		pm->tx_q[qid].skb = skb;
82 		queue_work(phy->dev->wq, &pm->wake_work);
83 	} else {
84 		dev_kfree_skb(skb);
85 	}
86 	spin_unlock_bh(&pm->txq_lock);
87 }
88 EXPORT_SYMBOL_GPL(mt76_connac_pm_queue_skb);
89 
90 void mt76_connac_pm_dequeue_skbs(struct mt76_phy *phy,
91 				 struct mt76_connac_pm *pm)
92 {
93 	int i;
94 
95 	spin_lock_bh(&pm->txq_lock);
96 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
97 		struct mt76_wcid *wcid = pm->tx_q[i].wcid;
98 		struct ieee80211_sta *sta = NULL;
99 
100 		if (!pm->tx_q[i].skb)
101 			continue;
102 
103 		if (wcid && wcid->sta)
104 			sta = container_of((void *)wcid, struct ieee80211_sta,
105 					   drv_priv);
106 
107 		mt76_tx(phy, sta, wcid, pm->tx_q[i].skb);
108 		pm->tx_q[i].skb = NULL;
109 	}
110 	spin_unlock_bh(&pm->txq_lock);
111 
112 	mt76_worker_schedule(&phy->dev->tx_worker);
113 }
114 EXPORT_SYMBOL_GPL(mt76_connac_pm_dequeue_skbs);
115