mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
On single-adie mt7986 the only phy is bound to band 1, but its chainmask
is stored unshifted, because dev->chainshift is still zero while the
eeprom is parsed for the main phy. mt7915_set_antenna() on the other
hand shifts by chainshift * band_idx, so the representation of the
chainmask changed as soon as the antenna configuration was touched.
Until then, mt7915_mcu_set_chan_info() passed rx_path = 0 to the
firmware, since shifting the unshifted mask down clears all bits.
Keep the unshifted form for that case and add helpers for the band local
chainmask, so that only the band 1 phy of a dbdc device uses the shifted
form.
Fixes: 3eb50cc90534 ("wifi: mt76: mt7915: rely on band_idx of mt76_phy")
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
410 lines
9.6 KiB
C
410 lines
9.6 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/* Copyright (C) 2020 MediaTek Inc. */
|
|
|
|
#include <linux/firmware.h>
|
|
#include <linux/moduleparam.h>
|
|
#include "mt7915.h"
|
|
#include "eeprom.h"
|
|
|
|
static bool enable_6ghz;
|
|
module_param(enable_6ghz, bool, 0644);
|
|
MODULE_PARM_DESC(enable_6ghz, "Enable 6 GHz instead of 5 GHz on hardware that supports both");
|
|
|
|
static int mt7915_eeprom_load_precal(struct mt7915_dev *dev)
|
|
{
|
|
struct mt76_dev *mdev = &dev->mt76;
|
|
u8 *eeprom = mdev->eeprom.data;
|
|
u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2;
|
|
u32 size, val = eeprom[offs];
|
|
int ret;
|
|
|
|
if (!dev->flash_mode || !val)
|
|
return 0;
|
|
|
|
size = mt7915_get_cal_group_size(dev) + mt7915_get_cal_dpd_size(dev);
|
|
|
|
dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
|
|
if (!dev->cal)
|
|
return -ENOMEM;
|
|
|
|
offs = is_mt7915(&dev->mt76) ? MT_EE_PRECAL : MT_EE_PRECAL_V2;
|
|
|
|
ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
|
|
if (!ret)
|
|
return ret;
|
|
|
|
ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
|
|
if (!ret)
|
|
return ret;
|
|
|
|
dev_warn(mdev->dev, "missing precal data, size=%d\n", size);
|
|
devm_kfree(mdev->dev, dev->cal);
|
|
dev->cal = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7915_check_eeprom(struct mt7915_dev *dev)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
u16 val = get_unaligned_le16(eeprom);
|
|
|
|
#define CHECK_EEPROM_ERR(match) (match ? 0 : -EINVAL)
|
|
switch (val) {
|
|
case 0x7915:
|
|
return CHECK_EEPROM_ERR(is_mt7915(&dev->mt76));
|
|
case 0x7916:
|
|
return CHECK_EEPROM_ERR(is_mt7916(&dev->mt76));
|
|
case 0x7981:
|
|
return CHECK_EEPROM_ERR(is_mt7981(&dev->mt76));
|
|
case 0x7986:
|
|
return CHECK_EEPROM_ERR(is_mt7986(&dev->mt76));
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static char *mt7915_eeprom_name(struct mt7915_dev *dev)
|
|
{
|
|
switch (mt76_chip(&dev->mt76)) {
|
|
case 0x7915:
|
|
return dev->dbdc_support ?
|
|
MT7915_EEPROM_DEFAULT_DBDC : MT7915_EEPROM_DEFAULT;
|
|
case 0x7981:
|
|
/* mt7981 only supports mt7976 and only in DBDC mode */
|
|
return MT7981_EEPROM_MT7976_DEFAULT_DBDC;
|
|
case 0x7986:
|
|
switch (mt7915_check_adie(dev, true)) {
|
|
case MT7976_ONE_ADIE_DBDC:
|
|
return MT7986_EEPROM_MT7976_DEFAULT_DBDC;
|
|
case MT7975_ONE_ADIE:
|
|
return MT7986_EEPROM_MT7975_DEFAULT;
|
|
case MT7976_ONE_ADIE:
|
|
return MT7986_EEPROM_MT7976_DEFAULT;
|
|
case MT7975_DUAL_ADIE:
|
|
return MT7986_EEPROM_MT7975_DUAL_DEFAULT;
|
|
case MT7976_DUAL_ADIE:
|
|
return MT7986_EEPROM_MT7976_DUAL_DEFAULT;
|
|
default:
|
|
break;
|
|
}
|
|
return NULL;
|
|
default:
|
|
return MT7916_EEPROM_DEFAULT;
|
|
}
|
|
}
|
|
|
|
static int
|
|
mt7915_eeprom_load_default(struct mt7915_dev *dev)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
const struct firmware *fw = NULL;
|
|
int ret;
|
|
|
|
ret = request_firmware(&fw, mt7915_eeprom_name(dev), dev->mt76.dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!fw || !fw->data) {
|
|
dev_err(dev->mt76.dev, "Invalid default bin\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
memcpy(eeprom, fw->data, mt7915_eeprom_size(dev));
|
|
dev->flash_mode = true;
|
|
|
|
out:
|
|
release_firmware(fw);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7915_eeprom_load(struct mt7915_dev *dev)
|
|
{
|
|
int ret;
|
|
u16 eeprom_size = mt7915_eeprom_size(dev);
|
|
|
|
ret = mt76_eeprom_init(&dev->mt76, eeprom_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret) {
|
|
dev->flash_mode = true;
|
|
} else {
|
|
u8 free_block_num;
|
|
u32 block_num, i;
|
|
u32 eeprom_blk_size = MT7915_EEPROM_BLOCK_SIZE;
|
|
|
|
ret = mt7915_mcu_get_eeprom_free_block(dev, &free_block_num);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* efuse info isn't enough */
|
|
if (free_block_num >= 29)
|
|
return -EINVAL;
|
|
|
|
/* read eeprom data from efuse */
|
|
block_num = DIV_ROUND_UP(eeprom_size, eeprom_blk_size);
|
|
for (i = 0; i < block_num; i++) {
|
|
ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return mt7915_check_eeprom(dev);
|
|
}
|
|
|
|
static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
|
|
{
|
|
struct mt7915_dev *dev = phy->dev;
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
u8 band = phy->mt76->band_idx;
|
|
u32 val;
|
|
|
|
val = eeprom[MT_EE_WIFI_CONF + band];
|
|
val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
|
|
|
|
if (!is_mt7915(&dev->mt76)) {
|
|
switch (val) {
|
|
case MT_EE_V2_BAND_SEL_5GHZ:
|
|
phy->mt76->cap.has_5ghz = true;
|
|
return;
|
|
case MT_EE_V2_BAND_SEL_6GHZ:
|
|
phy->mt76->cap.has_6ghz = true;
|
|
return;
|
|
case MT_EE_V2_BAND_SEL_5GHZ_6GHZ:
|
|
if (enable_6ghz) {
|
|
phy->mt76->cap.has_6ghz = true;
|
|
u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band],
|
|
MT_EE_V2_BAND_SEL_6GHZ,
|
|
MT_EE_WIFI_CONF0_BAND_SEL);
|
|
} else {
|
|
phy->mt76->cap.has_5ghz = true;
|
|
u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band],
|
|
MT_EE_V2_BAND_SEL_5GHZ,
|
|
MT_EE_WIFI_CONF0_BAND_SEL);
|
|
}
|
|
/* force to buffer mode */
|
|
dev->flash_mode = true;
|
|
|
|
return;
|
|
default:
|
|
phy->mt76->cap.has_2ghz = true;
|
|
return;
|
|
}
|
|
} else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) {
|
|
val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ;
|
|
}
|
|
|
|
switch (val) {
|
|
case MT_EE_BAND_SEL_5GHZ:
|
|
phy->mt76->cap.has_5ghz = true;
|
|
break;
|
|
case MT_EE_BAND_SEL_2GHZ:
|
|
phy->mt76->cap.has_2ghz = true;
|
|
break;
|
|
default:
|
|
phy->mt76->cap.has_2ghz = true;
|
|
phy->mt76->cap.has_5ghz = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev,
|
|
struct mt7915_phy *phy)
|
|
{
|
|
u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data;
|
|
struct mt76_phy *mphy = phy->mt76;
|
|
u8 band = phy->mt76->band_idx;
|
|
|
|
mt7915_eeprom_parse_band_config(phy);
|
|
|
|
/* read tx/rx path from eeprom */
|
|
if (is_mt7915(&dev->mt76)) {
|
|
path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
|
|
eeprom[MT_EE_WIFI_CONF]);
|
|
} else {
|
|
path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
|
|
eeprom[MT_EE_WIFI_CONF + band]);
|
|
}
|
|
|
|
if (!path || path > 4)
|
|
path = 4;
|
|
|
|
/* read tx/rx stream */
|
|
nss = path;
|
|
if (dev->dbdc_support) {
|
|
if (is_mt7915(&dev->mt76)) {
|
|
path = min_t(u8, path, 2);
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0,
|
|
eeprom[MT_EE_WIFI_CONF + 3]);
|
|
if (band)
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1,
|
|
eeprom[MT_EE_WIFI_CONF + 3]);
|
|
} else {
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM,
|
|
eeprom[MT_EE_WIFI_CONF + 2 + band]);
|
|
}
|
|
|
|
if (!is_mt798x(&dev->mt76))
|
|
nss_max = 2;
|
|
}
|
|
|
|
if (!nss)
|
|
nss = nss_max;
|
|
nss = min_t(u8, min_t(u8, nss_max, nss), path);
|
|
|
|
mphy->chainmask = BIT(path) - 1;
|
|
if (band && dev->dbdc_support)
|
|
mphy->chainmask <<= dev->chainshift;
|
|
mphy->antenna_mask = BIT(nss) - 1;
|
|
dev->chainmask |= mphy->chainmask;
|
|
dev->chainshift = hweight8(dev->mphy.chainmask);
|
|
}
|
|
|
|
int mt7915_eeprom_init(struct mt7915_dev *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = mt7915_eeprom_load(dev);
|
|
if (ret < 0) {
|
|
if (ret != -EINVAL)
|
|
return ret;
|
|
|
|
dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
|
|
ret = mt7915_eeprom_load_default(dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
mt7915_eeprom_load_precal(dev);
|
|
mt7915_eeprom_parse_hw_cap(dev, &dev->phy);
|
|
memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR,
|
|
ETH_ALEN);
|
|
|
|
return mt76_eeprom_override(&dev->mphy);
|
|
}
|
|
|
|
int mt7915_eeprom_get_target_power(struct mt7915_dev *dev,
|
|
struct ieee80211_channel *chan,
|
|
u8 chain_idx)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
int index, target_power;
|
|
bool tssi_on, is_7976;
|
|
|
|
if (chain_idx > 3)
|
|
return -EINVAL;
|
|
|
|
tssi_on = mt7915_tssi_enabled(dev, chan->band);
|
|
is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76);
|
|
|
|
if (chan->band == NL80211_BAND_2GHZ) {
|
|
if (is_7976) {
|
|
index = MT_EE_TX0_POWER_2G_V2 + chain_idx;
|
|
target_power = eeprom[index];
|
|
} else {
|
|
index = MT_EE_TX0_POWER_2G + chain_idx * 3;
|
|
target_power = eeprom[index];
|
|
|
|
if (!tssi_on)
|
|
target_power += eeprom[index + 1];
|
|
}
|
|
} else if (chan->band == NL80211_BAND_5GHZ) {
|
|
int group = mt7915_get_channel_group_5g(chan->hw_value, is_7976);
|
|
|
|
if (is_7976) {
|
|
index = MT_EE_TX0_POWER_5G_V2 + chain_idx * 5;
|
|
target_power = eeprom[index + group];
|
|
} else {
|
|
index = MT_EE_TX0_POWER_5G + chain_idx * 12;
|
|
target_power = eeprom[index + group];
|
|
|
|
if (!tssi_on)
|
|
target_power += eeprom[index + 8];
|
|
}
|
|
} else {
|
|
int group = mt7915_get_channel_group_6g(chan->hw_value);
|
|
|
|
index = MT_EE_TX0_POWER_6G_V2 + chain_idx * 8;
|
|
target_power = is_7976 ? eeprom[index + group] : 0;
|
|
}
|
|
|
|
return target_power;
|
|
}
|
|
|
|
s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
u32 val, offs;
|
|
s8 delta;
|
|
bool is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76);
|
|
|
|
if (band == NL80211_BAND_2GHZ)
|
|
offs = is_7976 ? MT_EE_RATE_DELTA_2G_V2 : MT_EE_RATE_DELTA_2G;
|
|
else if (band == NL80211_BAND_5GHZ)
|
|
offs = is_7976 ? MT_EE_RATE_DELTA_5G_V2 : MT_EE_RATE_DELTA_5G;
|
|
else
|
|
offs = is_7976 ? MT_EE_RATE_DELTA_6G_V2 : 0;
|
|
|
|
val = eeprom[offs];
|
|
|
|
if (!offs || !(val & MT_EE_RATE_DELTA_EN))
|
|
return 0;
|
|
|
|
delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val);
|
|
|
|
return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
|
|
}
|
|
|
|
bool
|
|
mt7915_eeprom_has_background_radar(struct mt7915_dev *dev)
|
|
{
|
|
u8 val, buf[MT7915_EEPROM_BLOCK_SIZE];
|
|
u8 band_sel, tx_path, rx_path;
|
|
int offs = MT_EE_WIFI_CONF + 1;
|
|
|
|
switch (mt76_chip(&dev->mt76)) {
|
|
case 0x7915:
|
|
return true;
|
|
case 0x7906:
|
|
/* read efuse to check background radar capability */
|
|
if (mt7915_mcu_get_eeprom(dev, offs, buf))
|
|
break;
|
|
|
|
val = buf[offs % MT7915_EEPROM_BLOCK_SIZE];
|
|
band_sel = u8_get_bits(val, MT_EE_WIFI_CONF0_BAND_SEL);
|
|
tx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_TX_PATH);
|
|
rx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_RX_PATH);
|
|
|
|
return (band_sel == MT_EE_V2_BAND_SEL_5GHZ &&
|
|
tx_path == rx_path && rx_path == 2);
|
|
case 0x7981:
|
|
case 0x7986:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const u8 mt7915_sku_group_len[] = {
|
|
[SKU_CCK] = 4,
|
|
[SKU_OFDM] = 8,
|
|
[SKU_HT_BW20] = 8,
|
|
[SKU_HT_BW40] = 9,
|
|
[SKU_VHT_BW20] = 12,
|
|
[SKU_VHT_BW40] = 12,
|
|
[SKU_VHT_BW80] = 12,
|
|
[SKU_VHT_BW160] = 12,
|
|
[SKU_HE_RU26] = 12,
|
|
[SKU_HE_RU52] = 12,
|
|
[SKU_HE_RU106] = 12,
|
|
[SKU_HE_RU242] = 12,
|
|
[SKU_HE_RU484] = 12,
|
|
[SKU_HE_RU996] = 12,
|
|
[SKU_HE_RU2x996] = 12
|
|
};
|