mt76: mt7615: add Smart Carrier Sense support
authorLorenzo Bianconi <lorenzo@kernel.org>
Sun, 28 Jul 2019 19:03:18 +0000 (21:03 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 5 Sep 2019 15:42:31 +0000 (17:42 +0200)
Introduce Smart Carrier Sense support in order to tune device
sensitivity according to RTS error rate and False CCA reported by the
radio

Tested-by: Ryder Lee <ryder.lee@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
drivers/net/wireless/mediatek/mt76/mt7615/init.c
drivers/net/wireless/mediatek/mt76/mt7615/mac.c
drivers/net/wireless/mediatek/mt76/mt7615/main.c
drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
drivers/net/wireless/mediatek/mt76/mt7615/regs.h

index 2618248..e62a5eb 100644 (file)
@@ -13,6 +13,42 @@ mt7615_radar_pattern_set(void *data, u64 val)
 DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_pattern, NULL,
                         mt7615_radar_pattern_set, "%lld\n");
 
+static int
+mt7615_scs_set(void *data, u64 val)
+{
+       struct mt7615_dev *dev = data;
+
+       mt7615_mac_set_scs(dev, val);
+
+       return 0;
+}
+
+static int
+mt7615_scs_get(void *data, u64 *val)
+{
+       struct mt7615_dev *dev = data;
+
+       *val = dev->scs_en;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_scs, mt7615_scs_get,
+                        mt7615_scs_set, "%lld\n");
+
+static int
+mt7615_radio_read(struct seq_file *s, void *data)
+{
+       struct mt7615_dev *dev = dev_get_drvdata(s->private);
+
+       seq_printf(s, "Sensitivity: ofdm=%d cck=%d\n",
+                  dev->ofdm_sensitivity, dev->cck_sensitivity);
+       seq_printf(s, "False CCA: ofdm=%d cck=%d\n",
+                  dev->false_cca_ofdm, dev->false_cca_cck);
+
+       return 0;
+}
+
 int mt7615_init_debugfs(struct mt7615_dev *dev)
 {
        struct dentry *dir;
@@ -21,6 +57,9 @@ int mt7615_init_debugfs(struct mt7615_dev *dev)
        if (!dir)
                return -ENOMEM;
 
+       debugfs_create_file("scs", 0600, dir, dev, &fops_scs);
+       debugfs_create_devm_seqfile(dev->mt76.dev, "radio", dir,
+                                   mt7615_radio_read);
        debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern);
        /* test pattern knobs */
        debugfs_create_u8("pattern_len", 0600, dir,
index be144e1..1104e4c 100644 (file)
@@ -50,6 +50,7 @@ static void mt7615_mac_init(struct mt7615_dev *dev)
                 MT_TMAC_CTCR0_INS_DDLMT_EN);
 
        mt7615_mcu_set_rts_thresh(dev, 0x92b);
+       mt7615_mac_set_scs(dev, false);
 
        mt76_rmw(dev, MT_AGG_SCR, MT_AGG_SCR_NLNAV_MID_PTEC_DIS,
                 MT_AGG_SCR_NLNAV_MID_PTEC_DIS);
index 642c3a2..0554aa2 100644 (file)
@@ -986,6 +986,175 @@ void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb)
        dev_kfree_skb(skb);
 }
 
+static void
+mt7615_mac_set_default_sensitivity(struct mt7615_dev *dev)
+{
+       mt76_rmw(dev, MT_WF_PHY_B0_MIN_PRI_PWR,
+                MT_WF_PHY_B0_PD_OFDM_MASK,
+                MT_WF_PHY_B0_PD_OFDM(0x13c));
+       mt76_rmw(dev, MT_WF_PHY_B1_MIN_PRI_PWR,
+                MT_WF_PHY_B1_PD_OFDM_MASK,
+                MT_WF_PHY_B1_PD_OFDM(0x13c));
+
+       mt76_rmw(dev, MT_WF_PHY_B0_RXTD_CCK_PD,
+                MT_WF_PHY_B0_PD_CCK_MASK,
+                MT_WF_PHY_B0_PD_CCK(0x92));
+       mt76_rmw(dev, MT_WF_PHY_B1_RXTD_CCK_PD,
+                MT_WF_PHY_B1_PD_CCK_MASK,
+                MT_WF_PHY_B1_PD_CCK(0x92));
+
+       dev->ofdm_sensitivity = -98;
+       dev->cck_sensitivity = -110;
+       dev->last_cca_adj = jiffies;
+}
+
+void mt7615_mac_set_scs(struct mt7615_dev *dev, bool enable)
+{
+       mutex_lock(&dev->mt76.mutex);
+
+       if (dev->scs_en == enable)
+               goto out;
+
+       if (enable) {
+               /* DBDC not supported */
+               mt76_set(dev, MT_WF_PHY_B0_MIN_PRI_PWR,
+                        MT_WF_PHY_B0_PD_BLK);
+               if (is_mt7622(&dev->mt76)) {
+                       mt76_set(dev, MT_MIB_M0_MISC_CR, 0x7 << 8);
+                       mt76_set(dev, MT_MIB_M0_MISC_CR, 0x7);
+               }
+       } else {
+               mt76_clear(dev, MT_WF_PHY_B0_MIN_PRI_PWR,
+                          MT_WF_PHY_B0_PD_BLK);
+               mt76_clear(dev, MT_WF_PHY_B1_MIN_PRI_PWR,
+                          MT_WF_PHY_B1_PD_BLK);
+       }
+
+       mt7615_mac_set_default_sensitivity(dev);
+       dev->scs_en = enable;
+
+out:
+       mutex_unlock(&dev->mt76.mutex);
+}
+
+void mt7615_mac_cca_stats_reset(struct mt7615_dev *dev)
+{
+       mt76_clear(dev, MT_WF_PHY_R0_B0_PHYMUX_5, GENMASK(22, 20));
+       mt76_set(dev, MT_WF_PHY_R0_B0_PHYMUX_5, BIT(22) | BIT(20));
+}
+
+static void
+mt7615_mac_adjust_sensitivity(struct mt7615_dev *dev,
+                             u32 rts_err_rate, bool ofdm)
+{
+       int false_cca = ofdm ? dev->false_cca_ofdm : dev->false_cca_cck;
+       u16 def_th = ofdm ? -98 : -110;
+       bool update = false;
+       s8 *sensitivity;
+       int signal;
+
+       sensitivity = ofdm ? &dev->ofdm_sensitivity : &dev->cck_sensitivity;
+       signal = mt76_get_min_avg_rssi(&dev->mt76);
+       if (!signal) {
+               mt7615_mac_set_default_sensitivity(dev);
+               return;
+       }
+
+       signal = min(signal, -72);
+       if (false_cca > 500) {
+               if (rts_err_rate > MT_FRAC(40, 100))
+                       return;
+
+               /* decrease coverage */
+               if (*sensitivity == def_th && signal > -90) {
+                       *sensitivity = -90;
+                       update = true;
+               } else if (*sensitivity + 2 < signal) {
+                       *sensitivity += 2;
+                       update = true;
+               }
+       } else if ((false_cca > 0 && false_cca < 50) ||
+                  rts_err_rate > MT_FRAC(60, 100)) {
+               /* increase coverage */
+               if (*sensitivity - 2 >= def_th) {
+                       *sensitivity -= 2;
+                       update = true;
+               }
+       }
+
+       if (*sensitivity > signal) {
+               *sensitivity = signal;
+               update = true;
+       }
+
+       if (update) {
+               u16 val;
+
+               if (ofdm) {
+                       /* DBDC not supported */
+                       val = *sensitivity * 2 + 512;
+                       mt76_rmw(dev, MT_WF_PHY_B0_MIN_PRI_PWR,
+                                MT_WF_PHY_B0_PD_OFDM_MASK,
+                                MT_WF_PHY_B0_PD_OFDM(val));
+               } else {
+                       val = *sensitivity + 256;
+                       mt76_rmw(dev, MT_WF_PHY_B0_RXTD_CCK_PD,
+                                MT_WF_PHY_B0_PD_CCK_MASK,
+                                MT_WF_PHY_B0_PD_CCK(val));
+                       mt76_rmw(dev, MT_WF_PHY_B1_RXTD_CCK_PD,
+                                MT_WF_PHY_B1_PD_CCK_MASK,
+                                MT_WF_PHY_B1_PD_CCK(val));
+               }
+               dev->last_cca_adj = jiffies;
+       }
+}
+
+static void
+mt7615_mac_scs_check(struct mt7615_dev *dev)
+{
+       u32 val, rts_cnt = 0, rts_retries_cnt = 0, rts_err_rate = 0;
+       u32 mdrdy_cck, mdrdy_ofdm, pd_cck, pd_ofdm;
+       int i;
+
+       if (!dev->scs_en)
+               return;
+
+       for (i = 0; i < 4; i++) {
+               u32 data;
+
+               val = mt76_rr(dev, MT_MIB_MB_SDR0(i));
+               data = FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val);
+               if (data > rts_retries_cnt) {
+                       rts_cnt = FIELD_GET(MT_MIB_RTS_COUNT_MASK, val);
+                       rts_retries_cnt = data;
+               }
+       }
+
+       val = mt76_rr(dev, MT_WF_PHY_R0_B0_PHYCTRL_STS0);
+       pd_cck = FIELD_GET(MT_WF_PHYCTRL_STAT_PD_CCK, val);
+       pd_ofdm = FIELD_GET(MT_WF_PHYCTRL_STAT_PD_OFDM, val);
+
+       val = mt76_rr(dev, MT_WF_PHY_R0_B0_PHYCTRL_STS5);
+       mdrdy_cck = FIELD_GET(MT_WF_PHYCTRL_STAT_MDRDY_CCK, val);
+       mdrdy_ofdm = FIELD_GET(MT_WF_PHYCTRL_STAT_MDRDY_OFDM, val);
+
+       dev->false_cca_ofdm = pd_ofdm - mdrdy_ofdm;
+       dev->false_cca_cck = pd_cck - mdrdy_cck;
+       mt7615_mac_cca_stats_reset(dev);
+
+       if (rts_cnt + rts_retries_cnt)
+               rts_err_rate = MT_FRAC(rts_retries_cnt,
+                                      rts_cnt + rts_retries_cnt);
+
+       /* cck */
+       mt7615_mac_adjust_sensitivity(dev, rts_err_rate, false);
+       /* ofdm */
+       mt7615_mac_adjust_sensitivity(dev, rts_err_rate, true);
+
+       if (time_after(jiffies, dev->last_cca_adj + 10 * HZ))
+               mt7615_mac_set_default_sensitivity(dev);
+}
+
 void mt7615_mac_work(struct work_struct *work)
 {
        struct mt7615_dev *dev;
@@ -993,6 +1162,13 @@ void mt7615_mac_work(struct work_struct *work)
        dev = (struct mt7615_dev *)container_of(work, struct mt76_dev,
                                                mac_work.work);
 
+       mutex_lock(&dev->mt76.mutex);
+       if (++dev->mac_work_count == 5) {
+               mt7615_mac_scs_check(dev);
+               dev->mac_work_count = 0;
+       }
+       mutex_unlock(&dev->mt76.mutex);
+
        mt76_tx_status_check(&dev->mt76, NULL, false);
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
                                     MT7615_WATCHDOG_TIME);
index c7c0b92..5fdda85 100644 (file)
@@ -148,6 +148,7 @@ static int mt7615_set_channel(struct mt7615_dev *dev)
                goto out;
 
        ret = mt7615_dfs_init_radar_detector(dev);
+       mt7615_mac_cca_stats_reset(dev);
 
 out:
        clear_bit(MT76_RESET, &dev->mt76.state);
index da4d0a5..4e6ab3a 100644 (file)
@@ -33,6 +33,9 @@
 #define MT7615_EEPROM_SIZE             1024
 #define MT7615_TOKEN_SIZE              4096
 
+#define MT_FRAC_SCALE          12
+#define MT_FRAC(val, div)      (((val) << MT_FRAC_SCALE) / (div))
+
 struct mt7615_vif;
 struct mt7615_sta;
 
@@ -87,6 +90,13 @@ struct mt7615_dev {
        u32 hw_pattern;
        int dfs_state;
 
+       int false_cca_ofdm, false_cca_cck;
+       unsigned long last_cca_adj;
+       u8 mac_work_count;
+       s8 ofdm_sensitivity;
+       s8 cck_sensitivity;
+       bool scs_en;
+
        spinlock_t token_lock;
        struct idr token;
 };
@@ -192,6 +202,11 @@ int mt7615_dfs_start_radar_detector(struct mt7615_dev *dev);
 int mt7615_dfs_stop_radar_detector(struct mt7615_dev *dev);
 int mt7615_mcu_rdd_send_pattern(struct mt7615_dev *dev);
 
+static inline bool is_mt7622(struct mt76_dev *dev)
+{
+       return mt76_chip(dev) == 0x7622;
+}
+
 static inline void mt7615_dfs_check_channel(struct mt7615_dev *dev)
 {
        enum nl80211_chan_width width = dev->mt76.chandef.width;
@@ -213,6 +228,8 @@ static inline void mt7615_irq_disable(struct mt7615_dev *dev, u32 mask)
        mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
 }
 
+void mt7615_mac_cca_stats_reset(struct mt7615_dev *dev);
+void mt7615_mac_set_scs(struct mt7615_dev *dev, bool enable);
 int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
                          struct sk_buff *skb, struct mt76_wcid *wcid,
                          struct ieee80211_sta *sta, int pid,
index 1dcd202..12fc12e 100644 (file)
 #define MT_WF_PHY_WF2_RFCTRL0          MT_WF_PHY(0x1900)
 #define MT_WF_PHY_WF2_RFCTRL0_LPBCN_EN BIT(9)
 
+#define MT_WF_PHY_R0_B0_PHYMUX_5       MT_WF_PHY(0x0614)
+
+#define MT_WF_PHY_R0_B0_PHYCTRL_STS0   MT_WF_PHY(0x020c)
+#define MT_WF_PHYCTRL_STAT_PD_OFDM     GENMASK(31, 16)
+#define MT_WF_PHYCTRL_STAT_PD_CCK      GENMASK(15, 0)
+
+#define MT_WF_PHY_R0_B0_PHYCTRL_STS5   MT_WF_PHY(0x0220)
+#define MT_WF_PHYCTRL_STAT_MDRDY_OFDM  GENMASK(31, 16)
+#define MT_WF_PHYCTRL_STAT_MDRDY_CCK   GENMASK(15, 0)
+
+#define MT_WF_PHY_B0_MIN_PRI_PWR       MT_WF_PHY(0x229c)
+#define MT_WF_PHY_B0_PD_OFDM_MASK      GENMASK(28, 20)
+#define MT_WF_PHY_B0_PD_OFDM(v)                ((v) << 20)
+#define MT_WF_PHY_B0_PD_BLK            BIT(19)
+
+#define MT_WF_PHY_B1_MIN_PRI_PWR       MT_WF_PHY(0x084)
+#define MT_WF_PHY_B1_PD_OFDM_MASK      GENMASK(24, 16)
+#define MT_WF_PHY_B1_PD_OFDM(v)                ((v) << 16)
+#define MT_WF_PHY_B1_PD_BLK            BIT(25)
+
+#define MT_WF_PHY_B0_RXTD_CCK_PD       MT_WF_PHY(0x2310)
+#define MT_WF_PHY_B0_PD_CCK_MASK       GENMASK(8, 1)
+#define MT_WF_PHY_B0_PD_CCK(v)         ((v) << 1)
+
+#define MT_WF_PHY_B1_RXTD_CCK_PD       MT_WF_PHY(0x2314)
+#define MT_WF_PHY_B1_PD_CCK_MASK       GENMASK(31, 24)
+#define MT_WF_PHY_B1_PD_CCK(v)         ((v) << 24)
+
 #define MT_WF_CFG_BASE                 0x20200
 #define MT_WF_CFG(ofs)                 (MT_WF_CFG_BASE + (ofs))
 
 #define MT_LPON_UTTR0                  MT_LPON(0x018)
 #define MT_LPON_UTTR1                  MT_LPON(0x01c)
 
+#define MT_WF_MIB_BASE                 0x24800
+#define MT_WF_MIB(ofs)                 (MT_WF_MIB_BASE + (ofs))
+
+#define MT_MIB_M0_MISC_CR              MT_WF_MIB(0x00c)
+#define MT_MIB_MB_SDR0(n)              MT_WF_MIB(0x100 + ((n) << 4))
+#define MT_MIB_RTS_RETRIES_COUNT_MASK  GENMASK(31, 16)
+#define MT_MIB_RTS_COUNT_MASK          GENMASK(15, 0)
+
 #define MT_EFUSE_BASE                  0x81070000
 #define MT_EFUSE_BASE_CTRL             0x000
 #define MT_EFUSE_BASE_CTRL_EMPTY       BIT(30)