1 | // SPDX-License-Identifier: ISC |
2 | /* Copyright (C) 2023 MediaTek Inc. */ |
3 | |
4 | #include <linux/acpi.h> |
5 | #include "mt792x.h" |
6 | |
7 | static int |
8 | mt792x_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; |
62 | free: |
63 | kfree(objp: sar_root); |
64 | |
65 | return ret; |
66 | } |
67 | |
68 | /* MTCL : Country List Table for 6G band */ |
69 | static int |
70 | mt792x_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 */ |
81 | static int |
82 | mt792x_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 */ |
116 | static int |
117 | mt792x_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 */ |
147 | static int |
148 | mt792x_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 | |
162 | int 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 | } |
200 | EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar); |
201 | |
202 | static s8 |
203 | mt792x_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 | |
257 | static s8 |
258 | mt792x_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 | |
290 | int 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 | } |
315 | EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power); |
316 | |
317 | u8 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 | } |
352 | EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags); |
353 | |
354 | static u8 |
355 | mt792x_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 | |
371 | u8 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 | } |
409 | EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf); |
410 | |