mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
Add support to correctly configure the rx chainmask of variants that have additional auxiliary RX path. e.g., 4T5R. The auxiliary RX path is transparent to driver, but driver needs to correctly configure it in the set channel fw command. Co-developed-by: StanleyYP Wang <[email protected]> Signed-off-by: StanleyYP Wang <[email protected]> Signed-off-by: Shayne Chen <[email protected]> Signed-off-by: Felix Fietkau <[email protected]>
272 lines
5.9 KiB
C
272 lines
5.9 KiB
C
// SPDX-License-Identifier: ISC
|
|
/*
|
|
* Copyright (C) 2022 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/firmware.h>
|
|
#include "mt7996.h"
|
|
#include "eeprom.h"
|
|
|
|
static int mt7996_check_eeprom(struct mt7996_dev *dev)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
u16 val = get_unaligned_le16(eeprom);
|
|
|
|
switch (val) {
|
|
case 0x7990:
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static char *mt7996_eeprom_name(struct mt7996_dev *dev)
|
|
{
|
|
/* reserve for future variants */
|
|
return MT7996_EEPROM_DEFAULT;
|
|
}
|
|
|
|
static int
|
|
mt7996_eeprom_load_default(struct mt7996_dev *dev)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
const struct firmware *fw = NULL;
|
|
int ret;
|
|
|
|
ret = request_firmware(&fw, mt7996_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, MT7996_EEPROM_SIZE);
|
|
dev->flash_mode = true;
|
|
|
|
out:
|
|
release_firmware(fw);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7996_eeprom_load(struct mt7996_dev *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = mt76_eeprom_init(&dev->mt76, MT7996_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 = MT7996_EEPROM_BLOCK_SIZE;
|
|
|
|
ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* efuse info isn't enough */
|
|
if (free_block_num >= 59)
|
|
return -EINVAL;
|
|
|
|
/* read eeprom data from efuse */
|
|
block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
|
|
for (i = 0; i < block_num; i++) {
|
|
ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return mt7996_check_eeprom(dev);
|
|
}
|
|
|
|
static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_dev *dev)
|
|
{
|
|
#define MODE_HE_ONLY BIT(0)
|
|
#define WTBL_SIZE_GROUP GENMASK(31, 28)
|
|
u32 cap = 0;
|
|
int ret;
|
|
|
|
ret = mt7996_mcu_get_chip_config(dev, &cap);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (cap) {
|
|
dev->has_eht = !(cap & MODE_HE_ONLY);
|
|
dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP);
|
|
}
|
|
|
|
if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4)
|
|
dev->wtbl_size_group = 2; /* set default */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
|
|
{
|
|
u8 *eeprom = phy->dev->mt76.eeprom.data;
|
|
u32 val = eeprom[MT_EE_WIFI_CONF];
|
|
int ret = 0;
|
|
|
|
switch (phy->mt76->band_idx) {
|
|
case MT_BAND1:
|
|
val = FIELD_GET(MT_EE_WIFI_CONF1_BAND_SEL, val);
|
|
break;
|
|
case MT_BAND2:
|
|
val = eeprom[MT_EE_WIFI_CONF + 1];
|
|
val = FIELD_GET(MT_EE_WIFI_CONF2_BAND_SEL, val);
|
|
break;
|
|
default:
|
|
val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
|
|
break;
|
|
}
|
|
|
|
switch (val) {
|
|
case MT_EE_BAND_SEL_2GHZ:
|
|
phy->mt76->cap.has_2ghz = true;
|
|
break;
|
|
case MT_EE_BAND_SEL_5GHZ:
|
|
phy->mt76->cap.has_5ghz = true;
|
|
break;
|
|
case MT_EE_BAND_SEL_6GHZ:
|
|
phy->mt76->cap.has_6ghz = true;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
|
|
{
|
|
u8 path, rx_path, nss, band_idx = phy->mt76->band_idx;
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
struct mt76_phy *mphy = phy->mt76;
|
|
int max_path = 5, max_nss = 4;
|
|
int ret;
|
|
|
|
switch (band_idx) {
|
|
case MT_BAND1:
|
|
path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
|
|
eeprom[MT_EE_WIFI_CONF + 2]);
|
|
rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1,
|
|
eeprom[MT_EE_WIFI_CONF + 3]);
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
|
|
eeprom[MT_EE_WIFI_CONF + 5]);
|
|
break;
|
|
case MT_BAND2:
|
|
path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
|
|
eeprom[MT_EE_WIFI_CONF + 2]);
|
|
rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2,
|
|
eeprom[MT_EE_WIFI_CONF + 4]);
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
|
|
eeprom[MT_EE_WIFI_CONF + 5]);
|
|
break;
|
|
default:
|
|
path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
|
|
eeprom[MT_EE_WIFI_CONF + 1]);
|
|
rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0,
|
|
eeprom[MT_EE_WIFI_CONF + 3]);
|
|
nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
|
|
eeprom[MT_EE_WIFI_CONF + 4]);
|
|
break;
|
|
}
|
|
|
|
if (!path || path > max_path)
|
|
path = max_path;
|
|
|
|
if (!nss || nss > max_nss)
|
|
nss = max_nss;
|
|
|
|
nss = min_t(u8, nss, path);
|
|
|
|
if (path != rx_path)
|
|
phy->has_aux_rx = true;
|
|
|
|
mphy->antenna_mask = BIT(nss) - 1;
|
|
mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx];
|
|
dev->chainmask |= mphy->chainmask;
|
|
if (band_idx < MT_BAND2)
|
|
dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] +
|
|
hweight16(mphy->chainmask);
|
|
|
|
ret = mt7996_eeprom_parse_efuse_hw_cap(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return mt7996_eeprom_parse_band_config(phy);
|
|
}
|
|
|
|
int mt7996_eeprom_init(struct mt7996_dev *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = mt7996_eeprom_load(dev);
|
|
if (ret < 0) {
|
|
if (ret != -EINVAL)
|
|
return ret;
|
|
|
|
dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
|
|
ret = mt7996_eeprom_load_default(dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN);
|
|
mt76_eeprom_override(&dev->mphy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
|
|
struct ieee80211_channel *chan)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
int target_power;
|
|
|
|
if (chan->band == NL80211_BAND_5GHZ)
|
|
target_power = eeprom[MT_EE_TX0_POWER_5G +
|
|
mt7996_get_channel_group_5g(chan->hw_value)];
|
|
else if (chan->band == NL80211_BAND_6GHZ)
|
|
target_power = eeprom[MT_EE_TX0_POWER_6G +
|
|
mt7996_get_channel_group_6g(chan->hw_value)];
|
|
else
|
|
target_power = eeprom[MT_EE_TX0_POWER_2G];
|
|
|
|
return target_power;
|
|
}
|
|
|
|
s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
|
|
{
|
|
u8 *eeprom = dev->mt76.eeprom.data;
|
|
u32 val;
|
|
s8 delta;
|
|
|
|
if (band == NL80211_BAND_5GHZ)
|
|
val = eeprom[MT_EE_RATE_DELTA_5G];
|
|
else if (band == NL80211_BAND_6GHZ)
|
|
val = eeprom[MT_EE_RATE_DELTA_6G];
|
|
else
|
|
val = eeprom[MT_EE_RATE_DELTA_2G];
|
|
|
|
if (!(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;
|
|
}
|