mt76: add energy detect CCA support to mt76x{0,2}e drivers

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>
This commit is contained in:
Lorenzo Bianconi 2018-12-12 22:51:55 +01:00 committed by Felix Fietkau
parent 3fd612df8a
commit f82ce8d9bd
9 changed files with 111 additions and 0 deletions

View 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);

View 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);

View 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];

View 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);

View 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);

View 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

View file

@ -318,6 +318,7 @@
#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)
@ -378,6 +379,9 @@
#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)
@ -398,6 +402,7 @@
#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)
@ -409,6 +414,7 @@
#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
@ -512,6 +518,7 @@
#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
@ -533,6 +540,7 @@
#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)

View 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);

View 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);