mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 05:16:57 +00:00
MediaTek has asked to switch from the ISC license to BSD-3-Clause-Clear, in order to improve clarity and the legal integrity of the code. The BSD-3-Clause license includes the "no endorsement" clause, which is important for protecting the reputation of the original authors and contributors by preventing unauthorized use of their names for endorsement purposes. This clause is absent in the BSD-2-Clause license, which is more permissive but lacks this specific protection. This change also cleans up the license of some Kconfig/Makefile files, which were accidentally marked as GPL. The GPL 2.0 remains in use on mt76x0, as well as two source files in mt7615 for which the license situation still needs to be clarified. Signed-off-by: Felix Fietkau <[email protected]>
487 lines
11 KiB
C
487 lines
11 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/* Copyright (C) 2023 MediaTek Inc. */
|
|
|
|
#include <linux/acpi.h>
|
|
#include "mt792x.h"
|
|
|
|
static const char * const cc_list_all[] = {
|
|
"00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR",
|
|
"CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME",
|
|
"MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR",
|
|
"TW", "TH", "UA", "GB", "US", "VN", "KH", "PY",
|
|
};
|
|
|
|
static const char * const cc_list_eu[] = {
|
|
"AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK",
|
|
"EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE",
|
|
"IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL",
|
|
"NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE",
|
|
"CH",
|
|
};
|
|
|
|
static const char * const cc_list_be[] = {
|
|
"AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU",
|
|
"RW", "VN", "KR", "UA", "", "", "", "",
|
|
"EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK",
|
|
"US",
|
|
};
|
|
|
|
static int
|
|
mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len)
|
|
{
|
|
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
struct mt76_dev *mdev = &dev->mt76;
|
|
union acpi_object *sar_root;
|
|
acpi_handle root, handle;
|
|
acpi_status status;
|
|
u32 i = 0;
|
|
int ret;
|
|
|
|
root = ACPI_HANDLE(mdev->dev);
|
|
if (!root)
|
|
return -EOPNOTSUPP;
|
|
|
|
status = acpi_get_handle(root, method, &handle);
|
|
if (ACPI_FAILURE(status))
|
|
return -EIO;
|
|
|
|
status = acpi_evaluate_object(handle, NULL, NULL, &buf);
|
|
if (ACPI_FAILURE(status))
|
|
return -EIO;
|
|
|
|
sar_root = buf.pointer;
|
|
if (sar_root->type != ACPI_TYPE_PACKAGE ||
|
|
sar_root->package.count < 4 ||
|
|
sar_root->package.elements[0].type != ACPI_TYPE_INTEGER) {
|
|
dev_err(mdev->dev, "sar cnt = %d\n",
|
|
sar_root->package.count);
|
|
ret = -EINVAL;
|
|
goto free;
|
|
}
|
|
|
|
if (!*tbl) {
|
|
*tbl = devm_kzalloc(mdev->dev, sar_root->package.count,
|
|
GFP_KERNEL);
|
|
if (!*tbl) {
|
|
ret = -ENOMEM;
|
|
goto free;
|
|
}
|
|
}
|
|
|
|
if (len)
|
|
*len = sar_root->package.count;
|
|
|
|
for (i = 0; i < sar_root->package.count; i++) {
|
|
union acpi_object *sar_unit = &sar_root->package.elements[i];
|
|
|
|
if (sar_unit->type != ACPI_TYPE_INTEGER)
|
|
break;
|
|
|
|
*(*tbl + i) = (u8)sar_unit->integer.value;
|
|
}
|
|
|
|
ret = i == sar_root->package.count ? 0 : -EINVAL;
|
|
free:
|
|
kfree(sar_root);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* MTCL : Country List Table for 6G band */
|
|
/* MTCL : Country List Table for 6G band and 11BE */
|
|
static int
|
|
mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
|
|
{
|
|
int len, ret;
|
|
|
|
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (len == sizeof(struct mt792x_asar_cl))
|
|
*version = ((struct mt792x_asar_cl *)*table)->version;
|
|
else if (len == sizeof(struct mt792x_asar_cl_v3))
|
|
*version = ((struct mt792x_asar_cl_v3 *)*table)->version;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* MTDS : Dynamic SAR Power Table */
|
|
static int
|
|
mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)
|
|
{
|
|
int len, ret, sarlen, prelen, tblcnt;
|
|
bool enable;
|
|
|
|
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Table content validation */
|
|
switch (version) {
|
|
case 1:
|
|
enable = ((struct mt792x_asar_dyn *)*table)->enable;
|
|
sarlen = sizeof(struct mt792x_asar_dyn_limit);
|
|
prelen = sizeof(struct mt792x_asar_dyn);
|
|
break;
|
|
case 2:
|
|
enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable;
|
|
sarlen = sizeof(struct mt792x_asar_dyn_limit_v2);
|
|
prelen = sizeof(struct mt792x_asar_dyn_v2);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
tblcnt = (len - prelen) / sarlen;
|
|
if (!enable ||
|
|
tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* MTGS : Geo SAR Power Table */
|
|
static int
|
|
mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)
|
|
{
|
|
int len, ret, sarlen, prelen, tblcnt;
|
|
|
|
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Table content validation */
|
|
switch (version) {
|
|
case 1:
|
|
sarlen = sizeof(struct mt792x_asar_geo_limit);
|
|
prelen = sizeof(struct mt792x_asar_geo);
|
|
break;
|
|
case 2:
|
|
sarlen = sizeof(struct mt792x_asar_geo_limit_v2);
|
|
prelen = sizeof(struct mt792x_asar_geo_v2);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
tblcnt = (len - prelen) / sarlen;
|
|
if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* MTFG : Flag Table */
|
|
static int
|
|
mt792x_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table)
|
|
{
|
|
int len, ret;
|
|
|
|
ret = mt792x_acpi_read(dev, MT792x_ACPI_MTFG, table, &len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (len < MT792x_ASAR_MIN_FG)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt792x_init_acpi_sar(struct mt792x_dev *dev)
|
|
{
|
|
struct mt792x_acpi_sar *asar;
|
|
int ret;
|
|
|
|
asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL);
|
|
if (!asar)
|
|
return -ENOMEM;
|
|
|
|
ret = mt792x_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver);
|
|
if (ret) {
|
|
devm_kfree(dev->mt76.dev, asar->countrylist);
|
|
asar->countrylist = NULL;
|
|
}
|
|
|
|
ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);
|
|
if (ret) {
|
|
devm_kfree(dev->mt76.dev, asar->dyn);
|
|
asar->dyn = NULL;
|
|
}
|
|
|
|
/* MTGS is optional */
|
|
ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver);
|
|
if (ret) {
|
|
devm_kfree(dev->mt76.dev, asar->geo);
|
|
asar->geo = NULL;
|
|
}
|
|
|
|
/* MTFG is optional */
|
|
ret = mt792x_asar_acpi_read_mtfg(dev, (u8 **)&asar->fg);
|
|
if (ret) {
|
|
devm_kfree(dev->mt76.dev, asar->fg);
|
|
asar->fg = NULL;
|
|
}
|
|
dev->phy.acpisar = asar;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar);
|
|
|
|
static s8
|
|
mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,
|
|
enum nl80211_band band, s8 dyn_power)
|
|
{
|
|
struct mt792x_acpi_sar *asar = phy->acpisar;
|
|
struct mt792x_asar_geo_band *band_pwr;
|
|
s8 geo_power;
|
|
u8 idx, max;
|
|
|
|
if (!asar->geo)
|
|
return dyn_power;
|
|
|
|
switch (phy->mt76->dev->region) {
|
|
case NL80211_DFS_FCC:
|
|
idx = 0;
|
|
break;
|
|
case NL80211_DFS_ETSI:
|
|
idx = 1;
|
|
break;
|
|
default: /* WW */
|
|
idx = 2;
|
|
break;
|
|
}
|
|
|
|
if (asar->ver == 1) {
|
|
band_pwr = &asar->geo->tbl[idx].band[0];
|
|
max = ARRAY_SIZE(asar->geo->tbl[idx].band);
|
|
} else {
|
|
band_pwr = &asar->geo_v2->tbl[idx].band[0];
|
|
max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band);
|
|
}
|
|
|
|
switch (band) {
|
|
case NL80211_BAND_2GHZ:
|
|
idx = 0;
|
|
break;
|
|
case NL80211_BAND_5GHZ:
|
|
idx = 1;
|
|
break;
|
|
case NL80211_BAND_6GHZ:
|
|
idx = 2;
|
|
break;
|
|
default:
|
|
return dyn_power;
|
|
}
|
|
|
|
if (idx >= max)
|
|
return dyn_power;
|
|
|
|
geo_power = (band_pwr + idx)->pwr;
|
|
dyn_power += (band_pwr + idx)->offset;
|
|
|
|
return min(geo_power, dyn_power);
|
|
}
|
|
|
|
static s8
|
|
mt792x_asar_range_pwr(struct mt792x_phy *phy,
|
|
const struct cfg80211_sar_freq_ranges *range,
|
|
u8 idx)
|
|
{
|
|
const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
|
|
struct mt792x_acpi_sar *asar = phy->acpisar;
|
|
u8 *limit, band, max;
|
|
|
|
if (!capa)
|
|
return 127;
|
|
|
|
if (asar->ver == 1) {
|
|
limit = &asar->dyn->tbl[0].frp[0];
|
|
max = ARRAY_SIZE(asar->dyn->tbl[0].frp);
|
|
} else {
|
|
limit = &asar->dyn_v2->tbl[0].frp[0];
|
|
max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp);
|
|
}
|
|
|
|
if (idx >= max)
|
|
return 127;
|
|
|
|
if (range->start_freq >= 5945)
|
|
band = NL80211_BAND_6GHZ;
|
|
else if (range->start_freq >= 5150)
|
|
band = NL80211_BAND_5GHZ;
|
|
else
|
|
band = NL80211_BAND_2GHZ;
|
|
|
|
return mt792x_asar_get_geo_pwr(phy, band, limit[idx]);
|
|
}
|
|
|
|
int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)
|
|
{
|
|
const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
|
|
int i;
|
|
|
|
if (!phy->acpisar || !((struct mt792x_acpi_sar *)phy->acpisar)->dyn)
|
|
return 0;
|
|
|
|
/* When ACPI SAR enabled in HW, we should apply rules for .frp
|
|
* 1. w/o .sar_specs : set ACPI SAR power as the defatul value
|
|
* 2. w/ .sar_specs : set power with min(.sar_specs, ACPI_SAR)
|
|
*/
|
|
for (i = 0; i < capa->num_freq_ranges; i++) {
|
|
struct mt76_freq_range_power *frp = &phy->mt76->frp[i];
|
|
|
|
frp->range = set_default ? &capa->freq_ranges[i] : frp->range;
|
|
if (!frp->range)
|
|
continue;
|
|
|
|
frp->power = min_t(s8, set_default ? 127 : frp->power,
|
|
mt792x_asar_range_pwr(phy, frp->range, i));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power);
|
|
|
|
u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
|
|
{
|
|
struct mt792x_acpi_sar *acpisar = phy->acpisar;
|
|
struct mt792x_asar_fg *fg;
|
|
struct {
|
|
u8 acpi_idx;
|
|
u8 chip_idx;
|
|
} map[] = {
|
|
{ 1, 1 },
|
|
{ 4, 2 },
|
|
};
|
|
u8 flags = BIT(0);
|
|
int i, j;
|
|
|
|
if (!acpisar)
|
|
return 0;
|
|
|
|
fg = acpisar->fg;
|
|
if (!fg)
|
|
return flags;
|
|
|
|
/* pickup necessary settings per device and
|
|
* translate the index of bitmap for chip command.
|
|
*/
|
|
for (i = 0; i < fg->nr_flag; i++) {
|
|
for (j = 0; j < ARRAY_SIZE(map); j++) {
|
|
if (fg->flag[i] == map[j].acpi_idx) {
|
|
flags |= BIT(map[j].chip_idx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags);
|
|
|
|
static u32
|
|
mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl)
|
|
{
|
|
u32 config = 0;
|
|
u8 mode_be = 0;
|
|
|
|
mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be;
|
|
|
|
if (cl->version > 2 && cl->clbe[row] & BIT(column))
|
|
config |= (mode_be & 0x3) << 4;
|
|
|
|
return config;
|
|
}
|
|
|
|
static u32
|
|
mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl)
|
|
{
|
|
u32 config = 0;
|
|
u8 mode_6g, mode_5g9;
|
|
|
|
mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g;
|
|
mode_5g9 = (cl->mode_5g9 > 0x01) ? 0 : cl->mode_5g9;
|
|
|
|
if ((cl->cl6g[row] & BIT(column)) || cl->mode_6g == 0x02)
|
|
config |= (mode_6g & 0x3) << 2;
|
|
if (cl->version > 1 && cl->cl5g9[row] & BIT(column))
|
|
config |= (mode_5g9 & 0x3);
|
|
|
|
return config;
|
|
}
|
|
|
|
static u32
|
|
mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2)
|
|
{
|
|
struct mt792x_acpi_sar *sar = phy->acpisar;
|
|
struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3;
|
|
int col, row, i;
|
|
|
|
if (sar->ver != 3)
|
|
goto out;
|
|
|
|
if (!cl)
|
|
return MT792X_ACPI_MTCL_INVALID;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) {
|
|
col = 7 - i % 8;
|
|
row = i / 8;
|
|
if (!memcmp(cc_list_be[i], alpha2, 2))
|
|
return mt792x_acpi_get_mtcl_map_v3(row, col, cl);
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) {
|
|
if (!memcmp(cc_list_eu[i], alpha2, 2))
|
|
return mt792x_acpi_get_mtcl_map_v3(3, 7, cl);
|
|
}
|
|
|
|
out:
|
|
/* Depends on driver */
|
|
return 0x20;
|
|
}
|
|
|
|
static u32
|
|
mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2)
|
|
{
|
|
struct mt792x_acpi_sar *sar = phy->acpisar;
|
|
struct mt792x_asar_cl *cl = sar->countrylist;
|
|
int col, row, i;
|
|
|
|
if (!cl)
|
|
return MT792X_ACPI_MTCL_INVALID;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) {
|
|
col = 7 - i % 8;
|
|
row = i / 8;
|
|
if (!memcmp(cc_list_all[i], alpha2, 2))
|
|
return mt792x_acpi_get_mtcl_map(row, col, cl);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++)
|
|
if (!memcmp(cc_list_eu[i], alpha2, 2))
|
|
return mt792x_acpi_get_mtcl_map(0, 6, cl);
|
|
|
|
return mt792x_acpi_get_mtcl_map(0, 7, cl);
|
|
}
|
|
|
|
u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
|
|
{
|
|
struct mt792x_acpi_sar *sar = phy->acpisar;
|
|
u32 config = 0;
|
|
|
|
if (!sar)
|
|
return MT792X_ACPI_MTCL_INVALID;
|
|
|
|
config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2);
|
|
|
|
if (config == MT792X_ACPI_MTCL_INVALID)
|
|
return MT792X_ACPI_MTCL_INVALID;
|
|
|
|
config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2);
|
|
|
|
return config;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf);
|