Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT76x2_COMMON
tristate
select MT76x02_LIB
config MT76x2E
tristate "MediaTek MT76x2E (PCIe) support"
select MT76x2_COMMON
depends on MAC80211
depends on PCI
help
This adds support for MT7612/MT7602/MT7662-based PCIe wireless
devices, which comply with IEEE 802.11ac standards and support
2SS to 866Mbit/s PHY rate.
To compile this driver as a module, choose M here.
config MT76x2U
tristate "MediaTek MT76x2U (USB) support"
select MT76x2_COMMON
select MT76x02_USB
depends on MAC80211
depends on USB
help
This adds support for MT7612U-based USB 3.0 wireless dongles,
which comply with IEEE 802.11ac standards and support 2SS to
866Mbit/s PHY rate.
To compile this driver as a module, choose M here.
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT76x2_COMMON) += mt76x2-common.o
obj-$(CONFIG_MT76x2E) += mt76x2e.o
obj-$(CONFIG_MT76x2U) += mt76x2u.o
mt76x2-common-y := \
eeprom.o mac.o init.o phy.o mcu.o
mt76x2e-y := \
pci.o pci_main.o pci_init.o pci_mcu.o \
pci_phy.o
mt76x2u-y := \
usb.o usb_init.o usb_main.o usb_mac.o usb_mcu.o \
usb_phy.o
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/unaligned.h>
#include "mt76x2.h"
#include "eeprom.h"
#define EE_FIELD(_name, _value) [MT_EE_##_name] = (_value) | 1
static int
mt76x2_eeprom_get_macaddr(struct mt76x02_dev *dev)
{
void *src = dev->mt76.eeprom.data + MT_EE_MAC_ADDR;
memcpy(dev->mphy.macaddr, src, ETH_ALEN);
return 0;
}
static bool
mt76x2_has_cal_free_data(struct mt76x02_dev *dev, u8 *efuse)
{
u16 *efuse_w = (u16 *)efuse;
if (efuse_w[MT_EE_NIC_CONF_0] != 0)
return false;
if (efuse_w[MT_EE_XTAL_TRIM_1] == 0xffff)
return false;
if (efuse_w[MT_EE_TX_POWER_DELTA_BW40] != 0)
return false;
if (efuse_w[MT_EE_TX_POWER_0_START_2G] == 0xffff)
return false;
if (efuse_w[MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA] != 0)
return false;
if (efuse_w[MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE] == 0xffff)
return false;
return true;
}
static void
mt76x2_apply_cal_free_data(struct mt76x02_dev *dev, u8 *efuse)
{
#define GROUP_5G(_id) \
MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \
MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1, \
MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \
MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1
static const u8 cal_free_bytes[] = {
MT_EE_XTAL_TRIM_1,
MT_EE_TX_POWER_EXT_PA_5G + 1,
MT_EE_TX_POWER_0_START_2G,
MT_EE_TX_POWER_0_START_2G + 1,
MT_EE_TX_POWER_1_START_2G,
MT_EE_TX_POWER_1_START_2G + 1,
GROUP_5G(0),
GROUP_5G(1),
GROUP_5G(2),
GROUP_5G(3),
GROUP_5G(4),
GROUP_5G(5),
MT_EE_RF_2G_TSSI_OFF_TXPOWER,
MT_EE_RF_2G_RX_HIGH_GAIN + 1,
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN,
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN + 1,
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN,
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN + 1,
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN,
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN + 1,
};
struct device_node *np = dev->mt76.dev->of_node;
u8 *eeprom = dev->mt76.eeprom.data;
u8 prev_grp0[4] = {
eeprom[MT_EE_TX_POWER_0_START_5G],
eeprom[MT_EE_TX_POWER_0_START_5G + 1],
eeprom[MT_EE_TX_POWER_1_START_5G],
eeprom[MT_EE_TX_POWER_1_START_5G + 1]
};
u16 val;
int i;
if (!np || !of_property_read_bool(np, "mediatek,eeprom-merge-otp"))
return;
if (!mt76x2_has_cal_free_data(dev, efuse))
return;
for (i = 0; i < ARRAY_SIZE(cal_free_bytes); i++) {
int offset = cal_free_bytes[i];
eeprom[offset] = efuse[offset];
}
if (!(efuse[MT_EE_TX_POWER_0_START_5G] |
efuse[MT_EE_TX_POWER_0_START_5G + 1]))
memcpy(eeprom + MT_EE_TX_POWER_0_START_5G, prev_grp0, 2);
if (!(efuse[MT_EE_TX_POWER_1_START_5G] |
efuse[MT_EE_TX_POWER_1_START_5G + 1]))
memcpy(eeprom + MT_EE_TX_POWER_1_START_5G, prev_grp0 + 2, 2);
val = get_unaligned_le16(efuse + MT_EE_BT_RCAL_RESULT);
if (val != 0xffff)
eeprom[MT_EE_BT_RCAL_RESULT] = val & 0xff;
val = get_unaligned_le16(efuse + MT_EE_BT_VCDL_CALIBRATION);
if (val != 0xffff)
eeprom[MT_EE_BT_VCDL_CALIBRATION + 1] = val >> 8;
val = get_unaligned_le16(efuse + MT_EE_BT_PMUCFG);
if (val != 0xffff)
eeprom[MT_EE_BT_PMUCFG] = val & 0xff;
}
static int mt76x2_check_eeprom(struct mt76x02_dev *dev)
{
u16 val = get_unaligned_le16(dev->mt76.eeprom.data);
if (!val)
val = get_unaligned_le16(dev->mt76.eeprom.data + MT_EE_PCI_ID);
switch (val) {
case 0x7662:
case 0x7612:
return 0;
default:
dev_err(dev->mt76.dev, "EEPROM data check failed: %04x\n", val);
return -EINVAL;
}
}
static int
mt76x2_eeprom_load(struct mt76x02_dev *dev)
{
void *efuse;
bool found;
int ret;
ret = mt76_eeprom_init(&dev->mt76, MT7662_EEPROM_SIZE);
if (ret < 0)
return ret;
found = ret;
if (found)
found = !mt76x2_check_eeprom(dev);
dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, MT7662_EEPROM_SIZE,
GFP_KERNEL);
dev->mt76.otp.size = MT7662_EEPROM_SIZE;
if (!dev->mt76.otp.data)
return -ENOMEM;
efuse = dev->mt76.otp.data;
if (mt76x02_get_efuse_data(dev, 0, efuse, MT7662_EEPROM_SIZE,
MT_EE_READ))
goto out;
if (found) {
mt76x2_apply_cal_free_data(dev, efuse);
} else {
/* FIXME: check if efuse data is complete */
found = true;
memcpy(dev->mt76.eeprom.data, efuse, MT7662_EEPROM_SIZE);
}
out:
if (!found)
return -ENOENT;
return 0;
}
static void
mt76x2_set_rx_gain_group(struct mt76x02_dev *dev, u8 val)
{
s8 *dest = dev->cal.rx.high_gain;
if (!mt76x02_field_valid(val)) {
dest[0] = 0;
dest[1] = 0;
return;
}
dest[0] = mt76x02_sign_extend(val, 4);
dest[1] = mt76x02_sign_extend(val >> 4, 4);
}
static void
mt76x2_set_rssi_offset(struct mt76x02_dev *dev, int chain, u8 val)
{
s8 *dest = dev->cal.rx.rssi_offset;
if (!mt76x02_field_valid(val)) {
dest[chain] = 0;
return;
}
dest[chain] = mt76x02_sign_extend_optional(val, 7);
}
static enum mt76x2_cal_channel_group
mt76x2_get_cal_channel_group(int channel)
{
if (channel >= 184 && channel <= 196)
return MT_CH_5G_JAPAN;
if (channel <= 48)
return MT_CH_5G_UNII_1;
if (channel <= 64)
return MT_CH_5G_UNII_2;
if (channel <= 114)
return MT_CH_5G_UNII_2E_1;
if (channel <= 144)
return MT_CH_5G_UNII_2E_2;
return MT_CH_5G_UNII_3;
}
static u8
mt76x2_get_5g_rx_gain(struct mt76x02_dev *dev, u8 channel)
{
enum mt76x2_cal_channel_group group;
group = mt76x2_get_cal_channel_group(channel);
switch (group) {
case MT_CH_5G_JAPAN:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN);
case MT_CH_5G_UNII_1:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN) >> 8;
case MT_CH_5G_UNII_2:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN);
case MT_CH_5G_UNII_2E_1:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN) >> 8;
case MT_CH_5G_UNII_2E_2:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN);
default:
return mt76x02_eeprom_get(dev,
MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN) >> 8;
}
}
void mt76x2_read_rx_gain(struct mt76x02_dev *dev)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
int channel = chan->hw_value;
s8 lna_5g[3], lna_2g;
bool use_lna;
u8 lna = 0;
u16 val;
if (chan->band == NL80211_BAND_2GHZ)
val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN) >> 8;
else
val = mt76x2_get_5g_rx_gain(dev, channel);
mt76x2_set_rx_gain_group(dev, val);
mt76x02_get_rx_gain(dev, chan->band, &val, &lna_2g, lna_5g);
mt76x2_set_rssi_offset(dev, 0, val);
mt76x2_set_rssi_offset(dev, 1, val >> 8);
dev->cal.rx.mcu_gain = (lna_2g & 0xff);
dev->cal.rx.mcu_gain |= (lna_5g[0] & 0xff) << 8;
dev->cal.rx.mcu_gain |= (lna_5g[1] & 0xff) << 16;
dev->cal.rx.mcu_gain |= (lna_5g[2] & 0xff) << 24;
val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1);
if (chan->band == NL80211_BAND_2GHZ)
use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_2G);
else
use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_5G);
if (use_lna)
lna = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan);
dev->cal.rx.lna_gain = mt76x02_sign_extend(lna, 8);
}
EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain);
void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t,
struct ieee80211_channel *chan)
{
bool is_5ghz;
u16 val;
is_5ghz = chan->band == NL80211_BAND_5GHZ;
memset(t, 0, sizeof(*t));
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_CCK);
t->cck[0] = t->cck[1] = mt76x02_rate_power_val(val);
t->cck[2] = t->cck[3] = mt76x02_rate_power_val(val >> 8);
if (is_5ghz)
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_6M);
else
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_6M);
t->ofdm[0] = t->ofdm[1] = mt76x02_rate_power_val(val);
t->ofdm[2] = t->ofdm[3] = mt76x02_rate_power_val(val >> 8);
if (is_5ghz)
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_24M);
else
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_24M);
t->ofdm[4] = t->ofdm[5] = mt76x02_rate_power_val(val);
t->ofdm[6] = t->ofdm[7] = mt76x02_rate_power_val(val >> 8);
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS0);
t->ht[0] = t->ht[1] = mt76x02_rate_power_val(val);
t->ht[2] = t->ht[3] = mt76x02_rate_power_val(val >> 8);
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS4);
t->ht[4] = t->ht[5] = mt76x02_rate_power_val(val);
t->ht[6] = t->ht[7] = mt76x02_rate_power_val(val >> 8);
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS8);
t->ht[8] = t->ht[9] = mt76x02_rate_power_val(val);
t->ht[10] = t->ht[11] = mt76x02_rate_power_val(val >> 8);
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS12);
t->ht[12] = t->ht[13] = mt76x02_rate_power_val(val);
t->ht[14] = t->ht[15] = mt76x02_rate_power_val(val >> 8);
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8);
if (!is_5ghz)
val >>= 8;
t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val >> 8);
}
EXPORT_SYMBOL_GPL(mt76x2_get_rate_power);
static void
mt76x2_get_power_info_2g(struct mt76x02_dev *dev,
struct mt76x2_tx_power_info *t,
struct ieee80211_channel *chan,
int chain, int offset)
{
int channel = chan->hw_value;
int delta_idx;
u8 data[6];
u16 val;
if (channel < 6)
delta_idx = 3;
else if (channel < 11)
delta_idx = 4;
else
delta_idx = 5;
mt76x02_eeprom_copy(dev, offset, data, sizeof(data));
t->chain[chain].tssi_slope = data[0];
t->chain[chain].tssi_offset = data[1];
t->chain[chain].target_power = data[2];
t->chain[chain].delta =
mt76x02_sign_extend_optional(data[delta_idx], 7);
val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_TSSI_OFF_TXPOWER);
t->target_power = val >> 8;
}
static void
mt76x2_get_power_info_5g(struct mt76x02_dev *dev,
struct mt76x2_tx_power_info *t,
struct ieee80211_channel *chan,
int chain, int offset)
{
int channel = chan->hw_value;
enum mt76x2_cal_channel_group group;
int delta_idx;
u16 val;
u8 data[5];
group = mt76x2_get_cal_channel_group(channel);
offset += group * MT_TX_POWER_GROUP_SIZE_5G;
if (channel >= 192)
delta_idx = 4;
else if (channel >= 184)
delta_idx = 3;
else if (channel < 44)
delta_idx = 3;
else if (channel < 52)
delta_idx = 4;
else if (channel < 58)
delta_idx = 3;
else if (channel < 98)
delta_idx = 4;
else if (channel < 106)
delta_idx = 3;
else if (channel < 116)
delta_idx = 4;
else if (channel < 130)
delta_idx = 3;
else if (channel < 149)
delta_idx = 4;
else if (channel < 157)
delta_idx = 3;
else
delta_idx = 4;
mt76x02_eeprom_copy(dev, offset, data, sizeof(data));
t->chain[chain].tssi_slope = data[0];
t->chain[chain].tssi_offset = data[1];
t->chain[chain].target_power = data[2];
t->chain[chain].delta =
mt76x02_sign_extend_optional(data[delta_idx], 7);
val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN);
t->target_power = val & 0xff;
}
void mt76x2_get_power_info(struct mt76x02_dev *dev,
struct mt76x2_tx_power_info *t,
struct ieee80211_channel *chan)
{
u16 bw40, bw80;
memset(t, 0, sizeof(*t));
bw40 = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40);
bw80 = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80);
if (chan->band == NL80211_BAND_5GHZ) {
bw40 >>= 8;
mt76x2_get_power_info_5g(dev, t, chan, 0,
MT_EE_TX_POWER_0_START_5G);
mt76x2_get_power_info_5g(dev, t, chan, 1,
MT_EE_TX_POWER_1_START_5G);
} else {
mt76x2_get_power_info_2g(dev, t, chan, 0,
MT_EE_TX_POWER_0_START_2G);
mt76x2_get_power_info_2g(dev, t, chan, 1,
MT_EE_TX_POWER_1_START_2G);
}
if (mt76x2_tssi_enabled(dev) ||
!mt76x02_field_valid(t->target_power))
t->target_power = t->chain[0].target_power;
t->delta_bw40 = mt76x02_rate_power_val(bw40);
t->delta_bw80 = mt76x02_rate_power_val(bw80);
}
EXPORT_SYMBOL_GPL(mt76x2_get_power_info);
int mt76x2_get_temp_comp(struct mt76x02_dev *dev, struct mt76x2_temp_comp *t)
{
enum nl80211_band band = dev->mphy.chandef.chan->band;
u16 val, slope;
u8 bounds;
memset(t, 0, sizeof(*t));
if (!mt76x2_temp_tx_alc_enabled(dev))
return -EINVAL;
if (!mt76x02_ext_pa_enabled(dev, band))
return -EINVAL;
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G) >> 8;
t->temp_25_ref = val & 0x7f;
if (band == NL80211_BAND_5GHZ) {
slope = mt76x02_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_5G);
bounds = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G);
} else {
slope = mt76x02_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_2G);
bounds = mt76x02_eeprom_get(dev,
MT_EE_TX_POWER_DELTA_BW80) >> 8;
}
t->high_slope = slope & 0xff;
t->low_slope = slope >> 8;
t->lower_bound = 0 - (bounds & 0xf);
t->upper_bound = (bounds >> 4) & 0xf;
return 0;
}
EXPORT_SYMBOL_GPL(mt76x2_get_temp_comp);
int mt76x2_eeprom_init(struct mt76x02_dev *dev)
{
int ret;
ret = mt76x2_eeprom_load(dev);
if (ret)
return ret;
mt76x02_eeprom_parse_hw_cap(dev);
mt76x2_eeprom_get_macaddr(dev);
ret = mt76_eeprom_override(&dev->mphy);
if (ret)
return ret;
dev->mphy.macaddr[0] &= ~BIT(1);
return 0;
}
EXPORT_SYMBOL_GPL(mt76x2_eeprom_init);
MODULE_DESCRIPTION("MediaTek MT76x2 EEPROM helpers");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#ifndef __MT76x2_EEPROM_H
#define __MT76x2_EEPROM_H
#include "../mt76x02_eeprom.h"
enum mt76x2_cal_channel_group {
MT_CH_5G_JAPAN,
MT_CH_5G_UNII_1,
MT_CH_5G_UNII_2,
MT_CH_5G_UNII_2E_1,
MT_CH_5G_UNII_2E_2,
MT_CH_5G_UNII_3,
__MT_CH_MAX
};
struct mt76x2_tx_power_info {
u8 target_power;
s8 delta_bw40;
s8 delta_bw80;
struct {
s8 tssi_slope;
s8 tssi_offset;
s8 target_power;
s8 delta;
} chain[MT_MAX_CHAINS];
};
struct mt76x2_temp_comp {
u8 temp_25_ref;
int lower_bound; /* J */
int upper_bound; /* J */
unsigned int high_slope; /* J / dB */
unsigned int low_slope; /* J / dB */
};
void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t,
struct ieee80211_channel *chan);
void mt76x2_get_power_info(struct mt76x02_dev *dev,
struct mt76x2_tx_power_info *t,
struct ieee80211_channel *chan);
int mt76x2_get_temp_comp(struct mt76x02_dev *dev, struct mt76x2_temp_comp *t);
void mt76x2_read_rx_gain(struct mt76x02_dev *dev);
static inline bool
mt76x2_has_ext_lna(struct mt76x02_dev *dev)
{
u32 val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1);
if (dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ)
return val & MT_EE_NIC_CONF_1_LNA_EXT_2G;
else
return val & MT_EE_NIC_CONF_1_LNA_EXT_5G;
}
static inline bool
mt76x2_temp_tx_alc_enabled(struct mt76x02_dev *dev)
{
u16 val;
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G);
if (!(val & BIT(15)))
return false;
return mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) &
MT_EE_NIC_CONF_1_TEMP_TX_ALC;
}
static inline bool
mt76x2_tssi_enabled(struct mt76x02_dev *dev)
{
return !mt76x2_temp_tx_alc_enabled(dev) &&
(mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) &
MT_EE_NIC_CONF_1_TX_ALC_EN);
}
#endif
@@ -0,0 +1,204 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2.h"
#include "eeprom.h"
#include "../mt76x02_phy.h"
int mt76x2_set_sar_specs(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar)
{
int err = -EINVAL, power = hw->conf.power_level * 2;
struct mt76x02_dev *dev = hw->priv;
struct mt76_phy *mphy = &dev->mphy;
mutex_lock(&dev->mt76.mutex);
if (!cfg80211_chandef_valid(&mphy->chandef))
goto out;
err = mt76_init_sar_power(hw, sar);
if (err)
goto out;
dev->txpower_conf = mt76_get_sar_power(mphy, mphy->chandef.chan,
power);
/* convert to per-chain power for 2x2 devices */
dev->txpower_conf -= 6;
if (test_bit(MT76_STATE_RUNNING, &mphy->state))
mt76x2_phy_set_txpower(dev);
out:
mutex_unlock(&dev->mt76.mutex);
return err;
}
EXPORT_SYMBOL_GPL(mt76x2_set_sar_specs);
static void
mt76x2_set_wlan_state(struct mt76x02_dev *dev, bool enable)
{
u32 val = mt76_rr(dev, MT_WLAN_FUN_CTRL);
if (enable)
val |= (MT_WLAN_FUN_CTRL_WLAN_EN |
MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
else
val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN |
MT_WLAN_FUN_CTRL_WLAN_CLK_EN);
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
}
void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable)
{
u32 val;
if (!enable)
goto out;
val = mt76_rr(dev, MT_WLAN_FUN_CTRL);
val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL;
if (val & MT_WLAN_FUN_CTRL_WLAN_EN) {
val |= MT_WLAN_FUN_CTRL_WLAN_RESET_RF;
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
val &= ~MT_WLAN_FUN_CTRL_WLAN_RESET_RF;
}
mt76_wr(dev, MT_WLAN_FUN_CTRL, val);
udelay(20);
out:
mt76x2_set_wlan_state(dev, enable);
}
EXPORT_SYMBOL_GPL(mt76x2_reset_wlan);
void mt76_write_mac_initvals(struct mt76x02_dev *dev)
{
#define DEFAULT_PROT_CFG_CCK \
(FIELD_PREP(MT_PROT_CFG_RATE, 0x3) | \
FIELD_PREP(MT_PROT_CFG_NAV, 1) | \
FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f) | \
MT_PROT_CFG_RTS_THRESH)
#define DEFAULT_PROT_CFG_OFDM \
(FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \
FIELD_PREP(MT_PROT_CFG_NAV, 1) | \
FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f) | \
MT_PROT_CFG_RTS_THRESH)
#define DEFAULT_PROT_CFG_20 \
(FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \
FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \
FIELD_PREP(MT_PROT_CFG_NAV, 1) | \
FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x17))
#define DEFAULT_PROT_CFG_40 \
(FIELD_PREP(MT_PROT_CFG_RATE, 0x2084) | \
FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \
FIELD_PREP(MT_PROT_CFG_NAV, 1) | \
FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f))
static const struct mt76_reg_pair vals[] = {
/* Copied from MediaTek reference source */
{ MT_PBF_SYS_CTRL, 0x00080c00 },
{ MT_PBF_CFG, 0x1efebcff },
{ MT_FCE_PSE_CTRL, 0x00000001 },
{ MT_MAC_SYS_CTRL, 0x00000000 },
{ MT_MAX_LEN_CFG, 0x003e3f00 },
{ MT_AMPDU_MAX_LEN_20M1S, 0xaaa99887 },
{ MT_AMPDU_MAX_LEN_20M2S, 0x000000aa },
{ MT_XIFS_TIME_CFG, 0x33a40d0a },
{ MT_BKOFF_SLOT_CFG, 0x00000209 },
{ MT_TBTT_SYNC_CFG, 0x00422010 },
{ MT_PWR_PIN_CFG, 0x00000000 },
{ 0x1238, 0x001700c8 },
{ MT_TX_SW_CFG0, 0x00101001 },
{ MT_TX_SW_CFG1, 0x00010000 },
{ MT_TX_SW_CFG2, 0x00000000 },
{ MT_TXOP_CTRL_CFG, 0x0400583f },
{ MT_TX_RTS_CFG, 0x00ffff20 },
{ MT_TX_TIMEOUT_CFG, 0x000a2290 },
{ MT_TX_RETRY_CFG, 0x47f01f0f },
{ MT_EXP_ACK_TIME, 0x002c00dc },
{ MT_TX_PROT_CFG6, 0xe3f42004 },
{ MT_TX_PROT_CFG7, 0xe3f42084 },
{ MT_TX_PROT_CFG8, 0xe3f42104 },
{ MT_PIFS_TX_CFG, 0x00060fff },
{ MT_RX_FILTR_CFG, 0x00015f97 },
{ MT_LEGACY_BASIC_RATE, 0x0000017f },
{ MT_HT_BASIC_RATE, 0x00004003 },
{ MT_PN_PAD_MODE, 0x00000003 },
{ MT_TXOP_HLDR_ET, 0x00000002 },
{ 0xa44, 0x00000000 },
{ MT_HEADER_TRANS_CTRL_REG, 0x00000000 },
{ MT_TSO_CTRL, 0x00000000 },
{ MT_AUX_CLK_CFG, 0x00000000 },
{ MT_DACCLK_EN_DLY_CFG, 0x00000000 },
{ MT_TX_ALC_CFG_4, 0x00000000 },
{ MT_TX_ALC_VGA3, 0x00000000 },
{ MT_TX_PWR_CFG_0, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_1, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_2, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_3, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_4, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_7, 0x3a3a3a3a },
{ MT_TX_PWR_CFG_8, 0x0000003a },
{ MT_TX_PWR_CFG_9, 0x0000003a },
{ MT_EFUSE_CTRL, 0x0000d000 },
{ MT_PAUSE_ENABLE_CONTROL1, 0x0000000a },
{ MT_FCE_WLAN_FLOW_CONTROL1, 0x60401c18 },
{ MT_WPDMA_DELAY_INT_CFG, 0x94ff0000 },
{ MT_TX_SW_CFG3, 0x00000004 },
{ MT_HT_FBK_TO_LEGACY, 0x00001818 },
{ MT_VHT_HT_FBK_CFG1, 0xedcba980 },
{ MT_PROT_AUTO_TX_CFG, 0x00830083 },
{ MT_HT_CTRL_CFG, 0x000001ff },
{ MT_TX_LINK_CFG, 0x00001020 },
};
struct mt76_reg_pair prot_vals[] = {
{ MT_CCK_PROT_CFG, DEFAULT_PROT_CFG_CCK },
{ MT_OFDM_PROT_CFG, DEFAULT_PROT_CFG_OFDM },
{ MT_MM20_PROT_CFG, DEFAULT_PROT_CFG_20 },
{ MT_MM40_PROT_CFG, DEFAULT_PROT_CFG_40 },
{ MT_GF20_PROT_CFG, DEFAULT_PROT_CFG_20 },
{ MT_GF40_PROT_CFG, DEFAULT_PROT_CFG_40 },
};
mt76_wr_rp(dev, 0, vals, ARRAY_SIZE(vals));
mt76_wr_rp(dev, 0, prot_vals, ARRAY_SIZE(prot_vals));
}
EXPORT_SYMBOL_GPL(mt76_write_mac_initvals);
void mt76x2_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband)
{
struct ieee80211_channel *chan;
struct mt76x2_tx_power_info txp;
struct mt76x02_rate_power t = {};
int i;
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
mt76x2_get_power_info(dev, &txp, chan);
mt76x2_get_rate_power(dev, &t, chan);
chan->orig_mpwr = mt76x02_get_max_rate_power(&t) +
txp.target_power;
chan->orig_mpwr = DIV_ROUND_UP(chan->orig_mpwr, 2);
/* convert to combined output power on 2x2 devices */
chan->orig_mpwr += 3;
chan->max_power = min_t(int, chan->max_reg_power,
chan->orig_mpwr);
}
}
EXPORT_SYMBOL_GPL(mt76x2_init_txpower);
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2.h"
void mt76x2_mac_stop(struct mt76x02_dev *dev, bool force)
{
bool stopped = false;
u32 rts_cfg;
int i;
mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
mt76_clear(dev, MT_TXOP_HLDR_ET, MT_TXOP_HLDR_TX40M_BLK_EN);
mt76_wr(dev, MT_MAC_SYS_CTRL, 0);
rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG);
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT);
/* Wait for MAC to become idle */
for (i = 0; i < 300; i++) {
if ((mt76_rr(dev, MT_MAC_STATUS) &
(MT_MAC_STATUS_RX | MT_MAC_STATUS_TX)) ||
mt76_rr(dev, MT_BBP(IBI, 12))) {
udelay(1);
continue;
}
stopped = true;
break;
}
if (force && !stopped) {
mt76_set(dev, MT_BBP(CORE, 4), BIT(1));
mt76_clear(dev, MT_BBP(CORE, 4), BIT(1));
mt76_set(dev, MT_BBP(CORE, 4), BIT(0));
mt76_clear(dev, MT_BBP(CORE, 4), BIT(0));
}
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg);
}
EXPORT_SYMBOL_GPL(mt76x2_mac_stop);
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#ifndef __MT76x2_MAC_H
#define __MT76x2_MAC_H
#include "mt76x2.h"
struct mt76x02_dev;
struct mt76x2_sta;
struct mt76x02_vif;
void mt76x2_mac_stop(struct mt76x02_dev *dev, bool force);
static inline void mt76x2_mac_resume(struct mt76x02_dev *dev)
{
mt76_wr(dev, MT_MAC_SYS_CTRL,
MT_MAC_SYS_CTRL_ENABLE_TX |
MT_MAC_SYS_CTRL_ENABLE_RX);
}
#endif
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include "mt76x2.h"
#include "mcu.h"
#include "eeprom.h"
int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw,
u8 bw_index, bool scan)
{
struct {
u8 idx;
u8 scan;
u8 bw;
u8 _pad0;
__le16 chainmask;
u8 ext_chan;
u8 _pad1;
} __packed __aligned(4) msg = {
.idx = channel,
.scan = scan,
.bw = bw,
.chainmask = cpu_to_le16(dev->mphy.chainmask),
};
/* first set the channel without the extension channel info */
mt76_mcu_send_msg(&dev->mt76, CMD_SWITCH_CHANNEL_OP, &msg,
sizeof(msg), true);
usleep_range(5000, 10000);
msg.ext_chan = 0xe0 + bw_index;
return mt76_mcu_send_msg(&dev->mt76, CMD_SWITCH_CHANNEL_OP, &msg,
sizeof(msg), true);
}
EXPORT_SYMBOL_GPL(mt76x2_mcu_set_channel);
int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
u8 channel)
{
struct {
u8 cr_mode;
u8 temp;
u8 ch;
u8 _pad0;
__le32 cfg;
} __packed __aligned(4) msg = {
.cr_mode = type,
.temp = temp_level,
.ch = channel,
};
u32 val;
val = BIT(31);
val |= (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff;
val |= (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00;
msg.cfg = cpu_to_le32(val);
/* first set the channel without the extension channel info */
return mt76_mcu_send_msg(&dev->mt76, CMD_LOAD_CR, &msg, sizeof(msg),
true);
}
EXPORT_SYMBOL_GPL(mt76x2_mcu_load_cr);
int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain,
bool force)
{
struct {
__le32 channel;
__le32 gain_val;
} __packed __aligned(4) msg = {
.channel = cpu_to_le32(channel),
.gain_val = cpu_to_le32(gain),
};
if (force)
msg.channel |= cpu_to_le32(BIT(31));
return mt76_mcu_send_msg(&dev->mt76, CMD_INIT_GAIN_OP, &msg,
sizeof(msg), true);
}
EXPORT_SYMBOL_GPL(mt76x2_mcu_init_gain);
int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev,
struct mt76x2_tssi_comp *tssi_data)
{
struct {
__le32 id;
struct mt76x2_tssi_comp data;
} __packed __aligned(4) msg = {
.id = cpu_to_le32(MCU_CAL_TSSI_COMP),
.data = *tssi_data,
};
return mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg,
sizeof(msg), true);
}
EXPORT_SYMBOL_GPL(mt76x2_mcu_tssi_comp);
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#ifndef __MT76x2_MCU_H
#define __MT76x2_MCU_H
#include "../mt76x02_mcu.h"
/* Register definitions */
#define MT_MCU_CPU_CTL 0x0704
#define MT_MCU_CLOCK_CTL 0x0708
#define MT_MCU_PCIE_REMAP_BASE1 0x0740
#define MT_MCU_PCIE_REMAP_BASE2 0x0744
#define MT_MCU_PCIE_REMAP_BASE3 0x0748
#define MT_MCU_ROM_PATCH_OFFSET 0x80000
#define MT_MCU_ROM_PATCH_ADDR 0x90000
#define MT_MCU_ILM_OFFSET 0x80000
#define MT_MCU_DLM_OFFSET 0x100000
#define MT_MCU_DLM_ADDR 0x90000
#define MT_MCU_DLM_ADDR_E3 0x90800
enum mcu_calibration {
MCU_CAL_R = 1,
MCU_CAL_TEMP_SENSOR,
MCU_CAL_RXDCOC,
MCU_CAL_RC,
MCU_CAL_SX_LOGEN,
MCU_CAL_LC,
MCU_CAL_TX_LOFT,
MCU_CAL_TXIQ,
MCU_CAL_TSSI,
MCU_CAL_TSSI_COMP,
MCU_CAL_DPD,
MCU_CAL_RXIQC_FI,
MCU_CAL_RXIQC_FD,
MCU_CAL_PWRON,
MCU_CAL_TX_SHAPING,
};
enum mt76x2_mcu_cr_mode {
MT_RF_CR,
MT_BBP_CR,
MT_RF_BBP_CR,
MT_HL_TEMP_CR_UPDATE,
};
struct mt76x2_tssi_comp {
u8 pa_mode;
u8 cal_mode;
u16 pad;
u8 slope0;
u8 slope1;
u8 offset0;
u8 offset1;
} __packed __aligned(4);
int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev,
struct mt76x2_tssi_comp *tssi_data);
int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain,
bool force);
#endif
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#ifndef __MT76x2_H
#define __MT76x2_H
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
#define MT7662_FIRMWARE "mt7662.bin"
#define MT7662_ROM_PATCH "mt7662_rom_patch.bin"
#define MT7662_EEPROM_SIZE 512
#include "../mt76x02.h"
#include "mac.h"
static inline bool is_mt7612(struct mt76x02_dev *dev)
{
return mt76_chip(&dev->mt76) == 0x7612;
}
static inline bool mt76x2_channel_silent(struct mt76x02_dev *dev)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
return ((chan->flags & IEEE80211_CHAN_RADAR) &&
chan->dfs_state != NL80211_DFS_AVAILABLE);
}
extern const struct ieee80211_ops mt76x2_ops;
int mt76x2_register_device(struct mt76x02_dev *dev);
int mt76x2_resume_device(struct mt76x02_dev *dev);
int mt76x2_set_sar_specs(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar);
void mt76x2_phy_power_on(struct mt76x02_dev *dev);
void mt76x2_stop_hardware(struct mt76x02_dev *dev);
int mt76x2_eeprom_init(struct mt76x02_dev *dev);
int mt76x2_apply_calibration_data(struct mt76x02_dev *dev, int channel);
int mt76x2e_set_channel(struct mt76_phy *phy);
int mt76x2u_set_channel(struct mt76_phy *phy);
void mt76x2_phy_set_antenna(struct mt76x02_dev *dev);
int mt76x2_phy_start(struct mt76x02_dev *dev);
int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
struct cfg80211_chan_def *chandef);
void mt76x2_phy_calibrate(struct work_struct *work);
void mt76x2_phy_set_txpower(struct mt76x02_dev *dev);
int mt76x2_mcu_init(struct mt76x02_dev *dev);
int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw,
u8 bw_index, bool scan);
int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level,
u8 channel);
void mt76x2_cleanup(struct mt76x02_dev *dev);
int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard);
void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable);
void mt76x2_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband);
void mt76_write_mac_initvals(struct mt76x02_dev *dev);
void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev);
void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev,
enum nl80211_band band);
void mt76x2_configure_tx_delay(struct mt76x02_dev *dev,
enum nl80211_band band, u8 bw);
void mt76x2_apply_gain_adj(struct mt76x02_dev *dev);
void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev);
#endif
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#ifndef __MT76x2U_H
#define __MT76x2U_H
#include <linux/device.h>
#include "mt76x2.h"
#include "mcu.h"
#define MT7612U_EEPROM_SIZE 512
#define MT_USB_AGGR_SIZE_LIMIT 21 /* 1024B unit */
#define MT_USB_AGGR_TIMEOUT 0x80 /* 33ns unit */
extern const struct ieee80211_ops mt76x2u_ops;
int mt76x2u_register_device(struct mt76x02_dev *dev);
int mt76x2u_init_hardware(struct mt76x02_dev *dev);
void mt76x2u_cleanup(struct mt76x02_dev *dev);
void mt76x2u_stop_hw(struct mt76x02_dev *dev);
int mt76x2u_mac_reset(struct mt76x02_dev *dev);
int mt76x2u_mac_stop(struct mt76x02_dev *dev);
int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
struct cfg80211_chan_def *chandef);
void mt76x2u_phy_calibrate(struct work_struct *work);
void mt76x2u_mcu_complete_urb(struct urb *urb);
int mt76x2u_mcu_init(struct mt76x02_dev *dev);
int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev);
int mt76x2u_alloc_queues(struct mt76x02_dev *dev);
void mt76x2u_queues_deinit(struct mt76x02_dev *dev);
void mt76x2u_stop_queues(struct mt76x02_dev *dev);
int mt76x2u_skb_dma_info(struct sk_buff *skb, enum dma_msg_port port,
u32 flags);
#endif /* __MT76x2U_H */
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "mt76x2.h"
static const struct pci_device_id mt76x2e_device_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7662) },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7612) },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7602) },
{ },
};
static int
mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
.txwi_size = sizeof(struct mt76x02_txwi),
.drv_flags = MT_DRV_TX_ALIGNED4_SKBS |
MT_DRV_SW_RX_AIRTIME |
MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x2e_set_channel,
.tx_prepare_skb = mt76x02_tx_prepare_skb,
.tx_complete_skb = mt76x02_tx_complete_skb,
.rx_skb = mt76x02_queue_rx_skb,
.rx_poll_complete = mt76x02_rx_poll_complete,
.sta_ps = mt76x02_sta_ps,
.sta_add = mt76x02_sta_add,
.sta_remove = mt76x02_sta_remove,
};
struct mt76x02_dev *dev;
struct mt76_dev *mdev;
int ret;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
if (ret)
return ret;
pci_set_master(pdev);
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt76x2_ops,
&drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt76x02_dev, mt76);
mt76_mmio_init(mdev, pcim_iomap_table(pdev)[0]);
mt76x2_reset_wlan(dev, false);
mdev->rev = mt76_rr(dev, MT_ASIC_VERSION);
dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev);
mt76_wr(dev, MT_INT_MASK_CSR, 0);
ret = devm_request_irq(mdev->dev, pdev->irq, mt76x02_irq_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (ret)
goto error;
ret = mt76x2_register_device(dev);
if (ret)
goto error;
/* Fix up ASPM configuration */
/* RG_SSUSB_G1_CDR_BIR_LTR = 0x9 */
mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9);
/* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */
mt76_rmw_field(dev, 0x15a0c, 0xfU << 28, 0xf);
/* RG_SSUSB_CDR_BR_PE1D = 0x3 */
mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3);
mt76_pci_disable_aspm(pdev);
return 0;
error:
mt76_free_device(&dev->mt76);
return ret;
}
static void
mt76x2e_remove(struct pci_dev *pdev)
{
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
mt76_unregister_device(mdev);
mt76x2_cleanup(dev);
mt76_free_device(mdev);
}
static int __maybe_unused
mt76x2e_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct mt76_dev *mdev = pci_get_drvdata(pdev);
int i, err;
napi_disable(&mdev->tx_napi);
tasklet_kill(&mdev->pre_tbtt_tasklet);
mt76_worker_disable(&mdev->tx_worker);
mt76_for_each_q_rx(mdev, i)
napi_disable(&mdev->napi[i]);
pci_enable_wake(pdev, pci_choose_state(pdev, state), true);
pci_save_state(pdev);
err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
if (err)
goto restore;
return 0;
restore:
mt76_for_each_q_rx(mdev, i)
napi_enable(&mdev->napi[i]);
napi_enable(&mdev->tx_napi);
return err;
}
static int __maybe_unused
mt76x2e_resume(struct pci_dev *pdev)
{
struct mt76_dev *mdev = pci_get_drvdata(pdev);
struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
int i, err;
err = pci_set_power_state(pdev, PCI_D0);
if (err)
return err;
pci_restore_state(pdev);
mt76_worker_enable(&mdev->tx_worker);
mt76_for_each_q_rx(mdev, i) {
napi_enable(&mdev->napi[i]);
}
napi_enable(&mdev->tx_napi);
local_bh_disable();
mt76_for_each_q_rx(mdev, i) {
napi_schedule(&mdev->napi[i]);
}
napi_schedule(&mdev->tx_napi);
local_bh_enable();
return mt76x2_resume_device(dev);
}
MODULE_DEVICE_TABLE(pci, mt76x2e_device_table);
MODULE_FIRMWARE(MT7662_FIRMWARE);
MODULE_FIRMWARE(MT7662_ROM_PATCH);
MODULE_DESCRIPTION("MediaTek MT76x2E (PCIe) wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
static struct pci_driver mt76pci_driver = {
.name = KBUILD_MODNAME,
.id_table = mt76x2e_device_table,
.probe = mt76x2e_probe,
.remove = mt76x2e_remove,
#ifdef CONFIG_PM
.suspend = mt76x2e_suspend,
.resume = mt76x2e_resume,
#endif /* CONFIG_PM */
};
module_pci_driver(mt76pci_driver);
@@ -0,0 +1,320 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include <linux/delay.h>
#include "mt76x2.h"
#include "eeprom.h"
#include "mcu.h"
#include "../mt76x02_mac.h"
static void
mt76x2_mac_pbf_init(struct mt76x02_dev *dev)
{
u32 val;
val = MT_PBF_SYS_CTRL_MCU_RESET |
MT_PBF_SYS_CTRL_DMA_RESET |
MT_PBF_SYS_CTRL_MAC_RESET |
MT_PBF_SYS_CTRL_PBF_RESET |
MT_PBF_SYS_CTRL_ASY_RESET;
mt76_set(dev, MT_PBF_SYS_CTRL, val);
mt76_clear(dev, MT_PBF_SYS_CTRL, val);
mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f);
mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf);
}
static void
mt76x2_fixup_xtal(struct mt76x02_dev *dev)
{
u16 eep_val;
s8 offset = 0;
eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_2);
offset = eep_val & 0x7f;
if ((eep_val & 0xff) == 0xff)
offset = 0;
else if (eep_val & 0x80)
offset = 0 - offset;
eep_val >>= 8;
if (eep_val == 0x00 || eep_val == 0xff) {
eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_1);
eep_val &= 0xff;
if (eep_val == 0x00 || eep_val == 0xff)
eep_val = 0x14;
}
eep_val &= 0x7f;
mt76_rmw_field(dev, MT_XO_CTRL5, MT_XO_CTRL5_C2_VAL, eep_val + offset);
mt76_set(dev, MT_XO_CTRL6, MT_XO_CTRL6_C2_CTRL);
eep_val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2);
switch (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) {
case 0:
mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80);
break;
case 1:
mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0);
break;
default:
break;
}
}
int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
{
const u8 *macaddr = dev->mphy.macaddr;
u32 val;
int i, k;
if (!mt76x02_wait_for_mac(&dev->mt76))
return -ETIMEDOUT;
val = mt76_rr(dev, MT_WPDMA_GLO_CFG);
val &= ~(MT_WPDMA_GLO_CFG_TX_DMA_EN |
MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
MT_WPDMA_GLO_CFG_RX_DMA_EN |
MT_WPDMA_GLO_CFG_RX_DMA_BUSY |
MT_WPDMA_GLO_CFG_DMA_BURST_SIZE);
val |= FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3);
mt76_wr(dev, MT_WPDMA_GLO_CFG, val);
mt76x2_mac_pbf_init(dev);
mt76_write_mac_initvals(dev);
mt76x2_fixup_xtal(dev);
mt76_clear(dev, MT_MAC_SYS_CTRL,
MT_MAC_SYS_CTRL_RESET_CSR |
MT_MAC_SYS_CTRL_RESET_BBP);
if (is_mt7612(dev))
mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN);
mt76_set(dev, MT_EXT_CCA_CFG, 0x0000f000);
mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31));
mt76_wr(dev, MT_RF_BYPASS_0, 0x06000000);
mt76_wr(dev, MT_RF_SETTING_0, 0x08800000);
usleep_range(5000, 10000);
mt76_wr(dev, MT_RF_BYPASS_0, 0x00000000);
mt76_wr(dev, MT_MCU_CLOCK_CTL, 0x1401);
mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN);
mt76x02_mac_setaddr(dev, macaddr);
mt76x02e_init_beacon_config(dev);
if (!hard)
return 0;
for (i = 0; i < 256 / 32; i++)
mt76_wr(dev, MT_WCID_DROP_BASE + i * 4, 0);
for (i = 0; i < 256; i++) {
mt76x02_mac_wcid_setup(dev, i, 0, NULL);
mt76_wr(dev, MT_WCID_TX_RATE(i), 0);
mt76_wr(dev, MT_WCID_TX_RATE(i) + 4, 0);
}
for (i = 0; i < MT_MAX_VIFS; i++)
mt76x02_mac_wcid_setup(dev, MT_VIF_WCID(i), i, NULL);
for (i = 0; i < 16; i++)
for (k = 0; k < 4; k++)
mt76x02_mac_shared_key_setup(dev, i, k, NULL);
for (i = 0; i < 16; i++)
mt76_rr(dev, MT_TX_STAT_FIFO);
mt76x02_set_tx_ackto(dev);
return 0;
}
static void
mt76x2_power_on_rf_patch(struct mt76x02_dev *dev)
{
mt76_set(dev, 0x10130, BIT(0) | BIT(16));
udelay(1);
mt76_clear(dev, 0x1001c, 0xff);
mt76_set(dev, 0x1001c, 0x30);
mt76_wr(dev, 0x10014, 0x484f);
udelay(1);
mt76_set(dev, 0x10130, BIT(17));
udelay(125);
mt76_clear(dev, 0x10130, BIT(16));
udelay(50);
mt76_set(dev, 0x1014c, BIT(19) | BIT(20));
}
static void
mt76x2_power_on_rf(struct mt76x02_dev *dev, int unit)
{
int shift = unit ? 8 : 0;
/* Enable RF BG */
mt76_set(dev, 0x10130, BIT(0) << shift);
udelay(10);
/* Enable RFDIG LDO/AFE/ABB/ADDA */
mt76_set(dev, 0x10130, (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift);
udelay(10);
/* Switch RFDIG power to internal LDO */
mt76_clear(dev, 0x10130, BIT(2) << shift);
udelay(10);
mt76x2_power_on_rf_patch(dev);
mt76_set(dev, 0x530, 0xf);
}
static void
mt76x2_power_on(struct mt76x02_dev *dev)
{
u32 val;
/* Turn on WL MTCMOS */
mt76_set(dev, MT_WLAN_MTC_CTRL, MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP);
val = MT_WLAN_MTC_CTRL_STATE_UP |
MT_WLAN_MTC_CTRL_PWR_ACK |
MT_WLAN_MTC_CTRL_PWR_ACK_S;
mt76_poll(dev, MT_WLAN_MTC_CTRL, val, val, 1000);
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0x7f << 16);
udelay(10);
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xf << 24);
udelay(10);
mt76_set(dev, MT_WLAN_MTC_CTRL, 0xf << 24);
mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xfff);
/* Turn on AD/DA power down */
mt76_clear(dev, 0x11204, BIT(3));
/* WLAN function enable */
mt76_set(dev, 0x10080, BIT(0));
/* Release BBP software reset */
mt76_clear(dev, 0x10064, BIT(18));
mt76x2_power_on_rf(dev, 0);
mt76x2_power_on_rf(dev, 1);
}
int mt76x2_resume_device(struct mt76x02_dev *dev)
{
int err;
mt76x02_dma_disable(dev);
mt76x2_reset_wlan(dev, true);
mt76x2_power_on(dev);
err = mt76x2_mac_reset(dev, true);
if (err)
return err;
mt76x02_mac_start(dev);
return mt76x2_mcu_init(dev);
}
static int mt76x2_init_hardware(struct mt76x02_dev *dev)
{
int ret;
mt76x02_dma_disable(dev);
mt76x2_reset_wlan(dev, true);
mt76x2_power_on(dev);
ret = mt76x2_eeprom_init(dev);
if (ret)
return ret;
ret = mt76x2_mac_reset(dev, true);
if (ret)
return ret;
dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
ret = mt76x02_dma_init(dev);
if (ret)
return ret;
set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
mt76x02_mac_start(dev);
ret = mt76x2_mcu_init(dev);
if (ret)
return ret;
mt76x2_mac_stop(dev, false);
return 0;
}
void mt76x2_stop_hardware(struct mt76x02_dev *dev)
{
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mphy.mac_work);
cancel_delayed_work_sync(&dev->wdt_work);
clear_bit(MT76_RESTART, &dev->mphy.state);
mt76x02_mcu_set_radio_state(dev, false);
mt76x2_mac_stop(dev, false);
}
void mt76x2_cleanup(struct mt76x02_dev *dev)
{
tasklet_disable(&dev->dfs_pd.dfs_tasklet);
tasklet_disable(&dev->mt76.pre_tbtt_tasklet);
mt76x2_stop_hardware(dev);
mt76_dma_cleanup(&dev->mt76);
mt76x02_mcu_cleanup(dev);
}
int mt76x2_register_device(struct mt76x02_dev *dev)
{
int ret;
INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate);
ret = mt76x02_init_device(dev);
if (ret)
return ret;
ret = mt76x2_init_hardware(dev);
if (ret)
return ret;
mt76x02_config_mac_addr_list(dev);
ret = mt76_register_device(&dev->mt76, true, mt76x02_rates,
ARRAY_SIZE(mt76x02_rates));
if (ret)
goto fail;
mt76x02_init_debugfs(dev);
mt76x2_init_txpower(dev, &dev->mphy.sband_2g.sband);
mt76x2_init_txpower(dev, &dev->mphy.sband_5g.sband);
return 0;
fail:
mt76x2_stop_hardware(dev);
return ret;
}
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include "mt76x2.h"
#include "../mt76x02_mac.h"
static int
mt76x2_start(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
mt76x02_mac_start(dev);
mt76x2_phy_start(dev);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
MT_MAC_WORK_INTERVAL);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work,
MT_WATCHDOG_TIME);
set_bit(MT76_STATE_RUNNING, &dev->mphy.state);
return 0;
}
static void
mt76x2_stop(struct ieee80211_hw *hw, bool suspend)
{
struct mt76x02_dev *dev = hw->priv;
clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
mt76x2_stop_hardware(dev);
}
int mt76x2e_set_channel(struct mt76_phy *phy)
{
struct mt76x02_dev *dev = container_of(phy->dev, struct mt76x02_dev, mt76);
tasklet_disable(&dev->mt76.pre_tbtt_tasklet);
tasklet_disable(&dev->dfs_pd.dfs_tasklet);
mt76x2_mac_stop(dev, true);
mt76x2_phy_set_channel(dev, &phy->chandef);
mt76x02_mac_cc_reset(dev);
mt76x02_dfs_init_params(dev);
mt76x2_mac_resume(dev);
tasklet_enable(&dev->dfs_pd.dfs_tasklet);
tasklet_enable(&dev->mt76.pre_tbtt_tasklet);
return 0;
}
static int
mt76x2_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{
struct mt76x02_dev *dev = hw->priv;
mutex_lock(&dev->mt76.mutex);
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
if (!(hw->conf.flags & IEEE80211_CONF_MONITOR))
dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC;
else
dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC;
mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter);
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {
struct mt76_phy *mphy = &dev->mphy;
dev->txpower_conf = hw->conf.power_level * 2;
dev->txpower_conf = mt76_get_sar_power(mphy,
mphy->chandef.chan,
dev->txpower_conf);
/* convert to per-chain power for 2x2 devices */
dev->txpower_conf -= 6;
if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
mt76x2_phy_set_txpower(dev);
mt76x02_tx_set_txpwr_auto(dev, dev->txpower_conf);
}
}
mutex_unlock(&dev->mt76.mutex);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
mt76_update_channel(&dev->mphy);
return 0;
}
static void
mt76x2_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop)
{
}
static int mt76x2_set_antenna(struct ieee80211_hw *hw, int radio_idx,
u32 tx_ant, u32 rx_ant)
{
struct mt76x02_dev *dev = hw->priv;
if (!tx_ant || tx_ant > 3 || tx_ant != rx_ant)
return -EINVAL;
mutex_lock(&dev->mt76.mutex);
dev->mphy.chainmask = (tx_ant == 3) ? 0x202 : 0x101;
dev->mphy.antenna_mask = tx_ant;
mt76_set_stream_caps(&dev->mphy, true);
mt76x2_phy_set_antenna(dev);
mutex_unlock(&dev->mt76.mutex);
return 0;
}
const struct ieee80211_ops mt76x2_ops = {
.add_chanctx = ieee80211_emulate_add_chanctx,
.remove_chanctx = ieee80211_emulate_remove_chanctx,
.change_chanctx = ieee80211_emulate_change_chanctx,
.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
.tx = mt76x02_tx,
.start = mt76x2_start,
.stop = mt76x2_stop,
.add_interface = mt76x02_add_interface,
.remove_interface = mt76x02_remove_interface,
.config = mt76x2_config,
.configure_filter = mt76x02_configure_filter,
.bss_info_changed = mt76x02_bss_info_changed,
.sta_state = mt76_sta_state,
.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
.set_key = mt76x02_set_key,
.conf_tx = mt76x02_conf_tx,
.sw_scan_start = mt76_sw_scan,
.sw_scan_complete = mt76x02_sw_scan_complete,
.flush = mt76x2_flush,
.ampdu_action = mt76x02_ampdu_action,
.get_txpower = mt76_get_txpower,
.wake_tx_queue = mt76_wake_tx_queue,
.sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
.release_buffered_frames = mt76_release_buffered_frames,
.set_coverage_class = mt76x02_set_coverage_class,
.get_survey = mt76_get_survey,
.set_tim = mt76_set_tim,
.set_antenna = mt76x2_set_antenna,
.get_antenna = mt76_get_antenna,
.set_rts_threshold = mt76x02_set_rts_threshold,
.reconfig_complete = mt76x02_reconfig_complete,
.set_sar_specs = mt76x2_set_sar_specs,
};
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include "mt76x2.h"
#include "mcu.h"
#include "eeprom.h"
static int
mt76pci_load_rom_patch(struct mt76x02_dev *dev)
{
const struct firmware *fw = NULL;
struct mt76x02_patch_header *hdr;
bool rom_protect = !is_mt7612(dev);
int len, ret = 0;
__le32 *cur;
u32 patch_mask, patch_reg;
if (rom_protect && !mt76_poll(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) {
dev_err(dev->mt76.dev,
"Could not get hardware semaphore for ROM PATCH\n");
return -ETIMEDOUT;
}
if (mt76xx_rev(dev) >= MT76XX_REV_E3) {
patch_mask = BIT(0);
patch_reg = MT_MCU_CLOCK_CTL;
} else {
patch_mask = BIT(1);
patch_reg = MT_MCU_COM_REG0;
}
if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) {
dev_info(dev->mt76.dev, "ROM patch already applied\n");
goto out;
}
ret = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev);
if (ret)
goto out;
if (!fw || !fw->data || fw->size <= sizeof(*hdr)) {
ret = -EIO;
dev_err(dev->mt76.dev, "Failed to load firmware\n");
goto out;
}
hdr = (struct mt76x02_patch_header *)fw->data;
dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time);
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET);
cur = (__le32 *)(fw->data + sizeof(*hdr));
len = fw->size - sizeof(*hdr);
mt76_wr_copy(dev, MT_MCU_ROM_PATCH_ADDR, cur, len);
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
/* Trigger ROM */
mt76_wr(dev, MT_MCU_INT_LEVEL, 4);
if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 2000)) {
dev_err(dev->mt76.dev, "Failed to load ROM patch\n");
ret = -ETIMEDOUT;
}
out:
/* release semaphore */
if (rom_protect)
mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1);
release_firmware(fw);
return ret;
}
static int
mt76pci_load_firmware(struct mt76x02_dev *dev)
{
const struct firmware *fw;
const struct mt76x02_fw_header *hdr;
int len, ret;
__le32 *cur;
u32 offset, val;
ret = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev);
if (ret)
return ret;
if (!fw || !fw->data || fw->size < sizeof(*hdr))
goto error;
hdr = (const struct mt76x02_fw_header *)fw->data;
len = sizeof(*hdr);
len += le32_to_cpu(hdr->ilm_len);
len += le32_to_cpu(hdr->dlm_len);
if (fw->size != len)
goto error;
val = le16_to_cpu(hdr->fw_ver);
dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n",
(val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf);
val = le16_to_cpu(hdr->build_ver);
dev_info(dev->mt76.dev, "Build: %x\n", val);
dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time);
cur = (__le32 *)(fw->data + sizeof(*hdr));
len = le32_to_cpu(hdr->ilm_len);
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ILM_OFFSET);
mt76_wr_copy(dev, MT_MCU_ILM_ADDR, cur, len);
cur += len / sizeof(*cur);
len = le32_to_cpu(hdr->dlm_len);
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
offset = MT_MCU_DLM_ADDR_E3;
else
offset = MT_MCU_DLM_ADDR;
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET);
mt76_wr_copy(dev, offset, cur, len);
mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0);
val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2);
if (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, val) == 1)
mt76_set(dev, MT_MCU_COM_REG0, BIT(30));
/* trigger firmware */
mt76_wr(dev, MT_MCU_INT_LEVEL, 2);
if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 200)) {
dev_err(dev->mt76.dev, "Firmware failed to start\n");
release_firmware(fw);
return -ETIMEDOUT;
}
mt76x02_set_ethtool_fwver(dev, hdr);
dev_info(dev->mt76.dev, "Firmware running!\n");
release_firmware(fw);
return ret;
error:
dev_err(dev->mt76.dev, "Invalid firmware\n");
release_firmware(fw);
return -ENOENT;
}
static int
mt76pci_mcu_restart(struct mt76_dev *mdev)
{
struct mt76x02_dev *dev;
int ret;
dev = container_of(mdev, struct mt76x02_dev, mt76);
mt76x02_mcu_cleanup(dev);
mt76x2_mac_reset(dev, true);
ret = mt76pci_load_firmware(dev);
if (ret)
return ret;
mt76_wr(dev, MT_WPDMA_RST_IDX, ~0);
return 0;
}
int mt76x2_mcu_init(struct mt76x02_dev *dev)
{
static const struct mt76_mcu_ops mt76x2_mcu_ops = {
.mcu_restart = mt76pci_mcu_restart,
.mcu_send_msg = mt76x02_mcu_msg_send,
.mcu_parse_response = mt76x02_mcu_parse_response,
};
int ret;
dev->mt76.mcu_ops = &mt76x2_mcu_ops;
ret = mt76pci_load_rom_patch(dev);
if (ret)
return ret;
ret = mt76pci_load_firmware(dev);
if (ret)
return ret;
mt76x02_mcu_function_select(dev, Q_SELECT, 1);
return 0;
}
@@ -0,0 +1,311 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
#include <linux/delay.h>
#include "mt76x2.h"
#include "mcu.h"
#include "eeprom.h"
#include "../mt76x02_phy.h"
static bool
mt76x2_phy_tssi_init_cal(struct mt76x02_dev *dev)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
u32 flag = 0;
if (!mt76x2_tssi_enabled(dev))
return false;
if (mt76x2_channel_silent(dev))
return false;
if (chan->band == NL80211_BAND_5GHZ)
flag |= BIT(0);
if (mt76x02_ext_pa_enabled(dev, chan->band))
flag |= BIT(8);
mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag);
dev->cal.tssi_cal_done = true;
return true;
}
static void
mt76x2_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
bool is_5ghz = chan->band == NL80211_BAND_5GHZ;
if (dev->cal.channel_cal_done)
return;
if (mt76x2_channel_silent(dev))
return;
if (!dev->cal.tssi_cal_done)
mt76x2_phy_tssi_init_cal(dev);
if (!mac_stopped)
mt76x2_mac_stop(dev, false);
if (is_5ghz)
mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0);
mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0);
mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0);
if (!mac_stopped)
mt76x2_mac_resume(dev);
mt76x2_apply_gain_adj(dev);
mt76x02_edcca_init(dev);
dev->cal.channel_cal_done = true;
}
void mt76x2_phy_set_antenna(struct mt76x02_dev *dev)
{
u32 val;
val = mt76_rr(dev, MT_BBP(AGC, 0));
val &= ~(BIT(4) | BIT(1));
switch (dev->mphy.antenna_mask) {
case 1:
/* disable mac DAC control */
mt76_clear(dev, MT_BBP(IBI, 9), BIT(11));
mt76_clear(dev, MT_BBP(TXBE, 5), 3);
mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0x3);
mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 2);
/* disable DAC 1 */
mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 4);
val &= ~(BIT(3) | BIT(0));
break;
case 2:
/* disable mac DAC control */
mt76_clear(dev, MT_BBP(IBI, 9), BIT(11));
mt76_rmw_field(dev, MT_BBP(TXBE, 5), 3, 1);
mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xc);
mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 1);
/* disable DAC 0 */
mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 1);
val &= ~BIT(3);
val |= BIT(0);
break;
case 3:
default:
/* enable mac DAC control */
mt76_set(dev, MT_BBP(IBI, 9), BIT(11));
mt76_set(dev, MT_BBP(TXBE, 5), 3);
mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xf);
mt76_clear(dev, MT_BBP(CORE, 32), GENMASK(21, 20));
mt76_clear(dev, MT_BBP(CORE, 33), GENMASK(12, 9));
val &= ~BIT(0);
val |= BIT(3);
break;
}
mt76_wr(dev, MT_BBP(AGC, 0), val);
}
int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_channel *chan = chandef->chan;
bool scan = test_bit(MT76_SCANNING, &dev->mphy.state);
enum nl80211_band band = chan->band;
u8 channel;
u32 ext_cca_chan[4] = {
[0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)),
[1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)),
[2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)),
[3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)),
};
int ch_group_index;
u8 bw, bw_index;
int freq, freq1;
int ret;
dev->cal.channel_cal_done = false;
freq = chandef->chan->center_freq;
freq1 = chandef->center_freq1;
channel = chan->hw_value;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_40:
bw = 1;
if (freq1 > freq) {
bw_index = 1;
ch_group_index = 0;
} else {
bw_index = 3;
ch_group_index = 1;
}
channel += 2 - ch_group_index * 4;
break;
case NL80211_CHAN_WIDTH_80:
ch_group_index = (freq - freq1 + 30) / 20;
if (WARN_ON(ch_group_index < 0 || ch_group_index > 3))
ch_group_index = 0;
bw = 2;
bw_index = ch_group_index;
channel += 6 - ch_group_index * 4;
break;
default:
bw = 0;
bw_index = 0;
ch_group_index = 0;
break;
}
mt76x2_read_rx_gain(dev);
mt76x2_phy_set_txpower_regs(dev, band);
mt76x2_configure_tx_delay(dev, band, bw);
mt76x2_phy_set_txpower(dev);
mt76x02_phy_set_band(dev, chan->band, ch_group_index & 1);
mt76x02_phy_set_bw(dev, chandef->width, ch_group_index);
mt76_rmw(dev, MT_EXT_CCA_CFG,
(MT_EXT_CCA_CFG_CCA0 |
MT_EXT_CCA_CFG_CCA1 |
MT_EXT_CCA_CFG_CCA2 |
MT_EXT_CCA_CFG_CCA3 |
MT_EXT_CCA_CFG_CCA_MASK),
ext_cca_chan[ch_group_index]);
ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan);
if (ret)
return ret;
mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true);
mt76x2_phy_set_antenna(dev);
/* Enable LDPC Rx */
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
mt76_set(dev, MT_BBP(RXO, 13), BIT(10));
if (!dev->cal.init_cal_done) {
u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT);
if (val != 0xff)
mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0);
}
mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel);
/* Rx LPF calibration */
if (!dev->cal.init_cal_done)
mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0);
dev->cal.init_cal_done = true;
mt76_wr(dev, MT_BBP(AGC, 61), 0xFF64A4E2);
mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010);
mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404);
mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070);
mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x04101B3F);
if (scan)
return 0;
mt76x2_phy_channel_calibrate(dev, true);
mt76x02_init_agc_gain(dev);
/* init default values for temp compensation */
if (mt76x2_tssi_enabled(dev)) {
mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
0x38);
mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP,
0x38);
}
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
MT_CALIBRATE_INTERVAL);
return 0;
}
static void
mt76x2_phy_temp_compensate(struct mt76x02_dev *dev)
{
struct mt76x2_temp_comp t;
int temp, db_diff;
if (mt76x2_get_temp_comp(dev, &t))
return;
temp = mt76_get_field(dev, MT_TEMP_SENSOR, MT_TEMP_SENSOR_VAL);
temp -= t.temp_25_ref;
temp = (temp * 1789) / 1000 + 25;
dev->cal.temp = temp;
if (temp > 25)
db_diff = (temp - 25) / t.high_slope;
else
db_diff = (25 - temp) / t.low_slope;
db_diff = min(db_diff, t.upper_bound);
db_diff = max(db_diff, t.lower_bound);
mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
db_diff * 2);
mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP,
db_diff * 2);
}
void mt76x2_phy_calibrate(struct work_struct *work)
{
struct mt76x02_dev *dev;
dev = container_of(work, struct mt76x02_dev, cal_work.work);
mutex_lock(&dev->mt76.mutex);
mt76x2_phy_channel_calibrate(dev, false);
mt76x2_phy_tssi_compensate(dev);
mt76x2_phy_temp_compensate(dev);
mt76x2_phy_update_channel_gain(dev);
mutex_unlock(&dev->mt76.mutex);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
MT_CALIBRATE_INTERVAL);
}
int mt76x2_phy_start(struct mt76x02_dev *dev)
{
int ret;
ret = mt76x02_mcu_set_radio_state(dev, true);
if (ret)
return ret;
mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0);
return ret;
}
@@ -0,0 +1,349 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2.h"
#include "eeprom.h"
#include "mcu.h"
#include "../mt76x02_phy.h"
static void
mt76x2_adjust_high_lna_gain(struct mt76x02_dev *dev, int reg, s8 offset)
{
s8 gain;
gain = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN,
mt76_rr(dev, MT_BBP(AGC, reg)));
gain -= offset / 2;
mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_LNA_HIGH_GAIN, gain);
}
static void
mt76x2_adjust_agc_gain(struct mt76x02_dev *dev, int reg, s8 offset)
{
s8 gain;
gain = FIELD_GET(MT_BBP_AGC_GAIN, mt76_rr(dev, MT_BBP(AGC, reg)));
gain += offset;
mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_GAIN, gain);
}
void mt76x2_apply_gain_adj(struct mt76x02_dev *dev)
{
s8 *gain_adj = dev->cal.rx.high_gain;
mt76x2_adjust_high_lna_gain(dev, 4, gain_adj[0]);
mt76x2_adjust_high_lna_gain(dev, 5, gain_adj[1]);
mt76x2_adjust_agc_gain(dev, 8, gain_adj[0]);
mt76x2_adjust_agc_gain(dev, 9, gain_adj[1]);
}
EXPORT_SYMBOL_GPL(mt76x2_apply_gain_adj);
void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev,
enum nl80211_band band)
{
u32 pa_mode[2];
u32 pa_mode_adj;
if (band == NL80211_BAND_2GHZ) {
pa_mode[0] = 0x010055ff;
pa_mode[1] = 0x00550055;
mt76_wr(dev, MT_TX_ALC_CFG_2, 0x35160a00);
mt76_wr(dev, MT_TX_ALC_CFG_3, 0x35160a06);
if (mt76x02_ext_pa_enabled(dev, band)) {
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0x0000ec00);
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0x0000ec00);
} else {
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0xf4000200);
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0xfa000200);
}
} else {
pa_mode[0] = 0x0000ffff;
pa_mode[1] = 0x00ff00ff;
if (mt76x02_ext_pa_enabled(dev, band)) {
mt76_wr(dev, MT_TX_ALC_CFG_2, 0x2f0f0400);
mt76_wr(dev, MT_TX_ALC_CFG_3, 0x2f0f0476);
} else {
mt76_wr(dev, MT_TX_ALC_CFG_2, 0x1b0f0400);
mt76_wr(dev, MT_TX_ALC_CFG_3, 0x1b0f0476);
}
if (mt76x02_ext_pa_enabled(dev, band))
pa_mode_adj = 0x04000000;
else
pa_mode_adj = 0;
mt76_wr(dev, MT_RF_PA_MODE_ADJ0, pa_mode_adj);
mt76_wr(dev, MT_RF_PA_MODE_ADJ1, pa_mode_adj);
}
mt76_wr(dev, MT_BB_PA_MODE_CFG0, pa_mode[0]);
mt76_wr(dev, MT_BB_PA_MODE_CFG1, pa_mode[1]);
mt76_wr(dev, MT_RF_PA_MODE_CFG0, pa_mode[0]);
mt76_wr(dev, MT_RF_PA_MODE_CFG1, pa_mode[1]);
if (mt76x02_ext_pa_enabled(dev, band)) {
u32 val;
if (band == NL80211_BAND_2GHZ)
val = 0x3c3c023c;
else
val = 0x363c023c;
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val);
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val);
mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00001818);
} else {
if (band == NL80211_BAND_2GHZ) {
u32 val = 0x0f3c3c3c;
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val);
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val);
mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00000606);
} else {
mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x383c023c);
mt76_wr(dev, MT_TX1_RF_GAIN_CORR, 0x24282e28);
mt76_wr(dev, MT_TX_ALC_CFG_4, 0);
}
}
}
EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower_regs);
static int
mt76x2_get_min_rate_power(struct mt76x02_rate_power *r)
{
int i;
s8 ret = 0;
for (i = 0; i < sizeof(r->all); i++) {
if (!r->all[i])
continue;
if (ret)
ret = min(ret, r->all[i]);
else
ret = r->all[i];
}
return ret;
}
void mt76x2_phy_set_txpower(struct mt76x02_dev *dev)
{
enum nl80211_chan_width width = dev->mphy.chandef.width;
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
struct mt76x2_tx_power_info txp;
int txp_0, txp_1, delta = 0;
struct mt76x02_rate_power t = {};
int base_power, gain;
mt76x2_get_power_info(dev, &txp, chan);
if (width == NL80211_CHAN_WIDTH_40)
delta = txp.delta_bw40;
else if (width == NL80211_CHAN_WIDTH_80)
delta = txp.delta_bw80;
mt76x2_get_rate_power(dev, &t, chan);
mt76x02_add_rate_power_offset(&t, txp.target_power + delta);
mt76x02_limit_rate_power(&t, dev->txpower_conf);
dev->mphy.txpower_cur = mt76x02_get_max_rate_power(&t);
base_power = mt76x2_get_min_rate_power(&t);
delta = base_power - txp.target_power;
txp_0 = txp.chain[0].target_power + txp.chain[0].delta + delta;
txp_1 = txp.chain[1].target_power + txp.chain[1].delta + delta;
gain = min(txp_0, txp_1);
if (gain < 0) {
base_power -= gain;
txp_0 -= gain;
txp_1 -= gain;
} else if (gain > 0x2f) {
base_power -= gain - 0x2f;
txp_0 = 0x2f;
txp_1 = 0x2f;
}
mt76x02_add_rate_power_offset(&t, -base_power);
dev->target_power = txp.target_power;
dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power;
dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power;
dev->rate_power = t;
mt76x02_phy_set_txpower(dev, txp_0, txp_1);
}
EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower);
void mt76x2_configure_tx_delay(struct mt76x02_dev *dev,
enum nl80211_band band, u8 bw)
{
u32 cfg0, cfg1;
if (mt76x02_ext_pa_enabled(dev, band)) {
cfg0 = bw ? 0x000b0c01 : 0x00101101;
cfg1 = 0x00011414;
} else {
cfg0 = bw ? 0x000b0b01 : 0x00101001;
cfg1 = 0x00021414;
}
mt76_wr(dev, MT_TX_SW_CFG0, cfg0);
mt76_wr(dev, MT_TX_SW_CFG1, cfg1);
mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, 15);
}
EXPORT_SYMBOL_GPL(mt76x2_configure_tx_delay);
void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
struct mt76x2_tx_power_info txp;
struct mt76x2_tssi_comp t = {};
if (!dev->cal.tssi_cal_done)
return;
if (!dev->cal.tssi_comp_pending) {
/* TSSI trigger */
t.cal_mode = BIT(0);
mt76x2_mcu_tssi_comp(dev, &t);
dev->cal.tssi_comp_pending = true;
} else {
if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4))
return;
dev->cal.tssi_comp_pending = false;
mt76x2_get_power_info(dev, &txp, chan);
if (mt76x02_ext_pa_enabled(dev, chan->band))
t.pa_mode = 1;
t.cal_mode = BIT(1);
t.slope0 = txp.chain[0].tssi_slope;
t.offset0 = txp.chain[0].tssi_offset;
t.slope1 = txp.chain[1].tssi_slope;
t.offset1 = txp.chain[1].tssi_offset;
mt76x2_mcu_tssi_comp(dev, &t);
if (t.pa_mode || dev->cal.dpd_cal_done || dev->ed_tx_blocked)
return;
usleep_range(10000, 20000);
mt76x02_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value);
dev->cal.dpd_cal_done = true;
}
}
EXPORT_SYMBOL_GPL(mt76x2_phy_tssi_compensate);
static void
mt76x2_phy_set_gain_val(struct mt76x02_dev *dev)
{
u32 val;
u8 gain_val[2];
gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust;
gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust;
val = 0x1836 << 16;
if (!mt76x2_has_ext_lna(dev) &&
dev->mphy.chandef.width >= NL80211_CHAN_WIDTH_40)
val = 0x1e42 << 16;
if (mt76x2_has_ext_lna(dev) &&
dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ &&
dev->mphy.chandef.width < NL80211_CHAN_WIDTH_40)
val = 0x0f36 << 16;
val |= 0xf8;
mt76_wr(dev, MT_BBP(AGC, 8),
val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[0]));
mt76_wr(dev, MT_BBP(AGC, 9),
val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[1]));
if (dev->mphy.chandef.chan->flags & IEEE80211_CHAN_RADAR)
mt76x02_phy_dfs_adjust_agc(dev);
}
void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
{
u8 *gain = dev->cal.agc_gain_init;
u8 low_gain_delta, gain_delta;
u32 agc_35, agc_37;
bool gain_change;
int low_gain;
u32 val;
dev->cal.avg_rssi_all = mt76_get_min_avg_rssi(&dev->mt76, 0);
if (!dev->cal.avg_rssi_all)
dev->cal.avg_rssi_all = -75;
low_gain = (dev->cal.avg_rssi_all > mt76x02_get_rssi_gain_thresh(dev)) +
(dev->cal.avg_rssi_all > mt76x02_get_low_rssi_gain_thresh(dev));
gain_change = dev->cal.low_gain < 0 ||
(dev->cal.low_gain & 2) ^ (low_gain & 2);
dev->cal.low_gain = low_gain;
if (!gain_change) {
if (mt76x02_phy_adjust_vga_gain(dev))
mt76x2_phy_set_gain_val(dev);
return;
}
if (dev->mphy.chandef.width == NL80211_CHAN_WIDTH_80) {
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211);
val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf;
if (low_gain == 2)
val |= 0x3;
else
val |= 0x5;
mt76_wr(dev, MT_BBP(AGC, 26), val);
} else {
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
}
if (mt76x2_has_ext_lna(dev))
low_gain_delta = 10;
else
low_gain_delta = 14;
agc_37 = 0x2121262c;
if (dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ)
agc_35 = 0x11111516;
else if (low_gain == 2)
agc_35 = agc_37 = 0x08080808;
else if (dev->mphy.chandef.width == NL80211_CHAN_WIDTH_80)
agc_35 = 0x10101014;
else
agc_35 = 0x11111116;
if (low_gain == 2) {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
gain_delta = low_gain_delta;
dev->cal.agc_gain_adjust = 0;
} else {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
gain_delta = 0;
dev->cal.agc_gain_adjust = low_gain_delta;
}
mt76_wr(dev, MT_BBP(AGC, 35), agc_35);
mt76_wr(dev, MT_BBP(AGC, 37), agc_37);
dev->cal.agc_gain_cur[0] = gain[0] - gain_delta;
dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
mt76x2_phy_set_gain_val(dev);
/* clear false CCA counters */
mt76_rr(dev, MT_RX_STAT_1);
}
EXPORT_SYMBOL_GPL(mt76x2_phy_update_channel_gain);
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "../mt76x02_usb.h"
#include "mt76x2u.h"
static const struct usb_device_id mt76x2u_device_table[] = {
{ USB_DEVICE(0x0b05, 0x1833) }, /* Asus USB-AC54 */
{ USB_DEVICE(0x0b05, 0x17eb) }, /* Asus USB-AC55 */
{ USB_DEVICE(0x0b05, 0x180b) }, /* Asus USB-N53 B1 */
{ USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */
{ USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */
{ USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */
{ USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */
{ USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */
{ USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */
{ USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */
{ USB_DEVICE(0x2c4e, 0x0103) }, /* Mercury UD13 */
{ USB_DEVICE(0x0846, 0x9014) }, /* Netgear WNDA3100v3 */
{ USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */
{ USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */
{ USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */
{ USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */
{ },
};
static int mt76x2u_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static const struct mt76_driver_ops drv_ops = {
.drv_flags = MT_DRV_SW_RX_AIRTIME |
MT_DRV_IGNORE_TXS_FAILED,
.survey_flags = SURVEY_INFO_TIME_TX,
.update_survey = mt76x02_update_channel,
.set_channel = mt76x2u_set_channel,
.tx_prepare_skb = mt76x02u_tx_prepare_skb,
.tx_complete_skb = mt76x02u_tx_complete_skb,
.tx_status_data = mt76x02_tx_status_data,
.rx_skb = mt76x02_queue_rx_skb,
.sta_ps = mt76x02_sta_ps,
.sta_add = mt76x02_sta_add,
.sta_remove = mt76x02_sta_remove,
};
struct usb_device *udev = interface_to_usbdev(intf);
struct mt76x02_dev *dev;
struct mt76_dev *mdev;
int err;
mdev = mt76_alloc_device(&intf->dev, sizeof(*dev), &mt76x2u_ops,
&drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt76x02_dev, mt76);
usb_reset_device(udev);
usb_set_intfdata(intf, dev);
mt76x02u_init_mcu(mdev);
err = mt76u_init(mdev, intf);
if (err < 0)
goto err;
mdev->rev = mt76_rr(dev, MT_ASIC_VERSION);
dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev);
if (!is_mt76x2(dev)) {
err = -ENODEV;
goto err;
}
err = mt76x2u_register_device(dev);
if (err < 0)
goto err;
return 0;
err:
mt76u_queues_deinit(&dev->mt76);
mt76_free_device(&dev->mt76);
usb_set_intfdata(intf, NULL);
return err;
}
static void mt76x2u_disconnect(struct usb_interface *intf)
{
struct mt76x02_dev *dev = usb_get_intfdata(intf);
struct ieee80211_hw *hw = mt76_hw(dev);
set_bit(MT76_REMOVED, &dev->mphy.state);
ieee80211_unregister_hw(hw);
mt76x2u_cleanup(dev);
mt76_free_device(&dev->mt76);
usb_set_intfdata(intf, NULL);
}
static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf,
pm_message_t state)
{
struct mt76x02_dev *dev = usb_get_intfdata(intf);
mt76u_stop_rx(&dev->mt76);
return 0;
}
static int __maybe_unused mt76x2u_resume(struct usb_interface *intf)
{
struct mt76x02_dev *dev = usb_get_intfdata(intf);
int err;
err = mt76u_resume_rx(&dev->mt76);
if (err < 0)
goto err;
err = mt76x2u_init_hardware(dev);
if (err < 0)
goto err;
return 0;
err:
mt76x2u_cleanup(dev);
return err;
}
MODULE_DEVICE_TABLE(usb, mt76x2u_device_table);
MODULE_FIRMWARE(MT7662_FIRMWARE);
MODULE_FIRMWARE(MT7662_ROM_PATCH);
static struct usb_driver mt76x2u_driver = {
.name = KBUILD_MODNAME,
.id_table = mt76x2u_device_table,
.probe = mt76x2u_probe,
.disconnect = mt76x2u_disconnect,
#ifdef CONFIG_PM
.suspend = mt76x2u_suspend,
.resume = mt76x2u_resume,
.reset_resume = mt76x2u_resume,
#endif /* CONFIG_PM */
.soft_unbind = 1,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(mt76x2u_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("MediaTek MT76x2U (USB) wireless driver");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include <linux/delay.h>
#include "mt76x2u.h"
#include "eeprom.h"
#include "../mt76x02_phy.h"
#include "../mt76x02_usb.h"
static void mt76x2u_init_dma(struct mt76x02_dev *dev)
{
u32 val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG));
val |= MT_USB_DMA_CFG_RX_DROP_OR_PAD |
MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN;
/* disable AGGR_BULK_RX in order to receive one
* frame in each rx urb and avoid copies
*/
val &= ~MT_USB_DMA_CFG_RX_BULK_AGG_EN;
mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val);
}
static void mt76x2u_power_on_rf_patch(struct mt76x02_dev *dev)
{
mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(0) | BIT(16));
udelay(1);
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x1c), 0xff);
mt76_set(dev, MT_VEND_ADDR(CFG, 0x1c), 0x30);
mt76_wr(dev, MT_VEND_ADDR(CFG, 0x14), 0x484f);
udelay(1);
mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(17));
usleep_range(150, 200);
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x130), BIT(16));
usleep_range(50, 100);
mt76_set(dev, MT_VEND_ADDR(CFG, 0x14c), BIT(19) | BIT(20));
}
static void mt76x2u_power_on_rf(struct mt76x02_dev *dev, int unit)
{
int shift = unit ? 8 : 0;
u32 val = (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift;
/* Enable RF BG */
mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(0) << shift);
usleep_range(10, 20);
/* Enable RFDIG LDO/AFE/ABB/ADDA */
mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), val);
usleep_range(10, 20);
/* Switch RFDIG power to internal LDO */
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x130), BIT(2) << shift);
usleep_range(10, 20);
mt76x2u_power_on_rf_patch(dev);
mt76_set(dev, 0x530, 0xf);
}
static void mt76x2u_power_on(struct mt76x02_dev *dev)
{
u32 val;
/* Turn on WL MTCMOS */
mt76_set(dev, MT_VEND_ADDR(CFG, 0x148),
MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP);
val = MT_WLAN_MTC_CTRL_STATE_UP |
MT_WLAN_MTC_CTRL_PWR_ACK |
MT_WLAN_MTC_CTRL_PWR_ACK_S;
mt76_poll(dev, MT_VEND_ADDR(CFG, 0x148), val, val, 1000);
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0x7f << 16);
usleep_range(10, 20);
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0xf << 24);
usleep_range(10, 20);
mt76_set(dev, MT_VEND_ADDR(CFG, 0x148), 0xf << 24);
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0xfff);
/* Turn on AD/DA power down */
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x1204), BIT(3));
/* WLAN function enable */
mt76_set(dev, MT_VEND_ADDR(CFG, 0x80), BIT(0));
/* Release BBP software reset */
mt76_clear(dev, MT_VEND_ADDR(CFG, 0x64), BIT(18));
mt76x2u_power_on_rf(dev, 0);
mt76x2u_power_on_rf(dev, 1);
}
static int mt76x2u_init_eeprom(struct mt76x02_dev *dev)
{
u32 val, i;
dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev,
MT7612U_EEPROM_SIZE,
GFP_KERNEL);
dev->mt76.eeprom.size = MT7612U_EEPROM_SIZE;
if (!dev->mt76.eeprom.data)
return -ENOMEM;
for (i = 0; i + 4 <= MT7612U_EEPROM_SIZE; i += 4) {
val = mt76_rr(dev, MT_VEND_ADDR(EEPROM, i));
put_unaligned_le32(val, dev->mt76.eeprom.data + i);
}
mt76x02_eeprom_parse_hw_cap(dev);
return 0;
}
int mt76x2u_init_hardware(struct mt76x02_dev *dev)
{
int i, k, err;
mt76x2_reset_wlan(dev, true);
mt76x2u_power_on(dev);
if (!mt76x02_wait_for_mac(&dev->mt76))
return -ETIMEDOUT;
err = mt76x2u_mcu_fw_init(dev);
if (err < 0)
return err;
if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG,
MT_WPDMA_GLO_CFG_TX_DMA_BUSY |
MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100))
return -EIO;
/* wait for asic ready after fw load. */
if (!mt76x02_wait_for_mac(&dev->mt76))
return -ETIMEDOUT;
mt76x2u_init_dma(dev);
err = mt76x2u_mcu_init(dev);
if (err < 0)
return err;
err = mt76x2u_mac_reset(dev);
if (err < 0)
return err;
mt76x02_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR);
dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
if (!mt76x02_wait_for_txrx_idle(&dev->mt76))
return -ETIMEDOUT;
/* reset wcid table */
for (i = 0; i < 256; i++)
mt76x02_mac_wcid_setup(dev, i, 0, NULL);
/* reset shared key table and pairwise key table */
for (i = 0; i < 16; i++) {
for (k = 0; k < 4; k++)
mt76x02_mac_shared_key_setup(dev, i, k, NULL);
}
mt76x02u_init_beacon_config(dev);
mt76_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e);
mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x583f);
err = mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0);
if (err < 0)
return err;
mt76x02_phy_set_rxpath(dev);
mt76x02_phy_set_txdac(dev);
return mt76x2u_mac_stop(dev);
}
int mt76x2u_register_device(struct mt76x02_dev *dev)
{
struct ieee80211_hw *hw = mt76_hw(dev);
struct mt76_usb *usb = &dev->mt76.usb;
bool vht;
int err;
INIT_DELAYED_WORK(&dev->cal_work, mt76x2u_phy_calibrate);
err = mt76x02_init_device(dev);
if (err)
return err;
err = mt76x2u_init_eeprom(dev);
if (err < 0)
return err;
usb->mcu.data = devm_kmalloc(dev->mt76.dev, MCU_RESP_URB_SIZE,
GFP_KERNEL);
if (!usb->mcu.data)
return -ENOMEM;
err = mt76u_alloc_queues(&dev->mt76);
if (err < 0)
goto fail;
err = mt76x2u_init_hardware(dev);
if (err < 0)
goto fail;
/* check hw sg support in order to enable AMSDU */
hw->max_tx_fragments = dev->mt76.usb.sg_en ? MT_TX_SG_MAX_SIZE : 1;
switch (dev->mt76.rev) {
case 0x76320044:
/* these ASIC revisions do not support VHT */
vht = false;
break;
default:
vht = true;
break;
}
err = mt76_register_device(&dev->mt76, vht, mt76x02_rates,
ARRAY_SIZE(mt76x02_rates));
if (err)
goto fail;
set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
mt76x02_init_debugfs(dev);
mt76x2_init_txpower(dev, &dev->mphy.sband_2g.sband);
mt76x2_init_txpower(dev, &dev->mphy.sband_5g.sband);
return 0;
fail:
mt76x2u_cleanup(dev);
return err;
}
void mt76x2u_stop_hw(struct mt76x02_dev *dev)
{
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mphy.mac_work);
mt76x2u_mac_stop(dev);
}
void mt76x2u_cleanup(struct mt76x02_dev *dev)
{
mt76x02_mcu_set_radio_state(dev, false);
mt76x2u_stop_hw(dev);
mt76u_queues_deinit(&dev->mt76);
}
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2u.h"
#include "eeprom.h"
static void mt76x2u_mac_fixup_xtal(struct mt76x02_dev *dev)
{
s8 offset = 0;
u16 eep_val;
eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_2);
offset = eep_val & 0x7f;
if ((eep_val & 0xff) == 0xff)
offset = 0;
else if (eep_val & 0x80)
offset = 0 - offset;
eep_val >>= 8;
if (eep_val == 0x00 || eep_val == 0xff) {
eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_1);
eep_val &= 0xff;
if (eep_val == 0x00 || eep_val == 0xff)
eep_val = 0x14;
}
eep_val &= 0x7f;
mt76_rmw_field(dev, MT_VEND_ADDR(CFG, MT_XO_CTRL5),
MT_XO_CTRL5_C2_VAL, eep_val + offset);
mt76_set(dev, MT_VEND_ADDR(CFG, MT_XO_CTRL6), MT_XO_CTRL6_C2_CTRL);
mt76_wr(dev, 0x504, 0x06000000);
mt76_wr(dev, 0x50c, 0x08800000);
mdelay(5);
mt76_wr(dev, 0x504, 0x0);
/* decrease SIFS from 16us to 13us */
mt76_rmw_field(dev, MT_XIFS_TIME_CFG,
MT_XIFS_TIME_CFG_OFDM_SIFS, 0xd);
mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, MT_BKOFF_SLOT_CFG_CC_DELAY, 1);
/* init fce */
mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN);
eep_val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2);
switch (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) {
case 0:
mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80);
break;
case 1:
mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0);
break;
default:
break;
}
}
int mt76x2u_mac_reset(struct mt76x02_dev *dev)
{
mt76_wr(dev, MT_WPDMA_GLO_CFG, BIT(4) | BIT(5));
/* init pbf regs */
mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f);
mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf);
mt76_write_mac_initvals(dev);
mt76_wr(dev, MT_TX_LINK_CFG, 0x1020);
mt76_wr(dev, MT_AUTO_RSP_CFG, 0x13);
mt76_wr(dev, MT_MAX_LEN_CFG, 0x2f00);
mt76_wr(dev, MT_WMM_AIFSN, 0x2273);
mt76_wr(dev, MT_WMM_CWMIN, 0x2344);
mt76_wr(dev, MT_WMM_CWMAX, 0x34aa);
mt76_clear(dev, MT_MAC_SYS_CTRL,
MT_MAC_SYS_CTRL_RESET_CSR |
MT_MAC_SYS_CTRL_RESET_BBP);
if (is_mt7612(dev))
mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN);
mt76_set(dev, MT_EXT_CCA_CFG, 0xf000);
mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31));
mt76x2u_mac_fixup_xtal(dev);
return 0;
}
int mt76x2u_mac_stop(struct mt76x02_dev *dev)
{
int i, count = 0, val;
bool stopped = false;
u32 rts_cfg;
if (test_bit(MT76_REMOVED, &dev->mphy.state))
return -EIO;
rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG);
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT);
mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
mt76_clear(dev, MT_TXOP_HLDR_ET, MT_TXOP_HLDR_TX40M_BLK_EN);
/* wait tx dma to stop */
for (i = 0; i < 2000; i++) {
val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG));
if (!(val & MT_USB_DMA_CFG_TX_BUSY) && i > 10)
break;
usleep_range(50, 100);
}
/* page count on TxQ */
for (i = 0; i < 200; i++) {
if (!(mt76_rr(dev, 0x0438) & 0xffffffff) &&
!(mt76_rr(dev, 0x0a30) & 0x000000ff) &&
!(mt76_rr(dev, 0x0a34) & 0xff00ff00))
break;
usleep_range(10, 20);
}
/* disable tx-rx */
mt76_clear(dev, MT_MAC_SYS_CTRL,
MT_MAC_SYS_CTRL_ENABLE_RX |
MT_MAC_SYS_CTRL_ENABLE_TX);
/* Wait for MAC to become idle */
for (i = 0; i < 1000; i++) {
if (!(mt76_rr(dev, MT_MAC_STATUS) & MT_MAC_STATUS_TX) &&
!mt76_rr(dev, MT_BBP(IBI, 12))) {
stopped = true;
break;
}
usleep_range(10, 20);
}
if (!stopped) {
mt76_set(dev, MT_BBP(CORE, 4), BIT(1));
mt76_clear(dev, MT_BBP(CORE, 4), BIT(1));
mt76_set(dev, MT_BBP(CORE, 4), BIT(0));
mt76_clear(dev, MT_BBP(CORE, 4), BIT(0));
}
/* page count on RxQ */
for (i = 0; i < 200; i++) {
if (!(mt76_rr(dev, 0x0430) & 0x00ff0000) &&
!(mt76_rr(dev, 0x0a30) & 0xffffffff) &&
!(mt76_rr(dev, 0x0a34) & 0xffffffff) &&
++count > 10)
break;
msleep(50);
}
if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 2000))
dev_warn(dev->mt76.dev, "MAC RX failed to stop\n");
/* wait rx dma to stop */
for (i = 0; i < 2000; i++) {
val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG));
if (!(val & MT_USB_DMA_CFG_RX_BUSY) && i > 10)
break;
usleep_range(50, 100);
}
mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg);
return 0;
}
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2u.h"
#include "../mt76x02_usb.h"
static int mt76x2u_start(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
int ret;
ret = mt76x02u_mac_start(dev);
if (ret)
return ret;
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
MT_MAC_WORK_INTERVAL);
set_bit(MT76_STATE_RUNNING, &dev->mphy.state);
return 0;
}
static void mt76x2u_stop(struct ieee80211_hw *hw, bool suspend)
{
struct mt76x02_dev *dev = hw->priv;
clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
mt76u_stop_tx(&dev->mt76);
mt76x2u_stop_hw(dev);
}
int mt76x2u_set_channel(struct mt76_phy *mphy)
{
struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, mt76);
int err;
mt76x02_pre_tbtt_enable(dev, false);
mt76x2_mac_stop(dev, false);
err = mt76x2u_phy_set_channel(dev, &mphy->chandef);
mt76x02_mac_cc_reset(dev);
mt76x2_mac_resume(dev);
mt76x02_pre_tbtt_enable(dev, true);
return err;
}
static int
mt76x2u_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{
struct mt76x02_dev *dev = hw->priv;
int err = 0;
mutex_lock(&dev->mt76.mutex);
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
if (!(hw->conf.flags & IEEE80211_CONF_MONITOR))
dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC;
else
dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC;
mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter);
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {
struct mt76_phy *mphy = &dev->mphy;
dev->txpower_conf = hw->conf.power_level * 2;
dev->txpower_conf = mt76_get_sar_power(mphy,
mphy->chandef.chan,
dev->txpower_conf);
/* convert to per-chain power for 2x2 devices */
dev->txpower_conf -= 6;
if (test_bit(MT76_STATE_RUNNING, &mphy->state))
mt76x2_phy_set_txpower(dev);
}
mutex_unlock(&dev->mt76.mutex);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
mt76_update_channel(&dev->mphy);
return err;
}
const struct ieee80211_ops mt76x2u_ops = {
.add_chanctx = ieee80211_emulate_add_chanctx,
.remove_chanctx = ieee80211_emulate_remove_chanctx,
.change_chanctx = ieee80211_emulate_change_chanctx,
.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
.tx = mt76x02_tx,
.start = mt76x2u_start,
.stop = mt76x2u_stop,
.add_interface = mt76x02_add_interface,
.remove_interface = mt76x02_remove_interface,
.sta_state = mt76_sta_state,
.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
.set_key = mt76x02_set_key,
.ampdu_action = mt76x02_ampdu_action,
.config = mt76x2u_config,
.wake_tx_queue = mt76_wake_tx_queue,
.bss_info_changed = mt76x02_bss_info_changed,
.configure_filter = mt76x02_configure_filter,
.conf_tx = mt76x02_conf_tx,
.sw_scan_start = mt76_sw_scan,
.sw_scan_complete = mt76x02_sw_scan_complete,
.sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
.get_txpower = mt76_get_txpower,
.get_survey = mt76_get_survey,
.set_tim = mt76_set_tim,
.release_buffered_frames = mt76_release_buffered_frames,
.get_antenna = mt76_get_antenna,
.set_sar_specs = mt76x2_set_sar_specs,
};
@@ -0,0 +1,255 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include <linux/firmware.h>
#include "mt76x2u.h"
#include "eeprom.h"
#include "../mt76x02_usb.h"
#define MT_CMD_HDR_LEN 4
#define MCU_FW_URB_MAX_PAYLOAD 0x3900
#define MCU_ROM_PATCH_MAX_PAYLOAD 2048
#define MT76U_MCU_ILM_OFFSET 0x80000
#define MT76U_MCU_DLM_OFFSET 0x110000
#define MT76U_MCU_ROM_PATCH_OFFSET 0x90000
static void mt76x2u_mcu_load_ivb(struct mt76x02_dev *dev)
{
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
USB_DIR_OUT | USB_TYPE_VENDOR,
0x12, 0, NULL, 0);
}
static void mt76x2u_mcu_enable_patch(struct mt76x02_dev *dev)
{
struct mt76_usb *usb = &dev->mt76.usb;
static const u8 data[] = {
0x6f, 0xfc, 0x08, 0x01,
0x20, 0x04, 0x00, 0x00,
0x00, 0x09, 0x00,
};
memcpy(usb->data, data, sizeof(data));
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
USB_DIR_OUT | USB_TYPE_CLASS,
0x12, 0, usb->data, sizeof(data));
}
static void mt76x2u_mcu_reset_wmt(struct mt76x02_dev *dev)
{
struct mt76_usb *usb = &dev->mt76.usb;
u8 data[] = {
0x6f, 0xfc, 0x05, 0x01,
0x07, 0x01, 0x00, 0x04
};
memcpy(usb->data, data, sizeof(data));
mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE,
USB_DIR_OUT | USB_TYPE_CLASS,
0x12, 0, usb->data, sizeof(data));
}
static int mt76x2u_mcu_load_rom_patch(struct mt76x02_dev *dev)
{
bool rom_protect = !is_mt7612(dev);
struct mt76x02_patch_header *hdr;
u32 val, patch_mask, patch_reg;
const struct firmware *fw;
int err;
if (rom_protect &&
!mt76_poll_msec(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) {
dev_err(dev->mt76.dev,
"could not get hardware semaphore for ROM PATCH\n");
return -ETIMEDOUT;
}
if (mt76xx_rev(dev) >= MT76XX_REV_E3) {
patch_mask = BIT(0);
patch_reg = MT_MCU_CLOCK_CTL;
} else {
patch_mask = BIT(1);
patch_reg = MT_MCU_COM_REG0;
}
if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) {
dev_info(dev->mt76.dev, "ROM patch already applied\n");
return 0;
}
err = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev);
if (err < 0)
return err;
if (!fw || !fw->data || fw->size <= sizeof(*hdr)) {
dev_err(dev->mt76.dev, "failed to load firmware\n");
err = -EIO;
goto out;
}
hdr = (struct mt76x02_patch_header *)fw->data;
dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time);
/* enable USB_DMA_CFG */
val = MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN |
FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20);
mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val);
/* vendor reset */
mt76x02u_mcu_fw_reset(dev);
usleep_range(5000, 10000);
/* enable FCE to send in-band cmd */
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
/* FCE tx_fs_base_ptr */
mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
/* FCE tx_fs_max_cnt */
mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1);
/* FCE pdma enable */
mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
/* FCE skip_fs_en */
mt76_wr(dev, MT_FCE_SKIP_FS, 0x3);
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr),
fw->size - sizeof(*hdr),
MCU_ROM_PATCH_MAX_PAYLOAD,
MT76U_MCU_ROM_PATCH_OFFSET);
if (err < 0) {
err = -EIO;
goto out;
}
mt76x2u_mcu_enable_patch(dev);
mt76x2u_mcu_reset_wmt(dev);
mdelay(20);
if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 100)) {
dev_err(dev->mt76.dev, "failed to load ROM patch\n");
err = -ETIMEDOUT;
}
out:
if (rom_protect)
mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1);
release_firmware(fw);
return err;
}
static int mt76x2u_mcu_load_firmware(struct mt76x02_dev *dev)
{
u32 val, dlm_offset = MT76U_MCU_DLM_OFFSET;
const struct mt76x02_fw_header *hdr;
int err, len, ilm_len, dlm_len;
const struct firmware *fw;
err = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev);
if (err < 0)
return err;
if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
err = -EINVAL;
goto out;
}
hdr = (const struct mt76x02_fw_header *)fw->data;
ilm_len = le32_to_cpu(hdr->ilm_len);
dlm_len = le32_to_cpu(hdr->dlm_len);
len = sizeof(*hdr) + ilm_len + dlm_len;
if (fw->size != len) {
err = -EINVAL;
goto out;
}
val = le16_to_cpu(hdr->fw_ver);
dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n",
(val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf);
val = le16_to_cpu(hdr->build_ver);
dev_info(dev->mt76.dev, "Build: %x\n", val);
dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time);
/* vendor reset */
mt76x02u_mcu_fw_reset(dev);
usleep_range(5000, 10000);
/* enable USB_DMA_CFG */
val = MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN |
FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20);
mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val);
/* enable FCE to send in-band cmd */
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
/* FCE tx_fs_base_ptr */
mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230);
/* FCE tx_fs_max_cnt */
mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1);
/* FCE pdma enable */
mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44);
/* FCE skip_fs_en */
mt76_wr(dev, MT_FCE_SKIP_FS, 0x3);
/* load ILM */
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr),
ilm_len, MCU_FW_URB_MAX_PAYLOAD,
MT76U_MCU_ILM_OFFSET);
if (err < 0) {
err = -EIO;
goto out;
}
/* load DLM */
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
dlm_offset += 0x800;
err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr) + ilm_len,
dlm_len, MCU_FW_URB_MAX_PAYLOAD,
dlm_offset);
if (err < 0) {
err = -EIO;
goto out;
}
mt76x2u_mcu_load_ivb(dev);
if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 100)) {
dev_err(dev->mt76.dev, "firmware failed to start\n");
err = -ETIMEDOUT;
goto out;
}
mt76_set(dev, MT_MCU_COM_REG0, BIT(1));
/* enable FCE to send in-band cmd */
mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1);
mt76x02_set_ethtool_fwver(dev, hdr);
dev_dbg(dev->mt76.dev, "firmware running\n");
out:
release_firmware(fw);
return err;
}
int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev)
{
int err;
err = mt76x2u_mcu_load_rom_patch(dev);
if (err < 0)
return err;
return mt76x2u_mcu_load_firmware(dev);
}
int mt76x2u_mcu_init(struct mt76x02_dev *dev)
{
int err;
err = mt76x02_mcu_function_select(dev, Q_SELECT, 1);
if (err < 0)
return err;
return mt76x02_mcu_set_radio_state(dev, true);
}
@@ -0,0 +1,201 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76x2u.h"
#include "eeprom.h"
#include "../mt76x02_phy.h"
static void
mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped)
{
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
bool is_5ghz = chan->band == NL80211_BAND_5GHZ;
if (dev->cal.channel_cal_done)
return;
if (mt76x2_channel_silent(dev))
return;
if (!mac_stopped)
mt76x2u_mac_stop(dev);
if (is_5ghz)
mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0);
mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz);
mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0);
mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0);
if (!mac_stopped)
mt76x2_mac_resume(dev);
mt76x2_apply_gain_adj(dev);
mt76x02_edcca_init(dev);
dev->cal.channel_cal_done = true;
}
void mt76x2u_phy_calibrate(struct work_struct *work)
{
struct mt76x02_dev *dev;
dev = container_of(work, struct mt76x02_dev, cal_work.work);
mutex_lock(&dev->mt76.mutex);
mt76x2u_phy_channel_calibrate(dev, false);
mt76x2_phy_tssi_compensate(dev);
mt76x2_phy_update_channel_gain(dev);
mutex_unlock(&dev->mt76.mutex);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
MT_CALIBRATE_INTERVAL);
}
int mt76x2u_phy_set_channel(struct mt76x02_dev *dev,
struct cfg80211_chan_def *chandef)
{
u32 ext_cca_chan[4] = {
[0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)),
[1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)),
[2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)),
[3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) |
FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)),
};
bool scan = test_bit(MT76_SCANNING, &dev->mphy.state);
struct ieee80211_channel *chan = chandef->chan;
u8 channel = chan->hw_value, bw, bw_index;
int ch_group_index, freq, freq1, ret;
dev->cal.channel_cal_done = false;
freq = chandef->chan->center_freq;
freq1 = chandef->center_freq1;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_40:
bw = 1;
if (freq1 > freq) {
bw_index = 1;
ch_group_index = 0;
} else {
bw_index = 3;
ch_group_index = 1;
}
channel += 2 - ch_group_index * 4;
break;
case NL80211_CHAN_WIDTH_80:
ch_group_index = (freq - freq1 + 30) / 20;
if (WARN_ON(ch_group_index < 0 || ch_group_index > 3))
ch_group_index = 0;
bw = 2;
bw_index = ch_group_index;
channel += 6 - ch_group_index * 4;
break;
default:
bw = 0;
bw_index = 0;
ch_group_index = 0;
break;
}
mt76x2_read_rx_gain(dev);
mt76x2_phy_set_txpower_regs(dev, chan->band);
mt76x2_configure_tx_delay(dev, chan->band, bw);
mt76x2_phy_set_txpower(dev);
mt76x02_phy_set_band(dev, chan->band, ch_group_index & 1);
mt76x02_phy_set_bw(dev, chandef->width, ch_group_index);
mt76_rmw(dev, MT_EXT_CCA_CFG,
(MT_EXT_CCA_CFG_CCA0 |
MT_EXT_CCA_CFG_CCA1 |
MT_EXT_CCA_CFG_CCA2 |
MT_EXT_CCA_CFG_CCA3 |
MT_EXT_CCA_CFG_CCA_MASK),
ext_cca_chan[ch_group_index]);
ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan);
if (ret)
return ret;
mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true);
/* Enable LDPC Rx */
if (mt76xx_rev(dev) >= MT76XX_REV_E3)
mt76_set(dev, MT_BBP(RXO, 13), BIT(10));
if (!dev->cal.init_cal_done) {
u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT);
if (val != 0xff)
mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0);
}
mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel);
/* Rx LPF calibration */
if (!dev->cal.init_cal_done)
mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0);
dev->cal.init_cal_done = true;
mt76_wr(dev, MT_BBP(AGC, 61), 0xff64a4e2);
mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010);
mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404);
mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070);
mt76_wr(dev, MT_TXOP_CTRL_CFG, 0X04101b3f);
mt76_set(dev, MT_BBP(TXO, 4), BIT(25));
mt76_set(dev, MT_BBP(RXO, 13), BIT(8));
if (scan)
return 0;
mt76x2u_phy_channel_calibrate(dev, true);
mt76x02_init_agc_gain(dev);
if (mt76x2_tssi_enabled(dev)) {
/* init default values for temp compensation */
mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
0x38);
mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP,
0x38);
/* init tssi calibration */
if (!mt76x2_channel_silent(dev)) {
struct ieee80211_channel *chan;
u32 flag = 0;
chan = dev->mphy.chandef.chan;
if (chan->band == NL80211_BAND_5GHZ)
flag |= BIT(0);
if (mt76x02_ext_pa_enabled(dev, chan->band))
flag |= BIT(8);
mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag);
dev->cal.tssi_cal_done = true;
}
}
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
MT_CALIBRATE_INTERVAL);
return 0;
}