4c28c0976e
We are introducing a new structure, mt792x_link_sta, to manage per-link configurations in preparation for future station (STA) support of Multi-Link Operation (MLO). This patch only includes structural changes and does not involve any logic changes. We have moved relevant parameters, such as the wcid from the mt76_wcid structure, from the mt7921x_sta structure to the mt792x_link_sta structure. For current drivers that do not support MLO, there is only one link STA, and link information is accessed via the deflink member. However, we have not yet created the per-link BSS configuration indexed by link ID for Multi-Link Device (MLD) support in mt7921x_sta. This step needs to be completed before adding MLD support for STA mode. Co-developed-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Deren Wu <deren.wu@mediatek.com> Link: https://patch.msgid.link/20240613030241.5771-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau <nbd@nbd.name>
388 lines
11 KiB
C
388 lines
11 KiB
C
// SPDX-License-Identifier: ISC
|
|
/* Copyright (C) 2023 MediaTek Inc. */
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include "mt792x.h"
|
|
#include "mt792x_regs.h"
|
|
|
|
void mt792x_mac_work(struct work_struct *work)
|
|
{
|
|
struct mt792x_phy *phy;
|
|
struct mt76_phy *mphy;
|
|
|
|
mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
|
|
mac_work.work);
|
|
phy = mphy->priv;
|
|
|
|
mt792x_mutex_acquire(phy->dev);
|
|
|
|
mt76_update_survey(mphy);
|
|
if (++mphy->mac_work_count == 2) {
|
|
mphy->mac_work_count = 0;
|
|
|
|
mt792x_mac_update_mib_stats(phy);
|
|
}
|
|
|
|
mt792x_mutex_release(phy->dev);
|
|
|
|
mt76_tx_status_check(mphy->dev, false);
|
|
ieee80211_queue_delayed_work(phy->mt76->hw, &mphy->mac_work,
|
|
MT792x_WATCHDOG_TIME);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_work);
|
|
|
|
void mt792x_mac_set_timeing(struct mt792x_phy *phy)
|
|
{
|
|
s16 coverage_class = phy->coverage_class;
|
|
struct mt792x_dev *dev = phy->dev;
|
|
u32 val, reg_offset;
|
|
u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) |
|
|
FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48);
|
|
u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) |
|
|
FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28);
|
|
bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ;
|
|
int sifs = is_2ghz ? 10 : 16, offset;
|
|
|
|
if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
|
|
return;
|
|
|
|
mt76_set(dev, MT_ARB_SCR(0),
|
|
MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
|
|
udelay(1);
|
|
|
|
offset = 3 * coverage_class;
|
|
reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) |
|
|
FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset);
|
|
|
|
mt76_wr(dev, MT_TMAC_CDTR(0), cck + reg_offset);
|
|
mt76_wr(dev, MT_TMAC_ODTR(0), ofdm + reg_offset);
|
|
mt76_wr(dev, MT_TMAC_ICR0(0),
|
|
FIELD_PREP(MT_IFS_EIFS, 360) |
|
|
FIELD_PREP(MT_IFS_RIFS, 2) |
|
|
FIELD_PREP(MT_IFS_SIFS, sifs) |
|
|
FIELD_PREP(MT_IFS_SLOT, phy->slottime));
|
|
|
|
if (phy->slottime < 20 || !is_2ghz)
|
|
val = MT792x_CFEND_RATE_DEFAULT;
|
|
else
|
|
val = MT792x_CFEND_RATE_11B;
|
|
|
|
mt76_rmw_field(dev, MT_AGG_ACR0(0), MT_AGG_ACR_CFEND_RATE, val);
|
|
mt76_clear(dev, MT_ARB_SCR(0),
|
|
MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_set_timeing);
|
|
|
|
void mt792x_mac_update_mib_stats(struct mt792x_phy *phy)
|
|
{
|
|
struct mt76_mib_stats *mib = &phy->mib;
|
|
struct mt792x_dev *dev = phy->dev;
|
|
int i, aggr0 = 0, aggr1;
|
|
u32 val;
|
|
|
|
mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(0),
|
|
MT_MIB_SDR3_FCS_ERR_MASK);
|
|
mib->ack_fail_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR3(0),
|
|
MT_MIB_ACK_FAIL_COUNT_MASK);
|
|
mib->ba_miss_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR2(0),
|
|
MT_MIB_BA_FAIL_COUNT_MASK);
|
|
mib->rts_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR0(0),
|
|
MT_MIB_RTS_COUNT_MASK);
|
|
mib->rts_retries_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR1(0),
|
|
MT_MIB_RTS_FAIL_COUNT_MASK);
|
|
|
|
mib->tx_ampdu_cnt += mt76_rr(dev, MT_MIB_SDR12(0));
|
|
mib->tx_mpdu_attempts_cnt += mt76_rr(dev, MT_MIB_SDR14(0));
|
|
mib->tx_mpdu_success_cnt += mt76_rr(dev, MT_MIB_SDR15(0));
|
|
|
|
val = mt76_rr(dev, MT_MIB_SDR32(0));
|
|
mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR9_EBF_CNT_MASK, val);
|
|
mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR9_IBF_CNT_MASK, val);
|
|
|
|
val = mt76_rr(dev, MT_ETBF_TX_APP_CNT(0));
|
|
mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, val);
|
|
mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, val);
|
|
|
|
val = mt76_rr(dev, MT_ETBF_RX_FB_CNT(0));
|
|
mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, val);
|
|
mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, val);
|
|
mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, val);
|
|
mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_ETBF_RX_FB_HT, val);
|
|
|
|
mib->rx_mpdu_cnt += mt76_rr(dev, MT_MIB_SDR5(0));
|
|
mib->rx_ampdu_cnt += mt76_rr(dev, MT_MIB_SDR22(0));
|
|
mib->rx_ampdu_bytes_cnt += mt76_rr(dev, MT_MIB_SDR23(0));
|
|
mib->rx_ba_cnt += mt76_rr(dev, MT_MIB_SDR31(0));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
|
|
val = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i));
|
|
mib->tx_amsdu[i] += val;
|
|
mib->tx_amsdu_cnt += val;
|
|
}
|
|
|
|
for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) {
|
|
u32 val2;
|
|
|
|
val = mt76_rr(dev, MT_TX_AGG_CNT(0, i));
|
|
val2 = mt76_rr(dev, MT_TX_AGG_CNT2(0, i));
|
|
|
|
phy->mt76->aggr_stats[aggr0++] += val & 0xffff;
|
|
phy->mt76->aggr_stats[aggr0++] += val >> 16;
|
|
phy->mt76->aggr_stats[aggr1++] += val2 & 0xffff;
|
|
phy->mt76->aggr_stats[aggr1++] += val2 >> 16;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_update_mib_stats);
|
|
|
|
struct mt76_wcid *mt792x_rx_get_wcid(struct mt792x_dev *dev, u16 idx,
|
|
bool unicast)
|
|
{
|
|
struct mt792x_link_sta *link;
|
|
struct mt792x_sta *sta;
|
|
struct mt76_wcid *wcid;
|
|
|
|
if (idx >= ARRAY_SIZE(dev->mt76.wcid))
|
|
return NULL;
|
|
|
|
wcid = rcu_dereference(dev->mt76.wcid[idx]);
|
|
if (unicast || !wcid)
|
|
return wcid;
|
|
|
|
if (!wcid->sta)
|
|
return NULL;
|
|
|
|
link = container_of(wcid, struct mt792x_link_sta, wcid);
|
|
sta = container_of(link, struct mt792x_sta, deflink);
|
|
if (!sta->vif)
|
|
return NULL;
|
|
|
|
return &sta->vif->sta.deflink.wcid;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_rx_get_wcid);
|
|
|
|
static void
|
|
mt792x_mac_rssi_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
|
|
{
|
|
struct sk_buff *skb = priv;
|
|
struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
|
|
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
|
|
struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
|
|
|
|
if (status->signal > 0)
|
|
return;
|
|
|
|
if (!ether_addr_equal(vif->addr, hdr->addr1))
|
|
return;
|
|
|
|
ewma_rssi_add(&mvif->bss_conf.rssi, -status->signal);
|
|
}
|
|
|
|
void mt792x_mac_assoc_rssi(struct mt792x_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
|
|
|
|
if (!ieee80211_is_assoc_resp(hdr->frame_control) &&
|
|
!ieee80211_is_auth(hdr->frame_control))
|
|
return;
|
|
|
|
ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
|
|
IEEE80211_IFACE_ITER_RESUME_ALL,
|
|
mt792x_mac_rssi_iter, skb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_assoc_rssi);
|
|
|
|
void mt792x_mac_reset_counters(struct mt792x_phy *phy)
|
|
{
|
|
struct mt792x_dev *dev = phy->dev;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
mt76_rr(dev, MT_TX_AGG_CNT(0, i));
|
|
mt76_rr(dev, MT_TX_AGG_CNT2(0, i));
|
|
}
|
|
|
|
dev->mt76.phy.survey_time = ktime_get_boottime();
|
|
memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
|
|
|
|
/* reset airtime counters */
|
|
mt76_rr(dev, MT_MIB_SDR9(0));
|
|
mt76_rr(dev, MT_MIB_SDR36(0));
|
|
mt76_rr(dev, MT_MIB_SDR37(0));
|
|
|
|
mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
|
|
mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_reset_counters);
|
|
|
|
static u8
|
|
mt792x_phy_get_nf(struct mt792x_phy *phy, int idx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mt792x_phy_update_channel(struct mt76_phy *mphy, int idx)
|
|
{
|
|
struct mt792x_dev *dev = container_of(mphy->dev, struct mt792x_dev, mt76);
|
|
struct mt792x_phy *phy = mphy->priv;
|
|
struct mt76_channel_state *state;
|
|
u64 busy_time, tx_time, rx_time, obss_time;
|
|
int nf;
|
|
|
|
busy_time = mt76_get_field(dev, MT_MIB_SDR9(idx),
|
|
MT_MIB_SDR9_BUSY_MASK);
|
|
tx_time = mt76_get_field(dev, MT_MIB_SDR36(idx),
|
|
MT_MIB_SDR36_TXTIME_MASK);
|
|
rx_time = mt76_get_field(dev, MT_MIB_SDR37(idx),
|
|
MT_MIB_SDR37_RXTIME_MASK);
|
|
obss_time = mt76_get_field(dev, MT_WF_RMAC_MIB_AIRTIME14(idx),
|
|
MT_MIB_OBSSTIME_MASK);
|
|
|
|
nf = mt792x_phy_get_nf(phy, idx);
|
|
if (!phy->noise)
|
|
phy->noise = nf << 4;
|
|
else if (nf)
|
|
phy->noise += nf - (phy->noise >> 4);
|
|
|
|
state = mphy->chan_state;
|
|
state->cc_busy += busy_time;
|
|
state->cc_tx += tx_time;
|
|
state->cc_rx += rx_time + obss_time;
|
|
state->cc_bss_rx += rx_time;
|
|
state->noise = -(phy->noise >> 4);
|
|
}
|
|
|
|
void mt792x_update_channel(struct mt76_phy *mphy)
|
|
{
|
|
struct mt792x_dev *dev = container_of(mphy->dev, struct mt792x_dev, mt76);
|
|
|
|
if (mt76_connac_pm_wake(mphy, &dev->pm))
|
|
return;
|
|
|
|
mt792x_phy_update_channel(mphy, 0);
|
|
/* reset obss airtime */
|
|
mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
|
|
mt76_connac_power_save_sched(mphy, &dev->pm);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_update_channel);
|
|
|
|
void mt792x_reset(struct mt76_dev *mdev)
|
|
{
|
|
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
|
|
struct mt76_connac_pm *pm = &dev->pm;
|
|
|
|
if (!dev->hw_init_done)
|
|
return;
|
|
|
|
if (dev->hw_full_reset)
|
|
return;
|
|
|
|
if (pm->suspended)
|
|
return;
|
|
|
|
queue_work(dev->mt76.wq, &dev->reset_work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_reset);
|
|
|
|
void mt792x_mac_init_band(struct mt792x_dev *dev, u8 band)
|
|
{
|
|
u32 mask, set;
|
|
|
|
mt76_rmw_field(dev, MT_TMAC_CTCR0(band),
|
|
MT_TMAC_CTCR0_INS_DDLMT_REFTIME, 0x3f);
|
|
mt76_set(dev, MT_TMAC_CTCR0(band),
|
|
MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN |
|
|
MT_TMAC_CTCR0_INS_DDLMT_EN);
|
|
|
|
mt76_set(dev, MT_WF_RMAC_MIB_TIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
|
|
mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
|
|
|
|
/* enable MIB tx-rx time reporting */
|
|
mt76_set(dev, MT_MIB_SCR1(band), MT_MIB_TXDUR_EN);
|
|
mt76_set(dev, MT_MIB_SCR1(band), MT_MIB_RXDUR_EN);
|
|
|
|
mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_MAX_RX_LEN, 1536);
|
|
/* disable rx rate report by default due to hw issues */
|
|
mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
|
|
|
|
/* filter out non-resp frames and get instantaneous signal reporting */
|
|
mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM;
|
|
set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) |
|
|
FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3);
|
|
mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_mac_init_band);
|
|
|
|
void mt792x_pm_wake_work(struct work_struct *work)
|
|
{
|
|
struct mt792x_dev *dev;
|
|
struct mt76_phy *mphy;
|
|
|
|
dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev,
|
|
pm.wake_work);
|
|
mphy = dev->phy.mt76;
|
|
|
|
if (!mt792x_mcu_drv_pmctrl(dev)) {
|
|
struct mt76_dev *mdev = &dev->mt76;
|
|
int i;
|
|
|
|
if (mt76_is_sdio(mdev)) {
|
|
mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
|
|
mt76_worker_schedule(&mdev->sdio.txrx_worker);
|
|
} else {
|
|
local_bh_disable();
|
|
mt76_for_each_q_rx(mdev, i)
|
|
napi_schedule(&mdev->napi[i]);
|
|
local_bh_enable();
|
|
mt76_connac_pm_dequeue_skbs(mphy, &dev->pm);
|
|
mt76_connac_tx_cleanup(mdev);
|
|
}
|
|
if (test_bit(MT76_STATE_RUNNING, &mphy->state))
|
|
ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
|
|
MT792x_WATCHDOG_TIME);
|
|
}
|
|
|
|
ieee80211_wake_queues(mphy->hw);
|
|
wake_up(&dev->pm.wait);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_pm_wake_work);
|
|
|
|
void mt792x_pm_power_save_work(struct work_struct *work)
|
|
{
|
|
struct mt792x_dev *dev;
|
|
unsigned long delta;
|
|
struct mt76_phy *mphy;
|
|
|
|
dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev,
|
|
pm.ps_work.work);
|
|
mphy = dev->phy.mt76;
|
|
|
|
delta = dev->pm.idle_timeout;
|
|
if (test_bit(MT76_HW_SCANNING, &mphy->state) ||
|
|
test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) ||
|
|
dev->fw_assert)
|
|
goto out;
|
|
|
|
if (mutex_is_locked(&dev->mt76.mutex))
|
|
/* if mt76 mutex is held we should not put the device
|
|
* to sleep since we are currently accessing device
|
|
* register map. We need to wait for the next power_save
|
|
* trigger.
|
|
*/
|
|
goto out;
|
|
|
|
if (time_is_after_jiffies(dev->pm.last_activity + delta)) {
|
|
delta = dev->pm.last_activity + delta - jiffies;
|
|
goto out;
|
|
}
|
|
|
|
if (!mt792x_mcu_fw_pmctrl(dev)) {
|
|
cancel_delayed_work_sync(&mphy->mac_work);
|
|
return;
|
|
}
|
|
out:
|
|
queue_delayed_work(dev->mt76.wq, &dev->pm.ps_work, delta);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_pm_power_save_work);
|