diff --git a/Makefile b/Makefile index 38247f73..6e2a6c99 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,5 @@ mt76x2e-y := \ mt7603e-y := \ mt7603_pci.o mt7603_main.o mt7603_init.o mt7603_mcu.o \ - mt7603_core.o mt7603_dma.o mt7603_mac.o mt7603_eeprom.o + mt7603_core.o mt7603_dma.o mt7603_mac.o mt7603_eeprom.o \ + mt7603_beacon.o diff --git a/mt7603.h b/mt7603.h index 3698b8ed..fbb5b60e 100644 --- a/mt7603.h +++ b/mt7603.h @@ -31,6 +31,8 @@ #define MT_AGG_SIZE_LIMIT(n) ((4 + 2 * (n & 1)) << (n / 2)) +#define MT7603_PRE_TBTT_TIME 5000 /* ms */ + enum { MT7603_REV_E1 = 0x00, MT7603_REV_E2 = 0x10 @@ -114,6 +116,8 @@ struct mt7603_dev { struct mt7603_mcu mcu; struct mt76_queue q_rx; + u8 beacon_mask; + struct tasklet_struct tx_tasklet; struct tasklet_struct pre_tbtt_tasklet; }; @@ -174,6 +178,7 @@ static inline void mt7603_irq_disable(struct mt7603_dev *dev, u32 mask) void mt7603_mac_start(struct mt7603_dev *dev); void mt7603_mac_stop(struct mt7603_dev *dev); void mt7603_mac_set_timing(struct mt7603_dev *dev); +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval); int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb); void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data); struct sk_buff *mt7603_mac_status_skb(struct mt7603_dev *dev, @@ -205,4 +210,6 @@ void mt7603_queue_rx_skb(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb); void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +void mt7603_pre_tbtt_tasklet(unsigned long arg); + #endif diff --git a/mt7603_beacon.c b/mt7603_beacon.c new file mode 100644 index 00000000..93c2e1f9 --- /dev/null +++ b/mt7603_beacon.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mt7603.h" + +struct beacon_bc_data { + struct mt7603_dev *dev; + struct sk_buff_head q; + struct sk_buff *tail[8]; +}; + +static void +mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7603_dev *dev = (struct mt7603_dev *) priv; + struct mt7603_vif *mvif = (struct mt7603_vif *) vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb = NULL; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_beacon_get(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + info->control.vif = vif; + mt76_tx_queue_skb(&dev->mt76, &dev->mt76.q_tx[MT_TXQ_BEACON], skb, + &mvif->sta.wcid, NULL); +} + +static void +mt7603_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt7603_dev *dev = data->dev; + struct mt7603_vif *mvif = (struct mt7603_vif *) vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + if (!(dev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + mt76_skb_set_moredata(skb, true); + __skb_queue_tail(&data->q, skb); + data->tail[mvif->idx] = skb; +} + +void mt7603_pre_tbtt_tasklet(unsigned long arg) +{ + struct mt7603_dev *dev = (struct mt7603_dev *) arg; + struct mt76_queue *q = &dev->mt76.q_tx[MT_TXQ_CAB]; + struct beacon_bc_data data = {}; + struct sk_buff *skb; + int i, nframes; + + data.dev = dev; + __skb_queue_head_init(&data.q); + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_update_beacon_iter, dev); + + do { + nframes = skb_queue_len(&data.q); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_add_buffered_bc, &data); + } while (nframes != skb_queue_len(&data.q)); + + if (!nframes) + return; + + for (i = 0; i < ARRAY_SIZE(data.tail); i++) { + if (!data.tail[i]) + continue; + + mt76_skb_set_moredata(data.tail[i], false); + } + + spin_lock_bh(&q->lock); + while ((skb = __skb_dequeue(&data.q)) != NULL) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_vif *mvif = (struct mt7603_vif *) vif->drv_priv; + + mt76_tx_queue_skb(&dev->mt76, q, skb, &mvif->sta.wcid, NULL); + } + mt76_queue_kick(dev, q); + spin_unlock_bh(&q->lock); +} + +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval) +{ + u32 pre_tbtt = MT7603_PRE_TBTT_TIME / 64; + + if (intval) + dev->beacon_mask |= BIT(idx); + else + dev->beacon_mask &= BIT(idx); + + if (!dev->beacon_mask) { + mt7603_irq_disable(dev, MT_INT_PRE_TBTT); + mt76_clear(dev, MT_WF_ARB_SCR, MT_WF_ARB_BCNQ_OPMODE_MASK); + mt76_wr(dev, MT_HW_INT_MASK(3), 0); + return; + } + + mt76_wr(dev, MT_TBTT, + MT76_SET(MT_TBTT_PERIOD, intval) | MT_TBTT_CAL_ENABLE); + + mt76_wr(dev, MT_TBTT_TIMER_CFG, 0x99); /* start timer */ + + mt76_rmw_field(dev, MT_WF_ARB_SCR, MT_WF_ARB_BCNQ_OPMODE_MASK, + MT_BCNQ_OPMODE_AP); + mt76_wr(dev, MT_PRE_TBTT, pre_tbtt); + + mt76_set(dev, MT_HW_INT_MASK(3), MT_HW_INT3_PRE_TBTT0); + + mt76_set(dev, MT_WF_ARB_BCN_START, MT_WF_ARB_BCN_START_BSSn(0)); + mt7603_irq_enable(dev, MT_INT_PRE_TBTT); +} diff --git a/mt7603_core.c b/mt7603_core.c index 0a1cc449..fcc2a433 100644 --- a/mt7603_core.c +++ b/mt7603_core.c @@ -58,14 +58,11 @@ irqreturn_t mt7603_irq_handler(int irq, void *dev_instance) napi_schedule(&dev->mt76.napi[1]); } -#if 0 - if (intr & MT_INT_PRE_TBTT) + if (intr & MT_INT_PRE_TBTT) { + u32 hwintr = mt76_rr(dev, MT_HW_INT_STATUS(3)); + mt76_wr(dev, MT_HW_INT_STATUS(3), hwintr); tasklet_schedule(&dev->pre_tbtt_tasklet); - - /* send buffered multicast frames now */ - if (intr & MT_INT_TBTT) - mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]); -#endif + } return IRQ_HANDLED; } diff --git a/mt7603_init.c b/mt7603_init.c index 745f986e..2fab409c 100644 --- a/mt7603_init.c +++ b/mt7603_init.c @@ -371,6 +371,8 @@ int mt7603_register_device(struct mt7603_dev *dev) spin_lock_init(&dev->status_lock); INIT_LIST_HEAD(&dev->status_list); + tasklet_init(&dev->pre_tbtt_tasklet, mt7603_pre_tbtt_tasklet, (unsigned long) dev); + dev->rx_chains = 2; dev->tx_chains = 2; dev->slottime = 9; diff --git a/mt7603_main.c b/mt7603_main.c index 4ceca972..6e21d96c 100644 --- a/mt7603_main.c +++ b/mt7603_main.c @@ -223,6 +223,13 @@ mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7603_mac_set_timing(dev); } + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) { + int beacon_int = !!info->enable_beacon * info->beacon_int; + tasklet_disable(&dev->pre_tbtt_tasklet); + mt7603_beacon_set_timer(dev, mvif->idx, beacon_int); + tasklet_enable(&dev->pre_tbtt_tasklet); + } + mutex_unlock(&dev->mutex); } diff --git a/mt7603_regs.h b/mt7603_regs.h index 9de9f3bd..8b5ace8d 100644 --- a/mt7603_regs.h +++ b/mt7603_regs.h @@ -209,8 +209,20 @@ #define MT_WF_ARB_RQCR_RXV_T_EN BIT(8) #define MT_WF_ARB_SCR MT_WF_ARB(0x080) +#define MT_WF_ARB_BCNQ_OPMODE_MASK GENMASK(1, 0) +#define MT_WF_ARB_BCNQ_OPMODE_SHIFT(n) (n * 2) #define MT_WF_ARB_TX_DISABLE BIT(8) #define MT_WF_ARB_RX_DISABLE BIT(9) +#define MT_WF_ARB_BCNQ_EMPTY_SKIP BIT(28) +#define MT_WF_ARB_TTTT_BTIM_PRIO BIT(29) +#define MT_WF_ARB_TBTT_BCN_PRIO BIT(30) +#define MT_WF_ARB_TBTT_BCAST_PRIO BIT(31) + +enum { + MT_BCNQ_OPMODE_STA = 0, + MT_BCNQ_OPMODE_AP = 1, + MT_BCNQ_OPMODE_ADHOC = 2, +}; #define MT_WF_ARB_TX_START_0 MT_WF_ARB(0x100) #define MT_WF_ARB_TX_START_1 MT_WF_ARB(0x104) @@ -219,6 +231,32 @@ #define MT_WF_ARB_TX_STOP_0 MT_WF_ARB(0x110) #define MT_WF_ARB_TX_STOP_1 MT_WF_ARB(0x114) +#define MT_WF_ARB_BCN_START MT_WF_ARB(0x118) +#define MT_WF_ARB_BCN_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_START_T_PRE_TTTT BIT(10) +#define MT_WF_ARB_BCN_START_T_TTTT BIT(11) +#define MT_WF_ARB_BCN_START_T_PRE_TBTT BIT(12) +#define MT_WF_ARB_BCN_START_T_TBTT BIT(13) +#define MT_WF_ARB_BCN_START_T_SLOT_IDLE BIT(14) +#define MT_WF_ARB_BCN_START_T_TX_START BIT(15) +#define MT_WF_ARB_BCN_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_BCN_FLUSH MT_WF_ARB(0x11c) +#define MT_WF_ARB_BCN_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_START MT_WF_ARB(0x120) +#define MT_WF_ARB_CAB_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_FLUSH MT_WF_ARB(0x124) +#define MT_WF_ARB_CAB_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_COUNT_0 MT_WF_ARB(0x128) +#define MT_WF_ARB_CAB_COUNT_1 MT_WF_ARB(0x12c) +#define MT_WF_ARB_CAB_COUNT_2 MT_WF_ARB(0x130) + #define MT_WF_TMAC_BASE 0x21600 #define MT_WF_TMAC(ofs) (MT_WF_TMAC_BASE + (ofs)) @@ -349,6 +387,28 @@ #define MT_LPON_BTEIR MT_LPON(0x020) #define MT_LPON_BTEIR_MBSS_MODE GENMASK(31, 29) +#define MT_PRE_TBTT MT_LPON(0x030) +#define MT_PRE_TBTT_MASK GENMASK(7, 0) +#define MT_PRE_TBTT_SHIFT 8 + +#define MT_TBTT MT_LPON(0x034) +#define MT_TBTT_PERIOD GENMASK(15, 0) +#define MT_TBTT_DTIM_PERIOD GENMASK(23, 16) +#define MT_TBTT_TBTT_WAKE_PERIOD GENMASK(27, 24) +#define MT_TBTT_DTIM_WAKE_PERIOD GENMASK(30, 28) +#define MT_TBTT_CAL_ENABLE BIT(31) + +#define MT_TBTT_TIMER_CFG MT_LPON(0x05c) + +#define MT_INT_WAKEUP_BASE 0x24400 +#define MT_INT_WAKEUP(n) (MT_INT_WAKEUP_BASE + (n)) + +#define MT_HW_INT_STATUS(n) MT_INT_WAKEUP(0x3c + (n) * 8) +#define MT_HW_INT_MASK(n) MT_INT_WAKEUP(0x40 + (n) * 8) + +#define MT_HW_INT3_TBTT0 BIT(15) +#define MT_HW_INT3_PRE_TBTT0 BIT(31) + #define MT_WTBL1_BASE 0x28000 #define MT_PCIE_REMAP_BASE_1 0x40000