File Index
Files and directories
Sign in
FILE

mt792x_acpi_sar.c

File preview with syntax highlighting.

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