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(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		ret = -EINVAL;
37		goto free;
38	}
39
40	if (!*tbl) {
41		*tbl = devm_kzalloc(mdev->dev, 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(sar_root);
64
65	return ret;
66}
67
68/* MTCL : Country List Table for 6G band */
69static void
70mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version)
71{
72	if (mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, NULL) < 0)
73		*version = 1;
74	else
75		*version = 2;
76}
77
78/* MTDS : Dynamic SAR Power Table */
79static int
80mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version)
81{
82	int len, ret, sarlen, prelen, tblcnt;
83	bool enable;
84
85	ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len);
86	if (ret)
87		return ret;
88
89	/* Table content validation */
90	switch (version) {
91	case 1:
92		enable = ((struct mt792x_asar_dyn *)*table)->enable;
93		sarlen = sizeof(struct mt792x_asar_dyn_limit);
94		prelen = sizeof(struct mt792x_asar_dyn);
95		break;
96	case 2:
97		enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable;
98		sarlen = sizeof(struct mt792x_asar_dyn_limit_v2);
99		prelen = sizeof(struct mt792x_asar_dyn_v2);
100		break;
101	default:
102		return -EINVAL;
103	}
104
105	tblcnt = (len - prelen) / sarlen;
106	if (!enable ||
107	    tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN)
108		return -EINVAL;
109
110	return 0;
111}
112
113/* MTGS : Geo SAR Power Table */
114static int
115mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version)
116{
117	int len, ret, sarlen, prelen, tblcnt;
118
119	ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len);
120	if (ret)
121		return ret;
122
123	/* Table content validation */
124	switch (version) {
125	case 1:
126		sarlen = sizeof(struct mt792x_asar_geo_limit);
127		prelen = sizeof(struct mt792x_asar_geo);
128		break;
129	case 2:
130		sarlen = sizeof(struct mt792x_asar_geo_limit_v2);
131		prelen = sizeof(struct mt792x_asar_geo_v2);
132		break;
133	default:
134		return -EINVAL;
135	}
136
137	tblcnt = (len - prelen) / sarlen;
138	if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO)
139		return -EINVAL;
140
141	return 0;
142}
143
144/* MTFG : Flag Table */
145static int
146mt792x_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table)
147{
148	int len, ret;
149
150	ret = mt792x_acpi_read(dev, MT792x_ACPI_MTFG, table, &len);
151	if (ret)
152		return ret;
153
154	if (len < MT792x_ASAR_MIN_FG)
155		return -EINVAL;
156
157	return 0;
158}
159
160int mt792x_init_acpi_sar(struct mt792x_dev *dev)
161{
162	struct mt792x_acpi_sar *asar;
163	int ret;
164
165	asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL);
166	if (!asar)
167		return -ENOMEM;
168
169	mt792x_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver);
170
171	/* MTDS is mandatory. Return error if table is invalid */
172	ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver);
173	if (ret) {
174		devm_kfree(dev->mt76.dev, asar->dyn);
175		devm_kfree(dev->mt76.dev, asar->countrylist);
176		devm_kfree(dev->mt76.dev, asar);
177
178		return ret;
179	}
180
181	/* MTGS is optional */
182	ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver);
183	if (ret) {
184		devm_kfree(dev->mt76.dev, asar->geo);
185		asar->geo = NULL;
186	}
187
188	/* MTFG is optional */
189	ret = mt792x_asar_acpi_read_mtfg(dev, (u8 **)&asar->fg);
190	if (ret) {
191		devm_kfree(dev->mt76.dev, asar->fg);
192		asar->fg = NULL;
193	}
194	dev->phy.acpisar = asar;
195
196	return 0;
197}
198EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar);
199
200static s8
201mt792x_asar_get_geo_pwr(struct mt792x_phy *phy,
202			enum nl80211_band band, s8 dyn_power)
203{
204	struct mt792x_acpi_sar *asar = phy->acpisar;
205	struct mt792x_asar_geo_band *band_pwr;
206	s8 geo_power;
207	u8 idx, max;
208
209	if (!asar->geo)
210		return dyn_power;
211
212	switch (phy->mt76->dev->region) {
213	case NL80211_DFS_FCC:
214		idx = 0;
215		break;
216	case NL80211_DFS_ETSI:
217		idx = 1;
218		break;
219	default: /* WW */
220		idx = 2;
221		break;
222	}
223
224	if (asar->ver == 1) {
225		band_pwr = &asar->geo->tbl[idx].band[0];
226		max = ARRAY_SIZE(asar->geo->tbl[idx].band);
227	} else {
228		band_pwr = &asar->geo_v2->tbl[idx].band[0];
229		max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band);
230	}
231
232	switch (band) {
233	case NL80211_BAND_2GHZ:
234		idx = 0;
235		break;
236	case NL80211_BAND_5GHZ:
237		idx = 1;
238		break;
239	case NL80211_BAND_6GHZ:
240		idx = 2;
241		break;
242	default:
243		return dyn_power;
244	}
245
246	if (idx >= max)
247		return dyn_power;
248
249	geo_power = (band_pwr + idx)->pwr;
250	dyn_power += (band_pwr + idx)->offset;
251
252	return min(geo_power, dyn_power);
253}
254
255static s8
256mt792x_asar_range_pwr(struct mt792x_phy *phy,
257		      const struct cfg80211_sar_freq_ranges *range,
258		      u8 idx)
259{
260	const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
261	struct mt792x_acpi_sar *asar = phy->acpisar;
262	u8 *limit, band, max;
263
264	if (!capa)
265		return 127;
266
267	if (asar->ver == 1) {
268		limit = &asar->dyn->tbl[0].frp[0];
269		max = ARRAY_SIZE(asar->dyn->tbl[0].frp);
270	} else {
271		limit = &asar->dyn_v2->tbl[0].frp[0];
272		max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp);
273	}
274
275	if (idx >= max)
276		return 127;
277
278	if (range->start_freq >= 5945)
279		band = NL80211_BAND_6GHZ;
280	else if (range->start_freq >= 5150)
281		band = NL80211_BAND_5GHZ;
282	else
283		band = NL80211_BAND_2GHZ;
284
285	return mt792x_asar_get_geo_pwr(phy, band, limit[idx]);
286}
287
288int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default)
289{
290	const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa;
291	int i;
292
293	if (!phy->acpisar)
294		return 0;
295
296	/* When ACPI SAR enabled in HW, we should apply rules for .frp
297	 * 1. w/o .sar_specs : set ACPI SAR power as the defatul value
298	 * 2. w/  .sar_specs : set power with min(.sar_specs, ACPI_SAR)
299	 */
300	for (i = 0; i < capa->num_freq_ranges; i++) {
301		struct mt76_freq_range_power *frp = &phy->mt76->frp[i];
302
303		frp->range = set_default ? &capa->freq_ranges[i] : frp->range;
304		if (!frp->range)
305			continue;
306
307		frp->power = min_t(s8, set_default ? 127 : frp->power,
308				   mt792x_asar_range_pwr(phy, frp->range, i));
309	}
310
311	return 0;
312}
313EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power);
314
315u8 mt792x_acpi_get_flags(struct mt792x_phy *phy)
316{
317	struct mt792x_acpi_sar *acpisar = phy->acpisar;
318	struct mt792x_asar_fg *fg;
319	struct {
320		u8 acpi_idx;
321		u8 chip_idx;
322	} map[] = {
323		{ 1, 1 },
324		{ 4, 2 },
325	};
326	u8 flags = BIT(0);
327	int i, j;
328
329	if (!acpisar)
330		return 0;
331
332	fg = acpisar->fg;
333	if (!fg)
334		return flags;
335
336	/* pickup necessary settings per device and
337	 * translate the index of bitmap for chip command.
338	 */
339	for (i = 0; i < fg->nr_flag; i++) {
340		for (j = 0; j < ARRAY_SIZE(map); j++) {
341			if (fg->flag[i] == map[j].acpi_idx) {
342				flags |= BIT(map[j].chip_idx);
343				break;
344			}
345		}
346	}
347
348	return flags;
349}
350EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags);
351