1// SPDX-License-Identifier: ISC
2/*
3 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
4 */
5#if defined(CONFIG_OF) && defined(CONFIG_MTD)
6#include <linux/of.h>
7#include <linux/of_net.h>
8#include <linux/mtd/mtd.h>
9#include <linux/mtd/partitions.h>
10#include <linux/nvmem-consumer.h>
11#endif
12#include <linux/etherdevice.h>
13#include "mt76.h"
14
15#if defined(CONFIG_OF)
16static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len)
17{
18	struct device_node *np = dev->dev->of_node;
19	const void *data;
20	int size;
21
22	data = of_get_property(np, "mediatek,eeprom-data", &size);
23	if (!data)
24		return -ENOENT;
25
26	if (size > len)
27		return -EINVAL;
28
29	memcpy(eep, data, size);
30
31	return 0;
32}
33#endif
34
35#if defined(CONFIG_MTD) && defined(CONFIG_OF)
36static int mt76_get_of_epprom_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len)
37{
38	struct device_node *np = dev->dev->of_node;
39	struct mtd_info *mtd;
40	const __be32 *list;
41	const char *part;
42	phandle phandle;
43	size_t retlen;
44	int size;
45	int ret;
46
47	list = of_get_property(np, "mediatek,mtd-eeprom", &size);
48	if (!list)
49		return -ENOENT;
50
51	phandle = be32_to_cpup(list++);
52	if (!phandle)
53		return -ENOENT;
54
55	np = of_find_node_by_phandle(phandle);
56	if (!np)
57		return -EINVAL;
58
59	part = of_get_property(np, "label", NULL);
60	if (!part)
61		part = np->name;
62
63	mtd = get_mtd_device_nm(part);
64	if (IS_ERR(mtd)) {
65		ret =  PTR_ERR(mtd);
66		goto out_put_node;
67	}
68
69	if (size <= sizeof(*list)) {
70		ret = -EINVAL;
71		goto out_put_node;
72	}
73
74	offset = be32_to_cpup(list);
75	ret = mtd_read(mtd, offset, len, &retlen, eep);
76	put_mtd_device(mtd);
77	if (mtd_is_bitflip(ret))
78		ret = 0;
79	if (ret) {
80		dev_err(dev->dev, "reading EEPROM from mtd %s failed: %i\n",
81			part, ret);
82		goto out_put_node;
83	}
84
85	if (retlen < len) {
86		ret = -EINVAL;
87		goto out_put_node;
88	}
89
90	if (of_property_read_bool(dev->dev->of_node, "big-endian")) {
91		u8 *data = (u8 *)eep;
92		int i;
93
94		/* convert eeprom data in Little Endian */
95		for (i = 0; i < round_down(len, 2); i += 2)
96			put_unaligned_le16(get_unaligned_be16(&data[i]),
97					   &data[i]);
98	}
99
100#ifdef CONFIG_NL80211_TESTMODE
101	dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
102	dev->test_mtd.offset = offset;
103#endif
104
105out_put_node:
106	of_node_put(np);
107	return ret;
108}
109#endif
110
111#if defined(CONFIG_OF)
112static int mt76_get_of_epprom_from_nvmem(struct mt76_dev *dev, void *eep, int len)
113{
114	struct device_node *np = dev->dev->of_node;
115	struct nvmem_cell *cell;
116	const void *data;
117	size_t retlen;
118	int ret = 0;
119
120	cell = of_nvmem_cell_get(np, "eeprom");
121	if (IS_ERR(cell))
122		return PTR_ERR(cell);
123
124	data = nvmem_cell_read(cell, &retlen);
125	nvmem_cell_put(cell);
126
127	if (IS_ERR(data))
128		return PTR_ERR(data);
129
130	if (retlen < len) {
131		ret = -EINVAL;
132		goto exit;
133	}
134
135	memcpy(eep, data, len);
136
137exit:
138	kfree(data);
139
140	return ret;
141}
142#endif
143
144int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int offset, int len)
145{
146#if defined(CONFIG_MTD) && defined(CONFIG_OF)
147	struct device_node *np = dev->dev->of_node;
148	int ret;
149
150	if (!np)
151		return -ENOENT;
152
153	ret = mt76_get_of_eeprom_data(dev, eep, len);
154	if (!ret)
155		return 0;
156
157	ret = mt76_get_of_epprom_from_mtd(dev, eep, offset, len);
158	if (!ret)
159		return 0;
160
161	return mt76_get_of_epprom_from_nvmem(dev, eep, len);
162#else
163	return -ENOENT;
164#endif
165}
166EXPORT_SYMBOL_GPL(mt76_get_of_eeprom);
167
168void
169mt76_eeprom_override(struct mt76_phy *phy)
170{
171	struct mt76_dev *dev = phy->dev;
172#if defined(CONFIG_OF)
173	struct device_node *np = dev->dev->of_node;
174
175	of_get_mac_address(np, phy->macaddr);
176
177	if (!is_valid_ether_addr(phy->macaddr)) {
178#endif
179		eth_random_addr(phy->macaddr);
180		dev_info(dev->dev,
181			 "Invalid MAC address, using random address %pM\n",
182			 phy->macaddr);
183#if defined(CONFIG_OF)
184	}
185#endif
186}
187EXPORT_SYMBOL_GPL(mt76_eeprom_override);
188
189#if defined(CONFIG_OF)
190static bool mt76_string_prop_find(struct property *prop, const char *str)
191{
192	const char *cp = NULL;
193
194	if (!prop || !str || !str[0])
195		return false;
196
197	while ((cp = of_prop_next_string(prop, cp)) != NULL)
198		if (!strcasecmp(cp, str))
199			return true;
200
201	return false;
202}
203
204static struct device_node *
205mt76_find_power_limits_node(struct mt76_dev *dev)
206{
207	struct device_node *np = dev->dev->of_node;
208	const char *const region_names[] = {
209		[NL80211_DFS_UNSET] = "ww",
210		[NL80211_DFS_ETSI] = "etsi",
211		[NL80211_DFS_FCC] = "fcc",
212		[NL80211_DFS_JP] = "jp",
213	};
214	struct device_node *cur, *fallback = NULL;
215	const char *region_name = NULL;
216
217	if (dev->region < ARRAY_SIZE(region_names))
218		region_name = region_names[dev->region];
219
220	np = of_get_child_by_name(np, "power-limits");
221	if (!np)
222		return NULL;
223
224	for_each_child_of_node(np, cur) {
225		struct property *country = of_find_property(cur, "country", NULL);
226		struct property *regd = of_find_property(cur, "regdomain", NULL);
227
228		if (!country && !regd) {
229			fallback = cur;
230			continue;
231		}
232
233		if (mt76_string_prop_find(country, dev->alpha2) ||
234		    mt76_string_prop_find(regd, region_name)) {
235			of_node_put(np);
236			return cur;
237		}
238	}
239
240	of_node_put(np);
241	return fallback;
242}
243
244static const __be32 *
245mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
246{
247	struct property *prop = of_find_property(np, name, NULL);
248
249	if (!prop || !prop->value || prop->length < min * 4)
250		return NULL;
251
252	*len = prop->length;
253
254	return prop->value;
255}
256
257static struct device_node *
258mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
259{
260	struct device_node *cur;
261	const __be32 *val;
262	size_t len;
263
264	for_each_child_of_node(np, cur) {
265		val = mt76_get_of_array(cur, "channels", &len, 2);
266		if (!val)
267			continue;
268
269		while (len >= 2 * sizeof(*val)) {
270			if (chan->hw_value >= be32_to_cpu(val[0]) &&
271			    chan->hw_value <= be32_to_cpu(val[1]))
272				return cur;
273
274			val += 2;
275			len -= 2 * sizeof(*val);
276		}
277	}
278
279	return NULL;
280}
281
282static s8
283mt76_get_txs_delta(struct device_node *np, u8 nss)
284{
285	const __be32 *val;
286	size_t len;
287
288	val = mt76_get_of_array(np, "txs-delta", &len, nss);
289	if (!val)
290		return 0;
291
292	return be32_to_cpu(val[nss - 1]);
293}
294
295static void
296mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
297		       s8 target_power, s8 nss_delta, s8 *max_power)
298{
299	int i;
300
301	if (!data)
302		return;
303
304	for (i = 0; i < pwr_len; i++) {
305		pwr[i] = min_t(s8, target_power,
306			       be32_to_cpu(data[i]) + nss_delta);
307		*max_power = max(*max_power, pwr[i]);
308	}
309}
310
311static void
312mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
313			     const __be32 *data, size_t len, s8 target_power,
314			     s8 nss_delta, s8 *max_power)
315{
316	int i, cur;
317
318	if (!data)
319		return;
320
321	len /= 4;
322	cur = be32_to_cpu(data[0]);
323	for (i = 0; i < pwr_num; i++) {
324		if (len < pwr_len + 1)
325			break;
326
327		mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
328				       target_power, nss_delta, max_power);
329		if (--cur > 0)
330			continue;
331
332		data += pwr_len + 1;
333		len -= pwr_len + 1;
334		if (!len)
335			break;
336
337		cur = be32_to_cpu(data[0]);
338	}
339}
340#endif
341
342s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
343			      struct ieee80211_channel *chan,
344			      struct mt76_power_limits *dest,
345			      s8 target_power)
346{
347	struct mt76_dev *dev = phy->dev;
348#if defined(CONFIG_OF)
349	struct device_node *np;
350	const __be32 *val;
351	char name[16];
352#endif
353	u32 mcs_rates = dev->drv->mcs_rates;
354#if defined(CONFIG_OF)
355	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
356	char band;
357	size_t len;
358#endif
359	s8 max_power = 0;
360#if defined(CONFIG_OF)
361	s8 txs_delta;
362#endif
363
364	if (!mcs_rates)
365		mcs_rates = 10;
366
367	memset(dest, target_power, sizeof(*dest));
368
369	if (!IS_ENABLED(CONFIG_OF))
370		return target_power;
371
372#if defined(CONFIG_OF)
373	np = mt76_find_power_limits_node(dev);
374	if (!np)
375		return target_power;
376
377	switch (chan->band) {
378	case NL80211_BAND_2GHZ:
379		band = '2';
380		break;
381	case NL80211_BAND_5GHZ:
382		band = '5';
383		break;
384	case NL80211_BAND_6GHZ:
385		band = '6';
386		break;
387	default:
388		return target_power;
389	}
390
391	snprintf(name, sizeof(name), "txpower-%cg", band);
392	np = of_get_child_by_name(np, name);
393	if (!np)
394		return target_power;
395
396	np = mt76_find_channel_node(np, chan);
397	if (!np)
398		return target_power;
399
400	txs_delta = mt76_get_txs_delta(np, hweight8(phy->antenna_mask));
401
402	val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
403	mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
404			       target_power, txs_delta, &max_power);
405
406	val = mt76_get_of_array(np, "rates-ofdm",
407				&len, ARRAY_SIZE(dest->ofdm));
408	mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
409			       target_power, txs_delta, &max_power);
410
411	val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
412	mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
413				     ARRAY_SIZE(dest->mcs), val, len,
414				     target_power, txs_delta, &max_power);
415
416	val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
417	mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
418				     ARRAY_SIZE(dest->ru), val, len,
419				     target_power, txs_delta, &max_power);
420
421#endif
422	return max_power;
423}
424EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
425
426int
427mt76_eeprom_init(struct mt76_dev *dev, int len)
428{
429	dev->eeprom.size = len;
430	dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
431	if (!dev->eeprom.data)
432		return -ENOMEM;
433
434	return !mt76_get_of_eeprom(dev, dev->eeprom.data, 0, len);
435}
436EXPORT_SYMBOL_GPL(mt76_eeprom_init);
437