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 	if (!test_bit(MT76_STATE_PM, &phy->state))
17 		return 0;
18 
19 	if (test_bit(MT76_HW_SCANNING, &phy->state) ||
20 	    test_bit(MT76_HW_SCHED_SCANNING, &phy->state))
21 		return 0;
22 
23 	if (queue_work(dev->wq, &pm->wake_work))
24 		reinit_completion(&pm->wake_cmpl);
25 
26 	if (!wait_for_completion_timeout(&pm->wake_cmpl, 3 * HZ)) {
27 		ieee80211_wake_queues(phy->hw);
28 		return -ETIMEDOUT;
29 	}
30 
31 	return 0;
32 }
33 EXPORT_SYMBOL_GPL(mt76_connac_pm_wake);
34 
35 void mt76_connac_power_save_sched(struct mt76_phy *phy,
36 				  struct mt76_connac_pm *pm)
37 {
38 	struct mt76_dev *dev = phy->dev;
39 
40 	if (!mt76_is_mmio(dev))
41 		return;
42 
43 	if (!pm->enable || !test_bit(MT76_STATE_RUNNING, &phy->state))
44 		return;
45 
46 	pm->last_activity = jiffies;
47 
48 	if (test_bit(MT76_HW_SCANNING, &phy->state) ||
49 	    test_bit(MT76_HW_SCHED_SCANNING, &phy->state))
50 		return;
51 
52 	if (!test_bit(MT76_STATE_PM, &phy->state))
53 		queue_delayed_work(dev->wq, &pm->ps_work, pm->idle_timeout);
54 }
55 EXPORT_SYMBOL_GPL(mt76_connac_power_save_sched);
56 
57 void mt76_connac_free_pending_tx_skbs(struct mt76_connac_pm *pm,
58 				      struct mt76_wcid *wcid)
59 {
60 	int i;
61 
62 	spin_lock_bh(&pm->txq_lock);
63 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
64 		if (wcid && pm->tx_q[i].wcid != wcid)
65 			continue;
66 
67 		dev_kfree_skb(pm->tx_q[i].skb);
68 		pm->tx_q[i].skb = NULL;
69 	}
70 	spin_unlock_bh(&pm->txq_lock);
71 }
72 EXPORT_SYMBOL_GPL(mt76_connac_free_pending_tx_skbs);
73 
74 void mt76_connac_pm_queue_skb(struct ieee80211_hw *hw,
75 			      struct mt76_connac_pm *pm,
76 			      struct mt76_wcid *wcid,
77 			      struct sk_buff *skb)
78 {
79 	int qid = skb_get_queue_mapping(skb);
80 	struct mt76_phy *phy = hw->priv;
81 
82 	spin_lock_bh(&pm->txq_lock);
83 	if (!pm->tx_q[qid].skb) {
84 		ieee80211_stop_queues(hw);
85 		pm->tx_q[qid].wcid = wcid;
86 		pm->tx_q[qid].skb = skb;
87 		queue_work(phy->dev->wq, &pm->wake_work);
88 	} else {
89 		dev_kfree_skb(skb);
90 	}
91 	spin_unlock_bh(&pm->txq_lock);
92 }
93 EXPORT_SYMBOL_GPL(mt76_connac_pm_queue_skb);
94 
95 void mt76_connac_pm_dequeue_skbs(struct mt76_phy *phy,
96 				 struct mt76_connac_pm *pm)
97 {
98 	int i;
99 
100 	spin_lock_bh(&pm->txq_lock);
101 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
102 		struct mt76_wcid *wcid = pm->tx_q[i].wcid;
103 		struct ieee80211_sta *sta = NULL;
104 
105 		if (!pm->tx_q[i].skb)
106 			continue;
107 
108 		if (wcid && wcid->sta)
109 			sta = container_of((void *)wcid, struct ieee80211_sta,
110 					   drv_priv);
111 
112 		mt76_tx(phy, sta, wcid, pm->tx_q[i].skb);
113 		pm->tx_q[i].skb = NULL;
114 	}
115 	spin_unlock_bh(&pm->txq_lock);
116 
117 	mt76_worker_schedule(&phy->dev->tx_worker);
118 }
119 EXPORT_SYMBOL_GPL(mt76_connac_pm_dequeue_skbs);
120