wifi: mt76: mt7921: move acpi_sar code in mt792x-lib module
authorLorenzo Bianconi <lorenzo@kernel.org>
Wed, 28 Jun 2023 07:07:18 +0000 (15:07 +0800)
committerFelix Fietkau <nbd@nbd.name>
Wed, 26 Jul 2023 09:36:15 +0000 (11:36 +0200)
Move acpi_sar code in mt792x-lib module since it is shared between
mt7921 and mt7925 driver.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Deren Wu <deren.wu@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/Makefile
drivers/net/wireless/mediatek/mt76/mt7921/Makefile
drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.c [deleted file]
drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.h [deleted file]
drivers/net/wireless/mediatek/mt76/mt7921/init.c
drivers/net/wireless/mediatek/mt76/mt7921/main.c
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
drivers/net/wireless/mediatek/mt76/mt792x.h
drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c [new file with mode: 0644]
drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h [new file with mode: 0644]

index d6231948dd6eea062bf7c427532986f4b3fb0dc7..f8a1928d62b2611d9cc400ee0262d2d74707ff83 100644 (file)
@@ -33,6 +33,7 @@ mt76-connac-lib-y := mt76_connac_mcu.o mt76_connac_mac.o mt76_connac3_mac.o
 
 mt792x-lib-y := mt792x_core.o mt792x_mac.o mt792x_trace.o \
                mt792x_debugfs.o mt792x_dma.o
+mt792x-lib-$(CONFIG_ACPI) += mt792x_acpi_sar.o
 
 obj-$(CONFIG_MT76x0_COMMON) += mt76x0/
 obj-$(CONFIG_MT76x2_COMMON) += mt76x2/
index fd82dff76dae65bd602e624ccfd4d83bc3d47854..f380ec4b6de16e264f6b20cf0f914c029c250008 100644 (file)
@@ -7,7 +7,6 @@ obj-$(CONFIG_MT7921U) += mt7921u.o
 
 mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o
 mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
-mt7921-common-$(CONFIG_ACPI) += acpi_sar.o
 mt7921e-y := pci.o pci_mac.o pci_mcu.o dma.o
 mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o
 mt7921u-y := usb.o usb_mac.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.c
deleted file mode 100644 (file)
index 057767a..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-// SPDX-License-Identifier: ISC
-/* Copyright (C) 2022 MediaTek Inc. */
-
-#include <linux/acpi.h>
-#include "mt7921.h"
-
-static int
-mt7921_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len)
-{
-       struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *sar_root, *sar_unit;
-       struct mt76_dev *mdev = &dev->mt76;
-       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++) {
-               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 */
-static int
-mt7921_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
-{
-       *version = (mt7921_acpi_read(dev, MT7921_ACPI_MTCL, table, NULL) < 0)
-                  ? 1 : 2;
-       return 0;
-}
-
-/* MTDS : Dynamic SAR Power Table */
-static int
-mt7921_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)
-{
-       int len, ret, sarlen, prelen, tblcnt;
-       bool enable;
-
-       ret = mt7921_acpi_read(dev, MT7921_ACPI_MTDS, table, &len);
-       if (ret)
-               return ret;
-
-       /* Table content validation */
-       switch (version) {
-       case 1:
-               enable = ((struct mt7921_asar_dyn *)*table)->enable;
-               sarlen = sizeof(struct mt7921_asar_dyn_limit);
-               prelen = sizeof(struct mt7921_asar_dyn);
-               break;
-       case 2:
-               enable = ((struct mt7921_asar_dyn_v2 *)*table)->enable;
-               sarlen = sizeof(struct mt7921_asar_dyn_limit_v2);
-               prelen = sizeof(struct mt7921_asar_dyn_v2);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       tblcnt = (len - prelen) / sarlen;
-       if (!enable ||
-           tblcnt > MT7921_ASAR_MAX_DYN || tblcnt < MT7921_ASAR_MIN_DYN)
-               ret = -EINVAL;
-
-       return ret;
-}
-
-/* MTGS : Geo SAR Power Table */
-static int
-mt7921_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)
-{
-       int len, ret = 0, sarlen, prelen, tblcnt;
-
-       ret = mt7921_acpi_read(dev, MT7921_ACPI_MTGS, table, &len);
-       if (ret)
-               return ret;
-
-       /* Table content validation */
-       switch (version) {
-       case 1:
-               sarlen = sizeof(struct mt7921_asar_geo_limit);
-               prelen = sizeof(struct mt7921_asar_geo);
-               break;
-       case 2:
-               sarlen = sizeof(struct mt7921_asar_geo_limit_v2);
-               prelen = sizeof(struct mt7921_asar_geo_v2);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       tblcnt = (len - prelen) / sarlen;
-       if (tblcnt > MT7921_ASAR_MAX_GEO || tblcnt < MT7921_ASAR_MIN_GEO)
-               ret = -EINVAL;
-
-       return ret;
-}
-
-/* MTFG : Flag Table */
-static int
-mt7921_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table)
-{
-       int len, ret;
-
-       ret = mt7921_acpi_read(dev, MT7921_ACPI_MTFG, table, &len);
-       if (ret)
-               return ret;
-
-       if (len < MT7921_ASAR_MIN_FG)
-               ret = -EINVAL;
-
-       return ret;
-}
-
-int mt7921_init_acpi_sar(struct mt792x_dev *dev)
-{
-       struct mt7921_acpi_sar *asar;
-       int ret;
-
-       asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL);
-       if (!asar)
-               return -ENOMEM;
-
-       mt7921_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver);
-
-       /* MTDS is mandatory. Return error if table is invalid */
-       ret = mt7921_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);
-       if (ret) {
-               devm_kfree(dev->mt76.dev, asar->dyn);
-               devm_kfree(dev->mt76.dev, asar->countrylist);
-               devm_kfree(dev->mt76.dev, asar);
-               return ret;
-       }
-
-       /* MTGS is optional */
-       ret = mt7921_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 = mt7921_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;
-}
-
-static s8
-mt7921_asar_get_geo_pwr(struct mt792x_phy *phy,
-                       enum nl80211_band band, s8 dyn_power)
-{
-       struct mt7921_acpi_sar *asar = phy->acpisar;
-       struct mt7921_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
-mt7921_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 mt7921_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 mt7921_asar_get_geo_pwr(phy, band, limit[idx]);
-}
-
-int mt7921_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)
-               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,
-                                  mt7921_asar_range_pwr(phy, frp->range, i));
-       }
-
-       return 0;
-}
-
-u8 mt7921_acpi_get_flags(struct mt792x_phy *phy)
-{
-       struct mt7921_acpi_sar *acpisar = phy->acpisar;
-       struct mt7921_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;
-}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt7921/acpi_sar.h
deleted file mode 100644 (file)
index 6f2c4a5..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* SPDX-License-Identifier: ISC */
-/* Copyright (C) 2022 MediaTek Inc. */
-
-#ifndef __MT7921_ACPI_SAR_H
-#define __MT7921_ACPI_SAR_H
-
-#define MT7921_ASAR_MIN_DYN            1
-#define MT7921_ASAR_MAX_DYN            8
-#define MT7921_ASAR_MIN_GEO            3
-#define MT7921_ASAR_MAX_GEO            8
-#define MT7921_ASAR_MIN_FG             8
-
-#define MT7921_ACPI_MTCL               "MTCL"
-#define MT7921_ACPI_MTDS               "MTDS"
-#define MT7921_ACPI_MTGS               "MTGS"
-#define MT7921_ACPI_MTFG               "MTFG"
-
-struct mt7921_asar_dyn_limit {
-       u8 idx;
-       u8 frp[5];
-} __packed;
-
-struct mt7921_asar_dyn {
-       u8 names[4];
-       u8 enable;
-       u8 nr_tbl;
-       DECLARE_FLEX_ARRAY(struct mt7921_asar_dyn_limit, tbl);
-} __packed;
-
-struct mt7921_asar_dyn_limit_v2 {
-       u8 idx;
-       u8 frp[11];
-} __packed;
-
-struct mt7921_asar_dyn_v2 {
-       u8 names[4];
-       u8 enable;
-       u8 rsvd;
-       u8 nr_tbl;
-       DECLARE_FLEX_ARRAY(struct mt7921_asar_dyn_limit_v2, tbl);
-} __packed;
-
-struct mt7921_asar_geo_band {
-       u8 pwr;
-       u8 offset;
-} __packed;
-
-struct mt7921_asar_geo_limit {
-       u8 idx;
-       /* 0:2G, 1:5G */
-       struct mt7921_asar_geo_band band[2];
-} __packed;
-
-struct mt7921_asar_geo {
-       u8 names[4];
-       u8 version;
-       u8 nr_tbl;
-       DECLARE_FLEX_ARRAY(struct mt7921_asar_geo_limit, tbl);
-} __packed;
-
-struct mt7921_asar_geo_limit_v2 {
-       u8 idx;
-       /* 0:2G, 1:5G, 2:6G */
-       struct mt7921_asar_geo_band band[3];
-} __packed;
-
-struct mt7921_asar_geo_v2 {
-       u8 names[4];
-       u8 version;
-       u8 rsvd;
-       u8 nr_tbl;
-       DECLARE_FLEX_ARRAY(struct mt7921_asar_geo_limit_v2, tbl);
-} __packed;
-
-struct mt7921_asar_cl {
-       u8 names[4];
-       u8 version;
-       u8 mode_6g;
-       u8 cl6g[6];
-} __packed;
-
-struct mt7921_asar_fg {
-       u8 names[4];
-       u8 version;
-       u8 rsvd;
-       u8 nr_flag;
-       u8 rsvd1;
-       u8 flag[];
-} __packed;
-
-struct mt7921_acpi_sar {
-       u8 ver;
-       union {
-               struct mt7921_asar_dyn *dyn;
-               struct mt7921_asar_dyn_v2 *dyn_v2;
-       };
-       union {
-               struct mt7921_asar_geo *geo;
-               struct mt7921_asar_geo_v2 *geo_v2;
-       };
-       struct mt7921_asar_cl *countrylist;
-       struct mt7921_asar_fg *fg;
-};
-
-#endif
index 3ff0205919c292e307251e3c54e556badf095877..7b8876bf8fc8c6c1f7796faa0283ed79b06fcba1 100644 (file)
@@ -230,7 +230,7 @@ int mt7921_register_device(struct mt792x_dev *dev)
        if (!mt76_is_mmio(&dev->mt76))
                hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE;
 
-       mt7921_init_acpi_sar(dev);
+       mt792x_init_acpi_sar(dev);
 
        ret = mt792x_init_wcid(dev);
        if (ret)
index 73f29fed216fe3576d7b48d32051702bcbe2a4d8..0844d28b3223dabed3ffe3dec15f62327851ab62 100644 (file)
@@ -1153,19 +1153,16 @@ int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw,
                          const struct cfg80211_sar_specs *sar)
 {
        struct mt76_phy *mphy = hw->priv;
-       int err;
 
        if (sar) {
-               err = mt76_init_sar_power(hw, sar);
+               int err = mt76_init_sar_power(hw, sar);
+
                if (err)
                        return err;
        }
+       mt792x_init_acpi_sar_power(mt792x_hw_phy(hw), !sar);
 
-       mt7921_init_acpi_sar_power(mt792x_hw_phy(hw), !sar);
-
-       err = mt76_connac_mcu_set_rate_txpower(mphy);
-
-       return err;
+       return mt76_connac_mcu_set_rate_txpower(mphy);
 }
 
 static int mt7921_set_sar_specs(struct ieee80211_hw *hw,
index bd40ca489447305f509683259cc68a28ba79f9be..e9caf750bca550774fcb2f040a48130c4d0482d1 100644 (file)
@@ -1197,7 +1197,7 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
        } __packed req = {
                .idx = idx,
                .env = env_cap,
-               .acpi_conf = mt7921_acpi_get_flags(&dev->phy),
+               .acpi_conf = mt792x_acpi_get_flags(&dev->phy),
        };
        int ret, valid_cnt = 0;
        u8 i, *pos;
index 0c60a15590417be9c6a07babcca6aff607acbc45..3ba873ec6bc47bc92f863eb59e2bf2e889e61575 100644 (file)
@@ -6,7 +6,6 @@
 
 #include "../mt792x.h"
 #include "regs.h"
-#include "acpi_sar.h"
 
 #define MT7921_PM_TIMEOUT              (HZ / 12)
 #define MT7921_HW_SCAN_TIMEOUT         (HZ / 10)
@@ -345,29 +344,6 @@ int mt7921_mcu_uni_add_beacon_offload(struct mt792x_dev *dev,
                                      struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif,
                                      bool enable);
-#ifdef CONFIG_ACPI
-int mt7921_init_acpi_sar(struct mt792x_dev *dev);
-int mt7921_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default);
-u8 mt7921_acpi_get_flags(struct mt792x_phy *phy);
-#else
-static inline int
-mt7921_init_acpi_sar(struct mt792x_dev *dev)
-{
-       return 0;
-}
-
-static inline int
-mt7921_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)
-{
-       return 0;
-}
-
-static inline u8
-mt7921_acpi_get_flags(struct mt792x_phy *phy)
-{
-       return 0;
-}
-#endif
 int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw,
                          const struct cfg80211_sar_specs *sar);
 
index 54ff9627530f0937aba8b10febed6a32ddcd5003..1ed688186fe74be7bcb9518d577a8bbe517eda6b 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "mt76_connac_mcu.h"
 #include "mt792x_regs.h"
+#include "mt792x_acpi_sar.h"
 
 #define MT792x_MAX_INTERFACES  4
 #define MT792x_WTBL_SIZE       20
@@ -297,4 +298,26 @@ int __mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev);
 int mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev);
 int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev);
 
+#ifdef CONFIG_ACPI
+int mt792x_init_acpi_sar(struct mt792x_dev *dev);
+int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default);
+u8 mt792x_acpi_get_flags(struct mt792x_phy *phy);
+#else
+static inline int mt792x_init_acpi_sar(struct mt792x_dev *dev)
+{
+       return 0;
+}
+
+static inline int mt792x_init_acpi_sar_power(struct mt792x_phy *phy,
+                                            bool set_default)
+{
+       return 0;
+}
+
+static inline u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
+{
+       return 0;
+}
+#endif
+
 #endif /* __MT7925_H */
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
new file mode 100644 (file)
index 0000000..303c0f5
--- /dev/null
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#include <linux/acpi.h>
+#include "mt792x.h"
+
+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 */
+static void
+mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
+{
+       if (mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, NULL) < 0)
+               *version = 1;
+       else
+               *version = 2;
+}
+
+/* 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;
+
+       mt792x_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver);
+
+       /* MTDS is mandatory. Return error if table is invalid */
+       ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);
+       if (ret) {
+               devm_kfree(dev->mt76.dev, asar->dyn);
+               devm_kfree(dev->mt76.dev, asar->countrylist);
+               devm_kfree(dev->mt76.dev, asar);
+
+               return ret;
+       }
+
+       /* 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)
+               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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
new file mode 100644 (file)
index 0000000..d6d332e
--- /dev/null
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2023 MediaTek Inc. */
+
+#ifndef __MT7921_ACPI_SAR_H
+#define __MT7921_ACPI_SAR_H
+
+#define MT792x_ASAR_MIN_DYN            1
+#define MT792x_ASAR_MAX_DYN            8
+#define MT792x_ASAR_MIN_GEO            3
+#define MT792x_ASAR_MAX_GEO            8
+#define MT792x_ASAR_MIN_FG             8
+
+#define MT792x_ACPI_MTCL               "MTCL"
+#define MT792x_ACPI_MTDS               "MTDS"
+#define MT792x_ACPI_MTGS               "MTGS"
+#define MT792x_ACPI_MTFG               "MTFG"
+
+struct mt792x_asar_dyn_limit {
+       u8 idx;
+       u8 frp[5];
+} __packed;
+
+struct mt792x_asar_dyn {
+       u8 names[4];
+       u8 enable;
+       u8 nr_tbl;
+       DECLARE_FLEX_ARRAY(struct mt792x_asar_dyn_limit, tbl);
+} __packed;
+
+struct mt792x_asar_dyn_limit_v2 {
+       u8 idx;
+       u8 frp[11];
+} __packed;
+
+struct mt792x_asar_dyn_v2 {
+       u8 names[4];
+       u8 enable;
+       u8 rsvd;
+       u8 nr_tbl;
+       DECLARE_FLEX_ARRAY(struct mt792x_asar_dyn_limit_v2, tbl);
+} __packed;
+
+struct mt792x_asar_geo_band {
+       u8 pwr;
+       u8 offset;
+} __packed;
+
+struct mt792x_asar_geo_limit {
+       u8 idx;
+       /* 0:2G, 1:5G */
+       struct mt792x_asar_geo_band band[2];
+} __packed;
+
+struct mt792x_asar_geo {
+       u8 names[4];
+       u8 version;
+       u8 nr_tbl;
+       DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit, tbl);
+} __packed;
+
+struct mt792x_asar_geo_limit_v2 {
+       u8 idx;
+       /* 0:2G, 1:5G, 2:6G */
+       struct mt792x_asar_geo_band band[3];
+} __packed;
+
+struct mt792x_asar_geo_v2 {
+       u8 names[4];
+       u8 version;
+       u8 rsvd;
+       u8 nr_tbl;
+       DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit_v2, tbl);
+} __packed;
+
+struct mt792x_asar_cl {
+       u8 names[4];
+       u8 version;
+       u8 mode_6g;
+       u8 cl6g[6];
+} __packed;
+
+struct mt792x_asar_fg {
+       u8 names[4];
+       u8 version;
+       u8 rsvd;
+       u8 nr_flag;
+       u8 rsvd1;
+       u8 flag[];
+} __packed;
+
+struct mt792x_acpi_sar {
+       u8 ver;
+       union {
+               struct mt792x_asar_dyn *dyn;
+               struct mt792x_asar_dyn_v2 *dyn_v2;
+       };
+       union {
+               struct mt792x_asar_geo *geo;
+               struct mt792x_asar_geo_v2 *geo_v2;
+       };
+       struct mt792x_asar_cl *countrylist;
+       struct mt792x_asar_fg *fg;
+};
+
+#endif