1// SPDX-License-Identifier: ISC
2/* Copyright (C) 2023 MediaTek Inc. */
3
4#include <linux/acpi.h>
5#include "mt792x.h"
6
7static int
8mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len)
9{
10 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
11 struct mt76_dev *mdev = &dev->mt76;
12 union acpi_object *sar_root;
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(parent: root, pathname: method, ret_handle: &handle);
23 if (ACPI_FAILURE(status))
24 return -EIO;
25
26 status = acpi_evaluate_object(object: handle, NULL, NULL, return_object_buffer: &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 ret = -EINVAL;
37 goto free;
38 }
39
40 if (!*tbl) {
41 *tbl = devm_kzalloc(dev: mdev->dev, size: sar_root->package.count,
42 GFP_KERNEL);
43 if (!*tbl) {
44 ret = -ENOMEM;
45 goto free;
46 }
47 }
48
49 if (len)
50 *len = sar_root->package.count;
51
52 for (i = 0; i < sar_root->package.count; i++) {
53 union acpi_object *sar_unit = &sar_root->package.elements[i];
54
55 if (sar_unit->type != ACPI_TYPE_INTEGER)
56 break;
57
58 *(*tbl + i) = (u8)sar_unit->integer.value;
59 }
60
61 ret = i == sar_root->package.count ? 0 : -EINVAL;
62free:
63 kfree(objp: sar_root);
64
65 return ret;
66}
67
68/* MTCL : Country List Table for 6G band */
69static int
70mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
71{
72 int ret;
73
74 *version = ((ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, tbl: table, NULL)) < 0)
75 ? 1 : 2;
76
77 return ret;
78}
79
80/* MTDS : Dynamic SAR Power Table */
81static int
82mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)
83{
84 int len, ret, sarlen, prelen, tblcnt;
85 bool enable;
86
87 ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, tbl: table, len: &len);
88 if (ret)
89 return ret;
90
91 /* Table content validation */
92 switch (version) {
93 case 1:
94 enable = ((struct mt792x_asar_dyn *)*table)->enable;
95 sarlen = sizeof(struct mt792x_asar_dyn_limit);
96 prelen = sizeof(struct mt792x_asar_dyn);
97 break;
98 case 2:
99 enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable;
100 sarlen = sizeof(struct mt792x_asar_dyn_limit_v2);
101 prelen = sizeof(struct mt792x_asar_dyn_v2);
102 break;
103 default:
104 return -EINVAL;
105 }
106
107 tblcnt = (len - prelen) / sarlen;
108 if (!enable ||
109 tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN)
110 return -EINVAL;
111
112 return 0;
113}
114
115/* MTGS : Geo SAR Power Table */
116static int
117mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)
118{
119 int len, ret, sarlen, prelen, tblcnt;
120
121 ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, tbl: table, len: &len);
122 if (ret)
123 return ret;
124
125 /* Table content validation */
126 switch (version) {
127 case 1:
128 sarlen = sizeof(struct mt792x_asar_geo_limit);
129 prelen = sizeof(struct mt792x_asar_geo);
130 break;
131 case 2:
132 sarlen = sizeof(struct mt792x_asar_geo_limit_v2);
133 prelen = sizeof(struct mt792x_asar_geo_v2);
134 break;
135 default:
136 return -EINVAL;
137 }
138
139 tblcnt = (len - prelen) / sarlen;
140 if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO)
141 return -EINVAL;
142
143 return 0;
144}
145
146/* MTFG : Flag Table */
147static int
148mt792x_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table)
149{
150 int len, ret;
151
152 ret = mt792x_acpi_read(dev, MT792x_ACPI_MTFG, tbl: table, len: &len);
153 if (ret)
154 return ret;
155
156 if (len < MT792x_ASAR_MIN_FG)
157 return -EINVAL;
158
159 return 0;
160}
161
162int mt792x_init_acpi_sar(struct mt792x_dev *dev)
163{
164 struct mt792x_acpi_sar *asar;
165 int ret;
166
167 asar = devm_kzalloc(dev: dev->mt76.dev, size: sizeof(*asar), GFP_KERNEL);
168 if (!asar)
169 return -ENOMEM;
170
171 ret = mt792x_asar_acpi_read_mtcl(dev, table: (u8 **)&asar->countrylist, version: &asar->ver);
172 if (ret) {
173 devm_kfree(dev: dev->mt76.dev, p: asar->countrylist);
174 asar->countrylist = NULL;
175 }
176
177 ret = mt792x_asar_acpi_read_mtds(dev, table: (u8 **)&asar->dyn, version: asar->ver);
178 if (ret) {
179 devm_kfree(dev: dev->mt76.dev, p: asar->dyn);
180 asar->dyn = NULL;
181 }
182
183 /* MTGS is optional */
184 ret = mt792x_asar_acpi_read_mtgs(dev, table: (u8 **)&asar->geo, version: asar->ver);
185 if (ret) {
186 devm_kfree(dev: dev->mt76.dev, p: asar->geo);
187 asar->geo = NULL;
188 }
189
190 /* MTFG is optional */
191 ret = mt792x_asar_acpi_read_mtfg(dev, table: (u8 **)&asar->fg);
192 if (ret) {
193 devm_kfree(dev: dev->mt76.dev, p: asar->fg);
194 asar->fg = NULL;
195 }
196 dev->phy.acpisar = asar;
197
198 return 0;
199}
200EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar);
201
202static s8
203mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,
204 enum nl80211_band band, s8 dyn_power)
205{
206 struct mt792x_acpi_sar *asar = phy->acpisar;
207 struct mt792x_asar_geo_band *band_pwr;
208 s8 geo_power;
209 u8 idx, max;
210
211 if (!asar->geo)
212 return dyn_power;
213
214 switch (phy->mt76->dev->region) {
215 case NL80211_DFS_FCC:
216 idx = 0;
217 break;
218 case NL80211_DFS_ETSI:
219 idx = 1;
220 break;
221 default: /* WW */
222 idx = 2;
223 break;
224 }
225
226 if (asar->ver == 1) {
227 band_pwr = &asar->geo->tbl[idx].band[0];
228 max = ARRAY_SIZE(asar->geo->tbl[idx].band);
229 } else {
230 band_pwr = &asar->geo_v2->tbl[idx].band[0];
231 max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band);
232 }
233
234 switch (band) {
235 case NL80211_BAND_2GHZ:
236 idx = 0;
237 break;
238 case NL80211_BAND_5GHZ:
239 idx = 1;
240 break;
241 case NL80211_BAND_6GHZ:
242 idx = 2;
243 break;
244 default:
245 return dyn_power;
246 }
247
248 if (idx >= max)
249 return dyn_power;
250
251 geo_power = (band_pwr + idx)->pwr;
252 dyn_power += (band_pwr + idx)->offset;
253
254 return min(geo_power, dyn_power);
255}
256
257static s8
258mt792x_asar_range_pwr(struct mt792x_phy *phy,
259 const struct cfg80211_sar_freq_ranges *range,
260 u8 idx)
261{
262 const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
263 struct mt792x_acpi_sar *asar = phy->acpisar;
264 u8 *limit, band, max;
265
266 if (!capa)
267 return 127;
268
269 if (asar->ver == 1) {
270 limit = &asar->dyn->tbl[0].frp[0];
271 max = ARRAY_SIZE(asar->dyn->tbl[0].frp);
272 } else {
273 limit = &asar->dyn_v2->tbl[0].frp[0];
274 max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp);
275 }
276
277 if (idx >= max)
278 return 127;
279
280 if (range->start_freq >= 5945)
281 band = NL80211_BAND_6GHZ;
282 else if (range->start_freq >= 5150)
283 band = NL80211_BAND_5GHZ;
284 else
285 band = NL80211_BAND_2GHZ;
286
287 return mt792x_asar_get_geo_pwr(phy, band, dyn_power: limit[idx]);
288}
289
290int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)
291{
292 const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
293 int i;
294
295 if (!phy->acpisar || !((struct mt792x_acpi_sar *)phy->acpisar)->dyn)
296 return 0;
297
298 /* When ACPI SAR enabled in HW, we should apply rules for .frp
299 * 1. w/o .sar_specs : set ACPI SAR power as the defatul value
300 * 2. w/ .sar_specs : set power with min(.sar_specs, ACPI_SAR)
301 */
302 for (i = 0; i < capa->num_freq_ranges; i++) {
303 struct mt76_freq_range_power *frp = &phy->mt76->frp[i];
304
305 frp->range = set_default ? &capa->freq_ranges[i] : frp->range;
306 if (!frp->range)
307 continue;
308
309 frp->power = min_t(s8, set_default ? 127 : frp->power,
310 mt792x_asar_range_pwr(phy, frp->range, i));
311 }
312
313 return 0;
314}
315EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power);
316
317u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
318{
319 struct mt792x_acpi_sar *acpisar = phy->acpisar;
320 struct mt792x_asar_fg *fg;
321 struct {
322 u8 acpi_idx;
323 u8 chip_idx;
324 } map[] = {
325 { 1, 1 },
326 { 4, 2 },
327 };
328 u8 flags = BIT(0);
329 int i, j;
330
331 if (!acpisar)
332 return 0;
333
334 fg = acpisar->fg;
335 if (!fg)
336 return flags;
337
338 /* pickup necessary settings per device and
339 * translate the index of bitmap for chip command.
340 */
341 for (i = 0; i < fg->nr_flag; i++) {
342 for (j = 0; j < ARRAY_SIZE(map); j++) {
343 if (fg->flag[i] == map[j].acpi_idx) {
344 flags |= BIT(map[j].chip_idx);
345 break;
346 }
347 }
348 }
349
350 return flags;
351}
352EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags);
353
354static u8
355mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl)
356{
357 u8 config = 0;
358 u8 mode_6g, mode_5g9;
359
360 mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g;
361 mode_5g9 = (cl->mode_5g9 > 0x01) ? 0 : cl->mode_5g9;
362
363 if ((cl->cl6g[row] & BIT(column)) || cl->mode_6g == 0x02)
364 config |= (mode_6g & 0x3) << 2;
365 if (cl->version > 1 && cl->cl5g9[row] & BIT(column))
366 config |= (mode_5g9 & 0x3);
367
368 return config;
369}
370
371u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2)
372{
373 static const char * const cc_list_all[] = {
374 "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR",
375 "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME",
376 "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR",
377 "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY",
378 };
379 static const char * const cc_list_eu[] = {
380 "AT", "BE", "BG", "CY", "CZ", "HR", "DK", "EE",
381 "FI", "FR", "DE", "GR", "HU", "IS", "IE", "IT",
382 "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL",
383 "PT", "RO", "SK", "SI", "ES", "SE", "CH",
384 };
385 struct mt792x_acpi_sar *sar = phy->acpisar;
386 struct mt792x_asar_cl *cl;
387 int col, row, i;
388
389 if (!sar)
390 return 0xf;
391
392 cl = sar->countrylist;
393 if (!cl)
394 return 0xc;
395
396 for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) {
397 col = 7 - i % 8;
398 row = i / 8;
399 if (!memcmp(p: cc_list_all[i], q: alpha2, size: 2))
400 return mt792x_acpi_get_mtcl_map(row, column: col, cl);
401 }
402
403 for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++)
404 if (!memcmp(p: cc_list_eu[i], q: alpha2, size: 2))
405 return mt792x_acpi_get_mtcl_map(row: 0, column: 6, cl);
406
407 return mt792x_acpi_get_mtcl_map(row: 0, column: 7, cl);
408}
409EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf);
410

source code of linux/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c