Commit f82ce8d9 authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Felix Fietkau
Browse files

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: default avatarLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 3fd612df
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+2 −0
Original line number Diff line number Diff line
@@ -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);

+6 −0
Original line number Diff line number Diff line
@@ -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];
+4 −0
Original line number Diff line number Diff line
@@ -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);
+85 −0
Original line number Diff line number Diff line
@@ -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);
Loading