summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h1
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c77
3 files changed, 75 insertions, 6 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 640061276d76..dc160d9ae537 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -539,6 +539,7 @@ struct mt76_hw_cap {
#define MT_DRV_HW_MGMT_TXQ BIT(4)
#define MT_DRV_AMSDU_OFFLOAD BIT(5)
#define MT_DRV_IGNORE_TXS_FAILED BIT(6)
+#define MT_DRV_HW_PS_BUFFERING BIT(7)
struct mt76_driver_ops {
u32 drv_flags;
@@ -1541,6 +1542,8 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
u16 tids, int nframes,
enum ieee80211_frame_release_type reason,
bool more_data);
+void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ bool ps);
bool mt76_has_tx_pending(struct mt76_phy *phy);
int mt76_update_channel(struct mt76_phy *phy);
void mt76_update_survey(struct mt76_phy *phy);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 51380849d24e..0ededa569e9a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1094,6 +1094,7 @@ enum {
MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
MCU_UNI_EVENT_COREDUMP = 0x0a,
MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c,
+ MCU_UNI_EVENT_PS_SYNC = 0x0d,
MCU_UNI_EVENT_SCAN_DONE = 0x0e,
MCU_UNI_EVENT_RDD_REPORT = 0x11,
MCU_UNI_EVENT_ROC = 0x27,
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index b03be0eb4712..12c615b1294b 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -267,6 +267,21 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
wcid = __mt76_wcid_ptr(dev, wcid_idx);
mt76_tx_check_non_aql(dev, wcid, skb);
+ if (wcid && (dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING) &&
+ test_bit(MT_WCID_FLAG_PS, &wcid->flags)) {
+ struct ieee80211_sta *sta = wcid_to_sta(wcid);
+
+ if (sta) {
+ struct ieee80211_hw *hw = mt76_phy_hw(dev, wcid->phy_idx);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ if (sta->txq[i])
+ ieee80211_schedule_txq(hw, sta->txq[i]);
+ mt76_worker_schedule(&dev->tx_worker);
+ }
+ }
+
#ifdef CONFIG_NL80211_TESTMODE
if (mt76_is_testmode_skb(dev, skb, &hw)) {
struct mt76_phy *phy = hw->priv;
@@ -476,8 +491,12 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q,
bool stop = false;
int idx;
- if (test_bit(MT_WCID_FLAG_PS, &wcid->flags))
- return 0;
+ if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) {
+ if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING))
+ return 0;
+ if (ieee80211_txq_aql_pending(phy->hw, txq))
+ return 0;
+ }
if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
return 0;
@@ -497,6 +516,9 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q,
if (idx < 0)
return idx;
+ if (test_bit(MT_WCID_FLAG_PS, &wcid->flags))
+ goto out;
+
do {
if (test_bit(MT76_RESET, &phy->state) || phy->offchannel)
break;
@@ -522,6 +544,7 @@ mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q,
n_frames++;
} while (1);
+out:
spin_lock(&q->lock);
dev->queue_ops->kick(dev, q);
spin_unlock(&q->lock);
@@ -548,10 +571,7 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)
mtxq = (struct mt76_txq *)txq->drv_priv;
wcid = __mt76_wcid_ptr(dev, mtxq->wcid);
- if (!wcid || test_bit(MT_WCID_FLAG_PS, &wcid->flags))
- continue;
-
- if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
+ if (!wcid)
continue;
phy = mt76_dev_phy(dev, wcid->phy_idx);
@@ -559,6 +579,24 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)
continue;
q = phy->q_tx[qid];
+
+ if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) {
+ if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING))
+ continue;
+
+ if (!mt76_txq_stopped(q))
+ n_frames = mt76_txq_send_burst(phy, q, mtxq, wcid);
+
+ ieee80211_return_txq(phy->hw, txq, false);
+
+ if (unlikely(n_frames < 0))
+ return n_frames;
+ ret += n_frames;
+ continue;
+ }
+
+ if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
+ continue;
if (dev->queue_ops->tx_cleanup &&
q->queued + 2 * MT_TXQ_FREE_THR >= q->ndesc) {
dev->queue_ops->tx_cleanup(dev, q, false);
@@ -765,6 +803,33 @@ void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta,
}
EXPORT_SYMBOL_GPL(mt76_stop_tx_queues);
+void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid,
+ bool ps)
+{
+ struct ieee80211_sta *sta;
+ struct ieee80211_hw *hw;
+ int i;
+
+ if (ps) {
+ set_bit(MT_WCID_FLAG_PS, &wcid->flags);
+ mt76_worker_schedule(&dev->tx_worker);
+ return;
+ }
+
+ clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
+
+ sta = wcid_to_sta(wcid);
+ if (!sta)
+ return;
+
+ hw = mt76_phy_hw(dev, wcid->phy_idx);
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ if (sta->txq[i])
+ ieee80211_schedule_txq(hw, sta->txq[i]);
+ mt76_worker_schedule(&dev->tx_worker);
+}
+EXPORT_SYMBOL_GPL(mt76_sta_ps_transition);
+
void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
struct mt76_phy *phy = hw->priv;