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