mt76: add energy detect CCA support to mt76x{0,2}e drivers
authorLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Wed, 12 Dec 2018 21:51:55 +0000 (22:51 +0100)
committerFelix Fietkau <nbd@nbd.name>
Fri, 11 Jan 2019 14:10:18 +0000 (15:10 +0100)
Ported from the reference driver. Should fix compliance with ETSI
regulatories on preventing transmission while energy detect values
are above the threshold.
The code has been tested using an ath9k device running tx99 as
noise generator

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
drivers/net/wireless/mediatek/mt76/mt76x02.h
drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
drivers/net/wireless/mediatek/mt76/mt76x02_regs.h
drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c

index 1906cc62690f5d23665fbb14bb8ad0cdf68abef0..720b05e325a5bd67aa2f9afeceadfcd755950044 100644 (file)
@@ -147,6 +147,7 @@ static int mt76x0e_register_device(struct mt76x02_dev *dev)
                MT_CH_TIME_CFG_RX_AS_BUSY |
                MT_CH_TIME_CFG_NAV_AS_BUSY |
                MT_CH_TIME_CFG_EIFS_AS_BUSY |
+               MT_CH_CCA_RC_EN |
                FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1));
 
        err = mt76x0_register_device(dev);
index 1eb1a802ed20d2a8744fa6496c4857c43a762046..b2b38b9fcac7d5c965eff2f45a83d590f40de5fe 100644 (file)
@@ -1013,6 +1013,8 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        mt76x0_phy_calibrate(dev, false);
        mt76x0_phy_set_txpower(dev);
 
+       mt76x02_edcca_init(dev);
+
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);
 
index 6782665049dddaac4eb90c67b25e7e966a19b435..f383fdd914c562dfee3bb85210ce7c60f3b6ff90 100644 (file)
@@ -101,6 +101,12 @@ struct mt76x02_dev {
        u8 slottime;
 
        struct mt76x02_dfs_pattern_detector dfs_pd;
+
+       /* edcca monitor */
+       bool ed_tx_blocked;
+       bool ed_monitor;
+       u8 ed_trigger;
+       u8 ed_silent;
 };
 
 extern struct ieee80211_rate mt76x02_rates[12];
index 5bd523b091b6ec3e2b31e1bb664f9fd3df3b0948..19fdcab746a0a957d8b9ff666e82a230457e3bba 100644 (file)
@@ -884,6 +884,10 @@ mt76x02_dfs_set_domain(struct mt76x02_dev *dev,
        mutex_lock(&dev->mt76.mutex);
        if (dfs_pd->region != region) {
                tasklet_disable(&dfs_pd->dfs_tasklet);
+
+               dev->ed_monitor = region == NL80211_DFS_ETSI;
+               mt76x02_edcca_init(dev);
+
                dfs_pd->region = region;
                mt76x02_dfs_init_params(dev);
                tasklet_enable(&dfs_pd->dfs_tasklet);
index ddc1c3146feb0dd4793ec6b8344013ba8ff442c5..2e06e1f418109f64944c609efe2486a0924c24ac 100644 (file)
@@ -847,6 +847,88 @@ static void mt76x02_check_mac_err(struct mt76x02_dev *dev)
                MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
 }
 
+static void
+mt76x02_edcca_tx_enable(struct mt76x02_dev *dev, bool enable)
+{
+       if (enable) {
+               u32 data;
+
+               mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
+               mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN);
+               /* enable pa-lna */
+               data = mt76_rr(dev, MT_TX_PIN_CFG);
+               data |= MT_TX_PIN_CFG_TXANT |
+                       MT_TX_PIN_CFG_RXANT |
+                       MT_TX_PIN_RFTR_EN |
+                       MT_TX_PIN_TRSW_EN;
+               mt76_wr(dev, MT_TX_PIN_CFG, data);
+       } else {
+               mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
+               mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN);
+               /* disable pa-lna */
+               mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT);
+               mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_RXANT);
+       }
+       dev->ed_tx_blocked = !enable;
+}
+
+void mt76x02_edcca_init(struct mt76x02_dev *dev)
+{
+       dev->ed_trigger = 0;
+       dev->ed_silent = 0;
+
+       if (dev->ed_monitor) {
+               struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+               u8 ed_th = chan->band == NL80211_BAND_5GHZ ? 0x0e : 0x20;
+
+               mt76_clear(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN);
+               mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+               mt76_rmw(dev, MT_BBP(AGC, 2), GENMASK(15, 0),
+                        ed_th << 8 | ed_th);
+               if (!is_mt76x2(dev))
+                       mt76_set(dev, MT_TXOP_HLDR_ET,
+                                MT_TXOP_HLDR_TX40M_BLK_EN);
+       } else {
+               mt76_set(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN);
+               mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+               if (is_mt76x2(dev)) {
+                       mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070);
+               } else {
+                       mt76_wr(dev, MT_BBP(AGC, 2), 0x003a6464);
+                       mt76_clear(dev, MT_TXOP_HLDR_ET,
+                                  MT_TXOP_HLDR_TX40M_BLK_EN);
+               }
+       }
+       mt76x02_edcca_tx_enable(dev, true);
+}
+EXPORT_SYMBOL_GPL(mt76x02_edcca_init);
+
+#define MT_EDCCA_TH            90
+#define MT_EDCCA_BLOCK_TH      2
+static void mt76x02_edcca_check(struct mt76x02_dev *dev)
+{
+       u32 val, busy;
+
+       val = mt76_rr(dev, MT_ED_CCA_TIMER);
+       busy = (val * 100) / jiffies_to_usecs(MT_CALIBRATE_INTERVAL);
+       busy = min_t(u32, busy, 100);
+
+       if (busy > MT_EDCCA_TH) {
+               dev->ed_trigger++;
+               dev->ed_silent = 0;
+       } else {
+               dev->ed_silent++;
+               dev->ed_trigger = 0;
+       }
+
+       if (dev->ed_trigger > MT_EDCCA_BLOCK_TH &&
+           !dev->ed_tx_blocked)
+               mt76x02_edcca_tx_enable(dev, false);
+       else if (dev->ed_silent > MT_EDCCA_BLOCK_TH &&
+                dev->ed_tx_blocked)
+               mt76x02_edcca_tx_enable(dev, true);
+}
+
 void mt76x02_mac_work(struct work_struct *work)
 {
        struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
@@ -867,6 +949,9 @@ void mt76x02_mac_work(struct work_struct *work)
        if (!dev->beacon_mask)
                mt76x02_check_mac_err(dev);
 
+       if (dev->ed_monitor)
+               mt76x02_edcca_check(dev);
+
        mutex_unlock(&dev->mt76.mutex);
 
        mt76_tx_status_check(&dev->mt76, NULL, false);
index 91c76a050f7eeb76186be49d7c1eaf2ea10449b5..735fe96440ba6b11c0a851eea6d56bd2dc441550 100644 (file)
@@ -212,4 +212,6 @@ int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
                           struct sk_buff *skb);
 void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, u8 vif_idx,
                                   bool val);
+
+void mt76x02_edcca_init(struct mt76x02_dev *dev);
 #endif
index 20ad07638699765bd789531800cbff33573dc4f7..b8d1ffbac6b5920fc96bec78f1c0112ab0891e73 100644 (file)
 #define MT_CH_TIME_CFG_NAV_AS_BUSY     BIT(3)
 #define MT_CH_TIME_CFG_EIFS_AS_BUSY    BIT(4)
 #define MT_CH_TIME_CFG_MDRDY_CNT_EN    BIT(5)
+#define MT_CH_CCA_RC_EN                        BIT(6)
 #define MT_CH_TIME_CFG_CH_TIMER_CLR    GENMASK(9, 8)
 #define MT_CH_TIME_CFG_MDRDY_CLR       GENMASK(11, 10)
 
 #define MT_TX_PWR_CFG_4                        0x1324
 #define MT_TX_PIN_CFG                  0x1328
 #define MT_TX_PIN_CFG_TXANT            GENMASK(3, 0)
+#define MT_TX_PIN_CFG_RXANT            GENMASK(11, 8)
+#define MT_TX_PIN_RFTR_EN              BIT(16)
+#define MT_TX_PIN_TRSW_EN              BIT(18)
 
 #define MT_TX_BAND_CFG                 0x132c
 #define MT_TX_BAND_CFG_UPPER_40M       BIT(0)
 #define MT_TXOP_CTRL_CFG               0x1340
 #define MT_TXOP_TRUN_EN                        GENMASK(5, 0)
 #define MT_TXOP_EXT_CCA_DLY            GENMASK(15, 8)
+#define MT_TXOP_ED_CCA_EN              BIT(20)
 
 #define MT_TX_RTS_CFG                  0x1344
 #define MT_TX_RTS_CFG_RETRY_LIMIT      GENMASK(7, 0)
 
 #define MT_TX_RETRY_CFG                        0x134c
 #define MT_TX_LINK_CFG                 0x1350
+#define MT_TX_CFACK_EN                 BIT(12)
 #define MT_VHT_HT_FBK_CFG0             0x1354
 #define MT_VHT_HT_FBK_CFG1             0x1358
 #define MT_LG_FBK_CFG0                 0x135c
 #define MT_RX_FILTR_CFG_CTRL_RSV       BIT(16)
 
 #define MT_AUTO_RSP_CFG                        0x1404
+#define MT_AUTO_RSP_EN                 BIT(0)
 #define MT_AUTO_RSP_PREAMB_SHORT       BIT(4)
 #define MT_LEGACY_BASIC_RATE           0x1408
 #define MT_HT_BASIC_RATE               0x140c
 #define MT_PN_PAD_MODE                 0x150c
 
 #define MT_TXOP_HLDR_ET                        0x1608
+#define MT_TXOP_HLDR_TX40M_BLK_EN      BIT(1)
 
 #define MT_PROT_AUTO_TX_CFG            0x1648
 #define MT_PROT_AUTO_TX_CFG_PROT_PADJ  GENMASK(11, 8)
index 2f05eacb894686d4fce8bcb2e98d6c679a05282d..9f313824eeadb098a6f2432e18df2f8483ec4a18 100644 (file)
@@ -151,6 +151,7 @@ static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
                MT_CH_TIME_CFG_RX_AS_BUSY |
                MT_CH_TIME_CFG_NAV_AS_BUSY |
                MT_CH_TIME_CFG_EIFS_AS_BUSY |
+               MT_CH_CCA_RC_EN |
                FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1));
 
        mt76x02_set_tx_ackto(dev);
index da7cd40f56ffb01ffe943dd2d8238f0e0915191d..65ed62229a5bf8afb56b0743545ba262a56b228c 100644 (file)
@@ -254,6 +254,8 @@ int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
                               0x38);
        }
 
+       mt76x02_edcca_init(dev);
+
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);