developer | 0f312e8 | 2022-11-01 12:31:52 +0800 | [diff] [blame^] | 1 | // SPDX-License-Identifier: ISC |
| 2 | /* Copyright (C) 2022 MediaTek Inc. */ |
| 3 | |
| 4 | #include <linux/acpi.h> |
| 5 | #include "mt7921.h" |
| 6 | |
| 7 | static int |
| 8 | mt7921_acpi_read(struct mt7921_dev *dev, u8 *method, u8 **tbl, u32 *len) |
| 9 | { |
| 10 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 11 | union acpi_object *sar_root, *sar_unit; |
| 12 | struct mt76_dev *mdev = &dev->mt76; |
| 13 | acpi_handle root, handle; |
| 14 | acpi_status status; |
| 15 | u32 i = 0; |
| 16 | int ret; |
| 17 | |
| 18 | root = ACPI_HANDLE(mdev->dev); |
| 19 | if (!root) |
| 20 | return -EOPNOTSUPP; |
| 21 | |
| 22 | status = acpi_get_handle(root, method, &handle); |
| 23 | if (ACPI_FAILURE(status)) |
| 24 | return -EIO; |
| 25 | |
| 26 | status = acpi_evaluate_object(handle, NULL, NULL, &buf); |
| 27 | if (ACPI_FAILURE(status)) |
| 28 | return -EIO; |
| 29 | |
| 30 | sar_root = buf.pointer; |
| 31 | if (sar_root->type != ACPI_TYPE_PACKAGE || |
| 32 | sar_root->package.count < 4 || |
| 33 | sar_root->package.elements[0].type != ACPI_TYPE_INTEGER) { |
| 34 | dev_err(mdev->dev, "sar cnt = %d\n", |
| 35 | sar_root->package.count); |
| 36 | goto free; |
| 37 | } |
| 38 | |
| 39 | if (!*tbl) { |
| 40 | *tbl = devm_kzalloc(mdev->dev, sar_root->package.count, |
| 41 | GFP_KERNEL); |
| 42 | if (!*tbl) |
| 43 | goto free; |
| 44 | } |
| 45 | if (len) |
| 46 | *len = sar_root->package.count; |
| 47 | |
| 48 | for (i = 0; i < sar_root->package.count; i++) { |
| 49 | sar_unit = &sar_root->package.elements[i]; |
| 50 | |
| 51 | if (sar_unit->type != ACPI_TYPE_INTEGER) |
| 52 | break; |
| 53 | *(*tbl + i) = (u8)sar_unit->integer.value; |
| 54 | } |
| 55 | free: |
| 56 | ret = (i == sar_root->package.count) ? 0 : -EINVAL; |
| 57 | |
| 58 | kfree(sar_root); |
| 59 | |
| 60 | return ret; |
| 61 | } |
| 62 | |
| 63 | /* MTCL : Country List Table for 6G band */ |
| 64 | static int |
| 65 | mt7921_asar_acpi_read_mtcl(struct mt7921_dev *dev, u8 **table, u8 *version) |
| 66 | { |
| 67 | *version = (mt7921_acpi_read(dev, MT7921_ACPI_MTCL, table, NULL) < 0) |
| 68 | ? 1 : 2; |
| 69 | return 0; |
| 70 | } |
| 71 | |
| 72 | /* MTDS : Dynamic SAR Power Table */ |
| 73 | static int |
| 74 | mt7921_asar_acpi_read_mtds(struct mt7921_dev *dev, u8 **table, u8 version) |
| 75 | { |
| 76 | int len, ret, sarlen, prelen, tblcnt; |
| 77 | bool enable; |
| 78 | |
| 79 | ret = mt7921_acpi_read(dev, MT7921_ACPI_MTDS, table, &len); |
| 80 | if (ret) |
| 81 | return ret; |
| 82 | |
| 83 | /* Table content validation */ |
| 84 | switch (version) { |
| 85 | case 1: |
| 86 | enable = ((struct mt7921_asar_dyn *)*table)->enable; |
| 87 | sarlen = sizeof(struct mt7921_asar_dyn_limit); |
| 88 | prelen = sizeof(struct mt7921_asar_dyn); |
| 89 | break; |
| 90 | case 2: |
| 91 | enable = ((struct mt7921_asar_dyn_v2 *)*table)->enable; |
| 92 | sarlen = sizeof(struct mt7921_asar_dyn_limit_v2); |
| 93 | prelen = sizeof(struct mt7921_asar_dyn_v2); |
| 94 | break; |
| 95 | default: |
| 96 | return -EINVAL; |
| 97 | } |
| 98 | |
| 99 | tblcnt = (len - prelen) / sarlen; |
| 100 | if (!enable || |
| 101 | tblcnt > MT7921_ASAR_MAX_DYN || tblcnt < MT7921_ASAR_MIN_DYN) |
| 102 | ret = -EINVAL; |
| 103 | |
| 104 | return ret; |
| 105 | } |
| 106 | |
| 107 | /* MTGS : Geo SAR Power Table */ |
| 108 | static int |
| 109 | mt7921_asar_acpi_read_mtgs(struct mt7921_dev *dev, u8 **table, u8 version) |
| 110 | { |
| 111 | int len, ret = 0, sarlen, prelen, tblcnt; |
| 112 | |
| 113 | ret = mt7921_acpi_read(dev, MT7921_ACPI_MTGS, table, &len); |
| 114 | if (ret) |
| 115 | return ret; |
| 116 | |
| 117 | /* Table content validation */ |
| 118 | switch (version) { |
| 119 | case 1: |
| 120 | sarlen = sizeof(struct mt7921_asar_geo_limit); |
| 121 | prelen = sizeof(struct mt7921_asar_geo); |
| 122 | break; |
| 123 | case 2: |
| 124 | sarlen = sizeof(struct mt7921_asar_geo_limit_v2); |
| 125 | prelen = sizeof(struct mt7921_asar_geo_v2); |
| 126 | break; |
| 127 | default: |
| 128 | return -EINVAL; |
| 129 | } |
| 130 | |
| 131 | tblcnt = (len - prelen) / sarlen; |
| 132 | if (tblcnt > MT7921_ASAR_MAX_GEO || tblcnt < MT7921_ASAR_MIN_GEO) |
| 133 | ret = -EINVAL; |
| 134 | |
| 135 | return ret; |
| 136 | } |
| 137 | |
| 138 | int mt7921_init_acpi_sar(struct mt7921_dev *dev) |
| 139 | { |
| 140 | struct mt7921_acpi_sar *asar; |
| 141 | int ret; |
| 142 | |
| 143 | asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL); |
| 144 | if (!asar) |
| 145 | return -ENOMEM; |
| 146 | |
| 147 | mt7921_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver); |
| 148 | |
| 149 | /* MTDS is mandatory. Return error if table is invalid */ |
| 150 | ret = mt7921_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver); |
| 151 | if (ret) { |
| 152 | devm_kfree(dev->mt76.dev, asar->dyn); |
| 153 | devm_kfree(dev->mt76.dev, asar->countrylist); |
| 154 | devm_kfree(dev->mt76.dev, asar); |
| 155 | return ret; |
| 156 | } |
| 157 | |
| 158 | /* MTGS is optional */ |
| 159 | ret = mt7921_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver); |
| 160 | if (ret) { |
| 161 | devm_kfree(dev->mt76.dev, asar->geo); |
| 162 | asar->geo = NULL; |
| 163 | } |
| 164 | |
| 165 | dev->phy.acpisar = asar; |
| 166 | |
| 167 | return 0; |
| 168 | } |
| 169 | |
| 170 | static s8 |
| 171 | mt7921_asar_get_geo_pwr(struct mt7921_phy *phy, |
| 172 | enum nl80211_band band, s8 dyn_power) |
| 173 | { |
| 174 | struct mt7921_acpi_sar *asar = phy->acpisar; |
| 175 | struct mt7921_asar_geo_band *band_pwr; |
| 176 | s8 geo_power; |
| 177 | u8 idx, max; |
| 178 | |
| 179 | if (!asar->geo) |
| 180 | return dyn_power; |
| 181 | |
| 182 | switch (phy->mt76->dev->region) { |
| 183 | case NL80211_DFS_FCC: |
| 184 | idx = 0; |
| 185 | break; |
| 186 | case NL80211_DFS_ETSI: |
| 187 | idx = 1; |
| 188 | break; |
| 189 | default: /* WW */ |
| 190 | idx = 2; |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | if (asar->ver == 1) { |
| 195 | band_pwr = &asar->geo->tbl[idx].band[0]; |
| 196 | max = ARRAY_SIZE(asar->geo->tbl[idx].band); |
| 197 | } else { |
| 198 | band_pwr = &asar->geo_v2->tbl[idx].band[0]; |
| 199 | max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band); |
| 200 | } |
| 201 | |
| 202 | switch (band) { |
| 203 | case NL80211_BAND_2GHZ: |
| 204 | idx = 0; |
| 205 | break; |
| 206 | case NL80211_BAND_5GHZ: |
| 207 | idx = 1; |
| 208 | break; |
| 209 | case NL80211_BAND_6GHZ: |
| 210 | idx = 2; |
| 211 | break; |
| 212 | default: |
| 213 | return dyn_power; |
| 214 | } |
| 215 | |
| 216 | if (idx >= max) |
| 217 | return dyn_power; |
| 218 | |
| 219 | geo_power = (band_pwr + idx)->pwr; |
| 220 | dyn_power += (band_pwr + idx)->offset; |
| 221 | |
| 222 | return min(geo_power, dyn_power); |
| 223 | } |
| 224 | |
| 225 | static s8 |
| 226 | mt7921_asar_range_pwr(struct mt7921_phy *phy, |
| 227 | const struct cfg80211_sar_freq_ranges *range, |
| 228 | u8 idx) |
| 229 | { |
| 230 | const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa; |
| 231 | struct mt7921_acpi_sar *asar = phy->acpisar; |
| 232 | u8 *limit, band, max; |
| 233 | |
| 234 | if (!capa) |
| 235 | return 127; |
| 236 | |
| 237 | if (asar->ver == 1) { |
| 238 | limit = &asar->dyn->tbl[0].frp[0]; |
| 239 | max = ARRAY_SIZE(asar->dyn->tbl[0].frp); |
| 240 | } else { |
| 241 | limit = &asar->dyn_v2->tbl[0].frp[0]; |
| 242 | max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp); |
| 243 | } |
| 244 | |
| 245 | if (idx >= max) |
| 246 | return 127; |
| 247 | |
| 248 | if (range->start_freq >= 5945) |
| 249 | band = NL80211_BAND_6GHZ; |
| 250 | else if (range->start_freq >= 5150) |
| 251 | band = NL80211_BAND_5GHZ; |
| 252 | else |
| 253 | band = NL80211_BAND_2GHZ; |
| 254 | |
| 255 | return mt7921_asar_get_geo_pwr(phy, band, limit[idx]); |
| 256 | } |
| 257 | |
| 258 | int mt7921_init_acpi_sar_power(struct mt7921_phy *phy, bool set_default) |
| 259 | { |
| 260 | const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa; |
| 261 | int i; |
| 262 | |
| 263 | if (!phy->acpisar) |
| 264 | return 0; |
| 265 | |
| 266 | /* When ACPI SAR enabled in HW, we should apply rules for .frp |
| 267 | * 1. w/o .sar_specs : set ACPI SAR power as the defatul value |
| 268 | * 2. w/ .sar_specs : set power with min(.sar_specs, ACPI_SAR) |
| 269 | */ |
| 270 | for (i = 0; i < capa->num_freq_ranges; i++) { |
| 271 | struct mt76_freq_range_power *frp = &phy->mt76->frp[i]; |
| 272 | |
| 273 | frp->range = set_default ? &capa->freq_ranges[i] : frp->range; |
| 274 | if (!frp->range) |
| 275 | continue; |
| 276 | |
| 277 | frp->power = min_t(s8, set_default ? 127 : frp->power, |
| 278 | mt7921_asar_range_pwr(phy, frp->range, i)); |
| 279 | } |
| 280 | |
| 281 | return 0; |
| 282 | } |