Path: blob/main/sys/contrib/dev/mediatek/mt76/mt792x_acpi_sar.c
48378 views
// SPDX-License-Identifier: ISC1/* Copyright (C) 2023 MediaTek Inc. */23#include <linux/acpi.h>4#include "mt792x.h"56static const char * const cc_list_all[] = {7"00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR",8"CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME",9"MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR",10"TW", "TH", "UA", "GB", "US", "VN", "KH", "PY",11};1213static const char * const cc_list_eu[] = {14"AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK",15"EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE",16"IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL",17"NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE",18"CH",19};2021static const char * const cc_list_be[] = {22"AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU",23"RW", "VN", "KR", "UA", "", "", "", "",24"EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK",25"US",26};2728static int29mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len)30{31struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };32struct mt76_dev *mdev = &dev->mt76;33union acpi_object *sar_root;34acpi_handle root, handle;35acpi_status status;36u32 i = 0;37int ret;3839root = ACPI_HANDLE(mdev->dev);40if (!root)41return -EOPNOTSUPP;4243status = acpi_get_handle(root, method, &handle);44if (ACPI_FAILURE(status))45return -EIO;4647status = acpi_evaluate_object(handle, NULL, NULL, &buf);48if (ACPI_FAILURE(status))49return -EIO;5051sar_root = buf.pointer;52if (sar_root->type != ACPI_TYPE_PACKAGE ||53sar_root->package.count < 4 ||54sar_root->package.elements[0].type != ACPI_TYPE_INTEGER) {55dev_err(mdev->dev, "sar cnt = %d\n",56sar_root->package.count);57ret = -EINVAL;58goto free;59}6061if (!*tbl) {62*tbl = devm_kzalloc(mdev->dev, sar_root->package.count,63GFP_KERNEL);64if (!*tbl) {65ret = -ENOMEM;66goto free;67}68}6970if (len)71*len = sar_root->package.count;7273for (i = 0; i < sar_root->package.count; i++) {74union acpi_object *sar_unit = &sar_root->package.elements[i];7576if (sar_unit->type != ACPI_TYPE_INTEGER)77break;7879*(*tbl + i) = (u8)sar_unit->integer.value;80}8182ret = i == sar_root->package.count ? 0 : -EINVAL;83free:84kfree(sar_root);8586return ret;87}8889/* MTCL : Country List Table for 6G band */90/* MTCL : Country List Table for 6G band and 11BE */91static int92mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)93{94int len, ret;9596ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len);97if (ret)98return ret;99100if (len == sizeof(struct mt792x_asar_cl))101*version = ((struct mt792x_asar_cl *)*table)->version;102else if (len == sizeof(struct mt792x_asar_cl_v3))103*version = ((struct mt792x_asar_cl_v3 *)*table)->version;104else105return -EINVAL;106107return ret;108}109110/* MTDS : Dynamic SAR Power Table */111static int112mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)113{114int len, ret, sarlen, prelen, tblcnt;115bool enable;116117ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len);118if (ret)119return ret;120121/* Table content validation */122switch (version) {123case 1:124enable = ((struct mt792x_asar_dyn *)*table)->enable;125sarlen = sizeof(struct mt792x_asar_dyn_limit);126prelen = sizeof(struct mt792x_asar_dyn);127break;128case 2:129enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable;130sarlen = sizeof(struct mt792x_asar_dyn_limit_v2);131prelen = sizeof(struct mt792x_asar_dyn_v2);132break;133default:134return -EINVAL;135}136137tblcnt = (len - prelen) / sarlen;138if (!enable ||139tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN)140return -EINVAL;141142return 0;143}144145/* MTGS : Geo SAR Power Table */146static int147mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)148{149int len, ret, sarlen, prelen, tblcnt;150151ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len);152if (ret)153return ret;154155/* Table content validation */156switch (version) {157case 1:158sarlen = sizeof(struct mt792x_asar_geo_limit);159prelen = sizeof(struct mt792x_asar_geo);160break;161case 2:162sarlen = sizeof(struct mt792x_asar_geo_limit_v2);163prelen = sizeof(struct mt792x_asar_geo_v2);164break;165default:166return -EINVAL;167}168169tblcnt = (len - prelen) / sarlen;170if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO)171return -EINVAL;172173return 0;174}175176/* MTFG : Flag Table */177static int178mt792x_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table)179{180int len, ret;181182ret = mt792x_acpi_read(dev, MT792x_ACPI_MTFG, table, &len);183if (ret)184return ret;185186if (len < MT792x_ASAR_MIN_FG)187return -EINVAL;188189return 0;190}191192int mt792x_init_acpi_sar(struct mt792x_dev *dev)193{194struct mt792x_acpi_sar *asar;195int ret;196197asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL);198if (!asar)199return -ENOMEM;200201ret = mt792x_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver);202if (ret) {203devm_kfree(dev->mt76.dev, asar->countrylist);204asar->countrylist = NULL;205}206207ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);208if (ret) {209devm_kfree(dev->mt76.dev, asar->dyn);210asar->dyn = NULL;211}212213/* MTGS is optional */214ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver);215if (ret) {216devm_kfree(dev->mt76.dev, asar->geo);217asar->geo = NULL;218}219220/* MTFG is optional */221ret = mt792x_asar_acpi_read_mtfg(dev, (u8 **)&asar->fg);222if (ret) {223devm_kfree(dev->mt76.dev, asar->fg);224asar->fg = NULL;225}226dev->phy.acpisar = asar;227228return 0;229}230EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar);231232static s8233mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,234enum nl80211_band band, s8 dyn_power)235{236struct mt792x_acpi_sar *asar = phy->acpisar;237struct mt792x_asar_geo_band *band_pwr;238s8 geo_power;239u8 idx, max;240241if (!asar->geo)242return dyn_power;243244switch (phy->mt76->dev->region) {245case NL80211_DFS_FCC:246idx = 0;247break;248case NL80211_DFS_ETSI:249idx = 1;250break;251default: /* WW */252idx = 2;253break;254}255256if (asar->ver == 1) {257band_pwr = &asar->geo->tbl[idx].band[0];258max = ARRAY_SIZE(asar->geo->tbl[idx].band);259} else {260band_pwr = &asar->geo_v2->tbl[idx].band[0];261max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band);262}263264switch (band) {265case NL80211_BAND_2GHZ:266idx = 0;267break;268case NL80211_BAND_5GHZ:269idx = 1;270break;271case NL80211_BAND_6GHZ:272idx = 2;273break;274default:275return dyn_power;276}277278if (idx >= max)279return dyn_power;280281geo_power = (band_pwr + idx)->pwr;282dyn_power += (band_pwr + idx)->offset;283284return min(geo_power, dyn_power);285}286287static s8288mt792x_asar_range_pwr(struct mt792x_phy *phy,289const struct cfg80211_sar_freq_ranges *range,290u8 idx)291{292const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;293struct mt792x_acpi_sar *asar = phy->acpisar;294u8 *limit, band, max;295296if (!capa)297return 127;298299if (asar->ver == 1) {300limit = &asar->dyn->tbl[0].frp[0];301max = ARRAY_SIZE(asar->dyn->tbl[0].frp);302} else {303limit = &asar->dyn_v2->tbl[0].frp[0];304max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp);305}306307if (idx >= max)308return 127;309310if (range->start_freq >= 5945)311band = NL80211_BAND_6GHZ;312else if (range->start_freq >= 5150)313band = NL80211_BAND_5GHZ;314else315band = NL80211_BAND_2GHZ;316317return mt792x_asar_get_geo_pwr(phy, band, limit[idx]);318}319320int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)321{322const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;323int i;324325if (!phy->acpisar || !((struct mt792x_acpi_sar *)phy->acpisar)->dyn)326return 0;327328/* When ACPI SAR enabled in HW, we should apply rules for .frp329* 1. w/o .sar_specs : set ACPI SAR power as the defatul value330* 2. w/ .sar_specs : set power with min(.sar_specs, ACPI_SAR)331*/332for (i = 0; i < capa->num_freq_ranges; i++) {333struct mt76_freq_range_power *frp = &phy->mt76->frp[i];334335frp->range = set_default ? &capa->freq_ranges[i] : frp->range;336if (!frp->range)337continue;338339frp->power = min_t(s8, set_default ? 127 : frp->power,340mt792x_asar_range_pwr(phy, frp->range, i));341}342343return 0;344}345EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power);346347u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)348{349struct mt792x_acpi_sar *acpisar = phy->acpisar;350struct mt792x_asar_fg *fg;351struct {352u8 acpi_idx;353u8 chip_idx;354} map[] = {355{ 1, 1 },356{ 4, 2 },357};358u8 flags = BIT(0);359int i, j;360361if (!acpisar)362return 0;363364fg = acpisar->fg;365if (!fg)366return flags;367368/* pickup necessary settings per device and369* translate the index of bitmap for chip command.370*/371for (i = 0; i < fg->nr_flag; i++) {372for (j = 0; j < ARRAY_SIZE(map); j++) {373if (fg->flag[i] == map[j].acpi_idx) {374flags |= BIT(map[j].chip_idx);375break;376}377}378}379380return flags;381}382EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags);383384static u32385mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl)386{387u32 config = 0;388u8 mode_be = 0;389390mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be;391392if (cl->version > 2 && cl->clbe[row] & BIT(column))393config |= (mode_be & 0x3) << 4;394395return config;396}397398static u32399mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl)400{401u32 config = 0;402u8 mode_6g, mode_5g9;403404mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g;405mode_5g9 = (cl->mode_5g9 > 0x01) ? 0 : cl->mode_5g9;406407if ((cl->cl6g[row] & BIT(column)) || cl->mode_6g == 0x02)408config |= (mode_6g & 0x3) << 2;409if (cl->version > 1 && cl->cl5g9[row] & BIT(column))410config |= (mode_5g9 & 0x3);411412return config;413}414415static u32416mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2)417{418struct mt792x_acpi_sar *sar = phy->acpisar;419struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3;420int col, row, i;421422if (sar->ver != 3)423goto out;424425if (!cl)426return MT792X_ACPI_MTCL_INVALID;427428for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) {429col = 7 - i % 8;430row = i / 8;431if (!memcmp(cc_list_be[i], alpha2, 2))432return mt792x_acpi_get_mtcl_map_v3(row, col, cl);433}434for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) {435if (!memcmp(cc_list_eu[i], alpha2, 2))436return mt792x_acpi_get_mtcl_map_v3(3, 7, cl);437}438439out:440/* Depends on driver */441return 0x20;442}443444static u32445mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2)446{447struct mt792x_acpi_sar *sar = phy->acpisar;448struct mt792x_asar_cl *cl = sar->countrylist;449int col, row, i;450451if (!cl)452return MT792X_ACPI_MTCL_INVALID;453454for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) {455col = 7 - i % 8;456row = i / 8;457if (!memcmp(cc_list_all[i], alpha2, 2))458return mt792x_acpi_get_mtcl_map(row, col, cl);459}460461for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++)462if (!memcmp(cc_list_eu[i], alpha2, 2))463return mt792x_acpi_get_mtcl_map(0, 6, cl);464465return mt792x_acpi_get_mtcl_map(0, 7, cl);466}467468u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)469{470struct mt792x_acpi_sar *sar = phy->acpisar;471u32 config = 0;472473if (!sar)474return MT792X_ACPI_MTCL_INVALID;475476config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2);477478if (config == MT792X_ACPI_MTCL_INVALID)479return MT792X_ACPI_MTCL_INVALID;480481config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2);482483return config;484}485EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf);486487488