xref: /freebsd/sys/contrib/dev/mediatek/mt76/eeprom.c (revision 8ba4d145d351db26e07695b8e90697398c5dfec2)
16c92544dSBjoern A. Zeeb // SPDX-License-Identifier: ISC
26c92544dSBjoern A. Zeeb /*
36c92544dSBjoern A. Zeeb  * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
46c92544dSBjoern A. Zeeb  */
56c92544dSBjoern A. Zeeb #if defined(CONFIG_OF) && defined(CONFIG_MTD)
66c92544dSBjoern A. Zeeb #include <linux/of.h>
76c92544dSBjoern A. Zeeb #include <linux/of_net.h>
86c92544dSBjoern A. Zeeb #include <linux/mtd/mtd.h>
96c92544dSBjoern A. Zeeb #include <linux/mtd/partitions.h>
10cbb3ec25SBjoern A. Zeeb #include <linux/nvmem-consumer.h>
116c92544dSBjoern A. Zeeb #endif
126c92544dSBjoern A. Zeeb #include <linux/etherdevice.h>
136c92544dSBjoern A. Zeeb #include "mt76.h"
146c92544dSBjoern A. Zeeb 
15cbb3ec25SBjoern A. Zeeb #if defined(CONFIG_OF)
mt76_get_of_eeprom_data(struct mt76_dev * dev,void * eep,int len)16cbb3ec25SBjoern A. Zeeb static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len)
176c92544dSBjoern A. Zeeb {
186c92544dSBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
196c92544dSBjoern A. Zeeb 	const void *data;
206c92544dSBjoern A. Zeeb 	int size;
216c92544dSBjoern A. Zeeb 
226c92544dSBjoern A. Zeeb 	data = of_get_property(np, "mediatek,eeprom-data", &size);
23cbb3ec25SBjoern A. Zeeb 	if (!data)
24cbb3ec25SBjoern A. Zeeb 		return -ENOENT;
25cbb3ec25SBjoern A. Zeeb 
266c92544dSBjoern A. Zeeb 	if (size > len)
276c92544dSBjoern A. Zeeb 		return -EINVAL;
286c92544dSBjoern A. Zeeb 
296c92544dSBjoern A. Zeeb 	memcpy(eep, data, size);
306c92544dSBjoern A. Zeeb 
316c92544dSBjoern A. Zeeb 	return 0;
326c92544dSBjoern A. Zeeb }
33cbb3ec25SBjoern A. Zeeb #endif
34cbb3ec25SBjoern A. Zeeb 
mt76_get_of_data_from_mtd(struct mt76_dev * dev,void * eep,int offset,int len)35*8ba4d145SBjoern A. Zeeb int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len)
36cbb3ec25SBjoern A. Zeeb {
37*8ba4d145SBjoern A. Zeeb #if !defined(CONFIG_MTD) || !defined(CONFIG_OF)
38*8ba4d145SBjoern A. Zeeb 	return -ENOENT;
39*8ba4d145SBjoern A. Zeeb #else
40cbb3ec25SBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
41cbb3ec25SBjoern A. Zeeb 	struct mtd_info *mtd;
42cbb3ec25SBjoern A. Zeeb 	const __be32 *list;
43cbb3ec25SBjoern A. Zeeb 	const char *part;
44cbb3ec25SBjoern A. Zeeb 	phandle phandle;
45cbb3ec25SBjoern A. Zeeb 	size_t retlen;
46cbb3ec25SBjoern A. Zeeb 	int size;
47cbb3ec25SBjoern A. Zeeb 	int ret;
486c92544dSBjoern A. Zeeb 
496c92544dSBjoern A. Zeeb 	list = of_get_property(np, "mediatek,mtd-eeprom", &size);
506c92544dSBjoern A. Zeeb 	if (!list)
516c92544dSBjoern A. Zeeb 		return -ENOENT;
526c92544dSBjoern A. Zeeb 
536c92544dSBjoern A. Zeeb 	phandle = be32_to_cpup(list++);
546c92544dSBjoern A. Zeeb 	if (!phandle)
556c92544dSBjoern A. Zeeb 		return -ENOENT;
566c92544dSBjoern A. Zeeb 
576c92544dSBjoern A. Zeeb 	np = of_find_node_by_phandle(phandle);
586c92544dSBjoern A. Zeeb 	if (!np)
596c92544dSBjoern A. Zeeb 		return -EINVAL;
606c92544dSBjoern A. Zeeb 
616c92544dSBjoern A. Zeeb 	part = of_get_property(np, "label", NULL);
626c92544dSBjoern A. Zeeb 	if (!part)
636c92544dSBjoern A. Zeeb 		part = np->name;
646c92544dSBjoern A. Zeeb 
656c92544dSBjoern A. Zeeb 	mtd = get_mtd_device_nm(part);
666c92544dSBjoern A. Zeeb 	if (IS_ERR(mtd)) {
676c92544dSBjoern A. Zeeb 		ret =  PTR_ERR(mtd);
686c92544dSBjoern A. Zeeb 		goto out_put_node;
696c92544dSBjoern A. Zeeb 	}
706c92544dSBjoern A. Zeeb 
716c92544dSBjoern A. Zeeb 	if (size <= sizeof(*list)) {
726c92544dSBjoern A. Zeeb 		ret = -EINVAL;
736c92544dSBjoern A. Zeeb 		goto out_put_node;
746c92544dSBjoern A. Zeeb 	}
756c92544dSBjoern A. Zeeb 
76*8ba4d145SBjoern A. Zeeb 	offset += be32_to_cpup(list);
776c92544dSBjoern A. Zeeb 	ret = mtd_read(mtd, offset, len, &retlen, eep);
786c92544dSBjoern A. Zeeb 	put_mtd_device(mtd);
796c92544dSBjoern A. Zeeb 	if (mtd_is_bitflip(ret))
806c92544dSBjoern A. Zeeb 		ret = 0;
816c92544dSBjoern A. Zeeb 	if (ret) {
826c92544dSBjoern A. Zeeb 		dev_err(dev->dev, "reading EEPROM from mtd %s failed: %i\n",
836c92544dSBjoern A. Zeeb 			part, ret);
846c92544dSBjoern A. Zeeb 		goto out_put_node;
856c92544dSBjoern A. Zeeb 	}
866c92544dSBjoern A. Zeeb 
876c92544dSBjoern A. Zeeb 	if (retlen < len) {
886c92544dSBjoern A. Zeeb 		ret = -EINVAL;
896c92544dSBjoern A. Zeeb 		goto out_put_node;
906c92544dSBjoern A. Zeeb 	}
916c92544dSBjoern A. Zeeb 
926c92544dSBjoern A. Zeeb 	if (of_property_read_bool(dev->dev->of_node, "big-endian")) {
936c92544dSBjoern A. Zeeb 		u8 *data = (u8 *)eep;
946c92544dSBjoern A. Zeeb 		int i;
956c92544dSBjoern A. Zeeb 
966c92544dSBjoern A. Zeeb 		/* convert eeprom data in Little Endian */
976c92544dSBjoern A. Zeeb 		for (i = 0; i < round_down(len, 2); i += 2)
986c92544dSBjoern A. Zeeb 			put_unaligned_le16(get_unaligned_be16(&data[i]),
996c92544dSBjoern A. Zeeb 					   &data[i]);
1006c92544dSBjoern A. Zeeb 	}
1016c92544dSBjoern A. Zeeb 
1026c92544dSBjoern A. Zeeb #ifdef CONFIG_NL80211_TESTMODE
1036c92544dSBjoern A. Zeeb 	dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
1046c92544dSBjoern A. Zeeb 	dev->test_mtd.offset = offset;
1056c92544dSBjoern A. Zeeb #endif
1066c92544dSBjoern A. Zeeb 
1076c92544dSBjoern A. Zeeb out_put_node:
1086c92544dSBjoern A. Zeeb 	of_node_put(np);
1096c92544dSBjoern A. Zeeb 	return ret;
110cbb3ec25SBjoern A. Zeeb #endif
111*8ba4d145SBjoern A. Zeeb }
112*8ba4d145SBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_get_of_data_from_mtd);
113cbb3ec25SBjoern A. Zeeb 
mt76_get_of_data_from_nvmem(struct mt76_dev * dev,void * eep,const char * cell_name,int len)114*8ba4d145SBjoern A. Zeeb int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
115*8ba4d145SBjoern A. Zeeb 				const char *cell_name, int len)
116cbb3ec25SBjoern A. Zeeb {
117*8ba4d145SBjoern A. Zeeb #if !defined(CONFIG_OF)
118*8ba4d145SBjoern A. Zeeb 	return -EOPNOTSUPP;
119*8ba4d145SBjoern A. Zeeb #else
120cbb3ec25SBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
121cbb3ec25SBjoern A. Zeeb 	struct nvmem_cell *cell;
122cbb3ec25SBjoern A. Zeeb 	const void *data;
123cbb3ec25SBjoern A. Zeeb 	size_t retlen;
124cbb3ec25SBjoern A. Zeeb 	int ret = 0;
125cbb3ec25SBjoern A. Zeeb 
126*8ba4d145SBjoern A. Zeeb 	cell = of_nvmem_cell_get(np, cell_name);
127cbb3ec25SBjoern A. Zeeb 	if (IS_ERR(cell))
128cbb3ec25SBjoern A. Zeeb 		return PTR_ERR(cell);
129cbb3ec25SBjoern A. Zeeb 
130cbb3ec25SBjoern A. Zeeb 	data = nvmem_cell_read(cell, &retlen);
131cbb3ec25SBjoern A. Zeeb 	nvmem_cell_put(cell);
132cbb3ec25SBjoern A. Zeeb 
133cbb3ec25SBjoern A. Zeeb 	if (IS_ERR(data))
134cbb3ec25SBjoern A. Zeeb 		return PTR_ERR(data);
135cbb3ec25SBjoern A. Zeeb 
136cbb3ec25SBjoern A. Zeeb 	if (retlen < len) {
137cbb3ec25SBjoern A. Zeeb 		ret = -EINVAL;
138cbb3ec25SBjoern A. Zeeb 		goto exit;
139cbb3ec25SBjoern A. Zeeb 	}
140cbb3ec25SBjoern A. Zeeb 
141cbb3ec25SBjoern A. Zeeb 	memcpy(eep, data, len);
142cbb3ec25SBjoern A. Zeeb 
143cbb3ec25SBjoern A. Zeeb exit:
144cbb3ec25SBjoern A. Zeeb 	kfree(data);
145cbb3ec25SBjoern A. Zeeb 
146cbb3ec25SBjoern A. Zeeb 	return ret;
147cbb3ec25SBjoern A. Zeeb #endif
148*8ba4d145SBjoern A. Zeeb }
149*8ba4d145SBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_get_of_data_from_nvmem);
150cbb3ec25SBjoern A. Zeeb 
mt76_get_of_eeprom(struct mt76_dev * dev,void * eep,int len)151*8ba4d145SBjoern A. Zeeb static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
152cbb3ec25SBjoern A. Zeeb {
153*8ba4d145SBjoern A. Zeeb #if !defined(CONFIG_MTD) || !defined(CONFIG_OF)
154*8ba4d145SBjoern A. Zeeb 	return -ENOENT;
155*8ba4d145SBjoern A. Zeeb #else
156cbb3ec25SBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
157cbb3ec25SBjoern A. Zeeb 	int ret;
158cbb3ec25SBjoern A. Zeeb 
159cbb3ec25SBjoern A. Zeeb 	if (!np)
160cbb3ec25SBjoern A. Zeeb 		return -ENOENT;
161cbb3ec25SBjoern A. Zeeb 
162cbb3ec25SBjoern A. Zeeb 	ret = mt76_get_of_eeprom_data(dev, eep, len);
163cbb3ec25SBjoern A. Zeeb 	if (!ret)
164cbb3ec25SBjoern A. Zeeb 		return 0;
165cbb3ec25SBjoern A. Zeeb 
166*8ba4d145SBjoern A. Zeeb 	ret = mt76_get_of_data_from_mtd(dev, eep, 0, len);
167cbb3ec25SBjoern A. Zeeb 	if (!ret)
168cbb3ec25SBjoern A. Zeeb 		return 0;
169cbb3ec25SBjoern A. Zeeb 
170*8ba4d145SBjoern A. Zeeb 	return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
1716c92544dSBjoern A. Zeeb #endif
1726c92544dSBjoern A. Zeeb }
1736c92544dSBjoern A. Zeeb 
1746c92544dSBjoern A. Zeeb void
mt76_eeprom_override(struct mt76_phy * phy)1756c92544dSBjoern A. Zeeb mt76_eeprom_override(struct mt76_phy *phy)
1766c92544dSBjoern A. Zeeb {
1776c92544dSBjoern A. Zeeb 	struct mt76_dev *dev = phy->dev;
1786c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
1796c92544dSBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
1806c92544dSBjoern A. Zeeb 
1816c92544dSBjoern A. Zeeb 	of_get_mac_address(np, phy->macaddr);
1826c92544dSBjoern A. Zeeb 
1836c92544dSBjoern A. Zeeb 	if (!is_valid_ether_addr(phy->macaddr)) {
1846c92544dSBjoern A. Zeeb #endif
1856c92544dSBjoern A. Zeeb 		eth_random_addr(phy->macaddr);
1866c92544dSBjoern A. Zeeb 		dev_info(dev->dev,
1876c92544dSBjoern A. Zeeb 			 "Invalid MAC address, using random address %pM\n",
1886c92544dSBjoern A. Zeeb 			 phy->macaddr);
1896c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
1906c92544dSBjoern A. Zeeb 	}
1916c92544dSBjoern A. Zeeb #endif
1926c92544dSBjoern A. Zeeb }
1936c92544dSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_eeprom_override);
1946c92544dSBjoern A. Zeeb 
1956c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
mt76_string_prop_find(struct property * prop,const char * str)1966c92544dSBjoern A. Zeeb static bool mt76_string_prop_find(struct property *prop, const char *str)
1976c92544dSBjoern A. Zeeb {
1986c92544dSBjoern A. Zeeb 	const char *cp = NULL;
1996c92544dSBjoern A. Zeeb 
2006c92544dSBjoern A. Zeeb 	if (!prop || !str || !str[0])
2016c92544dSBjoern A. Zeeb 		return false;
2026c92544dSBjoern A. Zeeb 
2036c92544dSBjoern A. Zeeb 	while ((cp = of_prop_next_string(prop, cp)) != NULL)
2046c92544dSBjoern A. Zeeb 		if (!strcasecmp(cp, str))
2056c92544dSBjoern A. Zeeb 			return true;
2066c92544dSBjoern A. Zeeb 	return false;
2076c92544dSBjoern A. Zeeb }
208*8ba4d145SBjoern A. Zeeb #endif
2096c92544dSBjoern A. Zeeb 
210*8ba4d145SBjoern A. Zeeb struct device_node *
mt76_find_power_limits_node(struct mt76_dev * dev)2116c92544dSBjoern A. Zeeb mt76_find_power_limits_node(struct mt76_dev *dev)
2126c92544dSBjoern A. Zeeb {
213*8ba4d145SBjoern A. Zeeb #if !defined(CONFIG_OF)
214*8ba4d145SBjoern A. Zeeb 	return NULL;
215*8ba4d145SBjoern A. Zeeb #else
2166c92544dSBjoern A. Zeeb 	struct device_node *np = dev->dev->of_node;
2176c92544dSBjoern A. Zeeb 	const char *const region_names[] = {
218cbb3ec25SBjoern A. Zeeb 		[NL80211_DFS_UNSET] = "ww",
2196c92544dSBjoern A. Zeeb 		[NL80211_DFS_ETSI] = "etsi",
2206c92544dSBjoern A. Zeeb 		[NL80211_DFS_FCC] = "fcc",
2216c92544dSBjoern A. Zeeb 		[NL80211_DFS_JP] = "jp",
2226c92544dSBjoern A. Zeeb 	};
2236c92544dSBjoern A. Zeeb 	struct device_node *cur, *fallback = NULL;
2246c92544dSBjoern A. Zeeb 	const char *region_name = NULL;
2256c92544dSBjoern A. Zeeb 
2266c92544dSBjoern A. Zeeb 	if (dev->region < ARRAY_SIZE(region_names))
2276c92544dSBjoern A. Zeeb 		region_name = region_names[dev->region];
2286c92544dSBjoern A. Zeeb 
2296c92544dSBjoern A. Zeeb 	np = of_get_child_by_name(np, "power-limits");
2306c92544dSBjoern A. Zeeb 	if (!np)
2316c92544dSBjoern A. Zeeb 		return NULL;
2326c92544dSBjoern A. Zeeb 
2336c92544dSBjoern A. Zeeb 	for_each_child_of_node(np, cur) {
2346c92544dSBjoern A. Zeeb 		struct property *country = of_find_property(cur, "country", NULL);
2356c92544dSBjoern A. Zeeb 		struct property *regd = of_find_property(cur, "regdomain", NULL);
2366c92544dSBjoern A. Zeeb 
2376c92544dSBjoern A. Zeeb 		if (!country && !regd) {
2386c92544dSBjoern A. Zeeb 			fallback = cur;
2396c92544dSBjoern A. Zeeb 			continue;
2406c92544dSBjoern A. Zeeb 		}
2416c92544dSBjoern A. Zeeb 
2426c92544dSBjoern A. Zeeb 		if (mt76_string_prop_find(country, dev->alpha2) ||
2436c92544dSBjoern A. Zeeb 		    mt76_string_prop_find(regd, region_name)) {
2446c92544dSBjoern A. Zeeb 			of_node_put(np);
2456c92544dSBjoern A. Zeeb 			return cur;
2466c92544dSBjoern A. Zeeb 		}
2476c92544dSBjoern A. Zeeb 	}
2486c92544dSBjoern A. Zeeb 
2496c92544dSBjoern A. Zeeb 	of_node_put(np);
2506c92544dSBjoern A. Zeeb 	return fallback;
251*8ba4d145SBjoern A. Zeeb #endif
2526c92544dSBjoern A. Zeeb }
253*8ba4d145SBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_find_power_limits_node);
2546c92544dSBjoern A. Zeeb 
255*8ba4d145SBjoern A. Zeeb #if defined(CONFIG_OF)
2566c92544dSBjoern A. Zeeb static const __be32 *
mt76_get_of_array(struct device_node * np,char * name,size_t * len,int min)2576c92544dSBjoern A. Zeeb mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
2586c92544dSBjoern A. Zeeb {
2596c92544dSBjoern A. Zeeb 	struct property *prop = of_find_property(np, name, NULL);
2606c92544dSBjoern A. Zeeb 
2616c92544dSBjoern A. Zeeb 	if (!prop || !prop->value || prop->length < min * 4)
2626c92544dSBjoern A. Zeeb 		return NULL;
2636c92544dSBjoern A. Zeeb 
2646c92544dSBjoern A. Zeeb 	*len = prop->length;
2656c92544dSBjoern A. Zeeb 
2666c92544dSBjoern A. Zeeb 	return prop->value;
2676c92544dSBjoern A. Zeeb }
268*8ba4d145SBjoern A. Zeeb #endif
2696c92544dSBjoern A. Zeeb 
270*8ba4d145SBjoern A. Zeeb struct device_node *
mt76_find_channel_node(struct device_node * np,struct ieee80211_channel * chan)2716c92544dSBjoern A. Zeeb mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
2726c92544dSBjoern A. Zeeb {
273*8ba4d145SBjoern A. Zeeb #if defined(CONFIG_OF)
2746c92544dSBjoern A. Zeeb 	struct device_node *cur;
2756c92544dSBjoern A. Zeeb 	const __be32 *val;
2766c92544dSBjoern A. Zeeb 	size_t len;
2776c92544dSBjoern A. Zeeb 
2786c92544dSBjoern A. Zeeb 	for_each_child_of_node(np, cur) {
2796c92544dSBjoern A. Zeeb 		val = mt76_get_of_array(cur, "channels", &len, 2);
2806c92544dSBjoern A. Zeeb 		if (!val)
2816c92544dSBjoern A. Zeeb 			continue;
2826c92544dSBjoern A. Zeeb 
2836c92544dSBjoern A. Zeeb 		while (len >= 2 * sizeof(*val)) {
2846c92544dSBjoern A. Zeeb 			if (chan->hw_value >= be32_to_cpu(val[0]) &&
2856c92544dSBjoern A. Zeeb 			    chan->hw_value <= be32_to_cpu(val[1]))
2866c92544dSBjoern A. Zeeb 				return cur;
2876c92544dSBjoern A. Zeeb 
2886c92544dSBjoern A. Zeeb 			val += 2;
2896c92544dSBjoern A. Zeeb 			len -= 2 * sizeof(*val);
2906c92544dSBjoern A. Zeeb 		}
2916c92544dSBjoern A. Zeeb 	}
292*8ba4d145SBjoern A. Zeeb #endif
2936c92544dSBjoern A. Zeeb 	return NULL;
2946c92544dSBjoern A. Zeeb }
295*8ba4d145SBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_find_channel_node);
2966c92544dSBjoern A. Zeeb 
297*8ba4d145SBjoern A. Zeeb #if defined(CONFIG_OF)
2986c92544dSBjoern A. Zeeb static s8
mt76_get_txs_delta(struct device_node * np,u8 nss)2996c92544dSBjoern A. Zeeb mt76_get_txs_delta(struct device_node *np, u8 nss)
3006c92544dSBjoern A. Zeeb {
3016c92544dSBjoern A. Zeeb 	const __be32 *val;
3026c92544dSBjoern A. Zeeb 	size_t len;
3036c92544dSBjoern A. Zeeb 
3046c92544dSBjoern A. Zeeb 	val = mt76_get_of_array(np, "txs-delta", &len, nss);
3056c92544dSBjoern A. Zeeb 	if (!val)
3066c92544dSBjoern A. Zeeb 		return 0;
3076c92544dSBjoern A. Zeeb 
3086c92544dSBjoern A. Zeeb 	return be32_to_cpu(val[nss - 1]);
3096c92544dSBjoern A. Zeeb }
3106c92544dSBjoern A. Zeeb 
3116c92544dSBjoern A. Zeeb static void
mt76_apply_array_limit(s8 * pwr,size_t pwr_len,const __be32 * data,s8 target_power,s8 nss_delta,s8 * max_power)3126c92544dSBjoern A. Zeeb mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
3136c92544dSBjoern A. Zeeb 		       s8 target_power, s8 nss_delta, s8 *max_power)
3146c92544dSBjoern A. Zeeb {
3156c92544dSBjoern A. Zeeb 	int i;
3166c92544dSBjoern A. Zeeb 
3176c92544dSBjoern A. Zeeb 	if (!data)
3186c92544dSBjoern A. Zeeb 		return;
3196c92544dSBjoern A. Zeeb 
3206c92544dSBjoern A. Zeeb 	for (i = 0; i < pwr_len; i++) {
3216c92544dSBjoern A. Zeeb 		pwr[i] = min_t(s8, target_power,
3226c92544dSBjoern A. Zeeb 			       be32_to_cpu(data[i]) + nss_delta);
3236c92544dSBjoern A. Zeeb 		*max_power = max(*max_power, pwr[i]);
3246c92544dSBjoern A. Zeeb 	}
3256c92544dSBjoern A. Zeeb }
3266c92544dSBjoern A. Zeeb 
3276c92544dSBjoern A. Zeeb static void
mt76_apply_multi_array_limit(s8 * pwr,size_t pwr_len,s8 pwr_num,const __be32 * data,size_t len,s8 target_power,s8 nss_delta,s8 * max_power)3286c92544dSBjoern A. Zeeb mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
3296c92544dSBjoern A. Zeeb 			     const __be32 *data, size_t len, s8 target_power,
3306c92544dSBjoern A. Zeeb 			     s8 nss_delta, s8 *max_power)
3316c92544dSBjoern A. Zeeb {
3326c92544dSBjoern A. Zeeb 	int i, cur;
3336c92544dSBjoern A. Zeeb 
3346c92544dSBjoern A. Zeeb 	if (!data)
3356c92544dSBjoern A. Zeeb 		return;
3366c92544dSBjoern A. Zeeb 
3376c92544dSBjoern A. Zeeb 	len /= 4;
3386c92544dSBjoern A. Zeeb 	cur = be32_to_cpu(data[0]);
3396c92544dSBjoern A. Zeeb 	for (i = 0; i < pwr_num; i++) {
3406c92544dSBjoern A. Zeeb 		if (len < pwr_len + 1)
3416c92544dSBjoern A. Zeeb 			break;
3426c92544dSBjoern A. Zeeb 
3436c92544dSBjoern A. Zeeb 		mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
3446c92544dSBjoern A. Zeeb 				       target_power, nss_delta, max_power);
3456c92544dSBjoern A. Zeeb 		if (--cur > 0)
3466c92544dSBjoern A. Zeeb 			continue;
3476c92544dSBjoern A. Zeeb 
3486c92544dSBjoern A. Zeeb 		data += pwr_len + 1;
3496c92544dSBjoern A. Zeeb 		len -= pwr_len + 1;
3506c92544dSBjoern A. Zeeb 		if (!len)
3516c92544dSBjoern A. Zeeb 			break;
3526c92544dSBjoern A. Zeeb 
3536c92544dSBjoern A. Zeeb 		cur = be32_to_cpu(data[0]);
3546c92544dSBjoern A. Zeeb 	}
3556c92544dSBjoern A. Zeeb }
3566c92544dSBjoern A. Zeeb #endif
3576c92544dSBjoern A. Zeeb 
mt76_get_rate_power_limits(struct mt76_phy * phy,struct ieee80211_channel * chan,struct mt76_power_limits * dest,s8 target_power)3586c92544dSBjoern A. Zeeb s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
3596c92544dSBjoern A. Zeeb 			      struct ieee80211_channel *chan,
3606c92544dSBjoern A. Zeeb 			      struct mt76_power_limits *dest,
3616c92544dSBjoern A. Zeeb 			      s8 target_power)
3626c92544dSBjoern A. Zeeb {
3636c92544dSBjoern A. Zeeb 	struct mt76_dev *dev = phy->dev;
3646c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
3656c92544dSBjoern A. Zeeb 	struct device_node *np;
3666c92544dSBjoern A. Zeeb 	const __be32 *val;
3676c92544dSBjoern A. Zeeb 	char name[16];
3686c92544dSBjoern A. Zeeb #endif
3696c92544dSBjoern A. Zeeb 	u32 mcs_rates = dev->drv->mcs_rates;
3706c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
3716c92544dSBjoern A. Zeeb 	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
3726c92544dSBjoern A. Zeeb 	char band;
3736c92544dSBjoern A. Zeeb 	size_t len;
3746c92544dSBjoern A. Zeeb #endif
3756c92544dSBjoern A. Zeeb 	s8 max_power = 0;
3766c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
3776c92544dSBjoern A. Zeeb 	s8 txs_delta;
3786c92544dSBjoern A. Zeeb #endif
3796c92544dSBjoern A. Zeeb 
3806c92544dSBjoern A. Zeeb 	if (!mcs_rates)
3816c92544dSBjoern A. Zeeb 		mcs_rates = 10;
3826c92544dSBjoern A. Zeeb 
3836c92544dSBjoern A. Zeeb 	memset(dest, target_power, sizeof(*dest));
3846c92544dSBjoern A. Zeeb 
3856c92544dSBjoern A. Zeeb 	if (!IS_ENABLED(CONFIG_OF))
3866c92544dSBjoern A. Zeeb 		return target_power;
3876c92544dSBjoern A. Zeeb 
3886c92544dSBjoern A. Zeeb #if defined(CONFIG_OF)
3896c92544dSBjoern A. Zeeb 	np = mt76_find_power_limits_node(dev);
3906c92544dSBjoern A. Zeeb 	if (!np)
3916c92544dSBjoern A. Zeeb 		return target_power;
3926c92544dSBjoern A. Zeeb 
3936c92544dSBjoern A. Zeeb 	switch (chan->band) {
3946c92544dSBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
3956c92544dSBjoern A. Zeeb 		band = '2';
3966c92544dSBjoern A. Zeeb 		break;
3976c92544dSBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
3986c92544dSBjoern A. Zeeb 		band = '5';
3996c92544dSBjoern A. Zeeb 		break;
4006c92544dSBjoern A. Zeeb 	case NL80211_BAND_6GHZ:
4016c92544dSBjoern A. Zeeb 		band = '6';
4026c92544dSBjoern A. Zeeb 		break;
4036c92544dSBjoern A. Zeeb 	default:
4046c92544dSBjoern A. Zeeb 		return target_power;
4056c92544dSBjoern A. Zeeb 	}
4066c92544dSBjoern A. Zeeb 
4076c92544dSBjoern A. Zeeb 	snprintf(name, sizeof(name), "txpower-%cg", band);
4086c92544dSBjoern A. Zeeb 	np = of_get_child_by_name(np, name);
4096c92544dSBjoern A. Zeeb 	if (!np)
4106c92544dSBjoern A. Zeeb 		return target_power;
4116c92544dSBjoern A. Zeeb 
4126c92544dSBjoern A. Zeeb 	np = mt76_find_channel_node(np, chan);
4136c92544dSBjoern A. Zeeb 	if (!np)
4146c92544dSBjoern A. Zeeb 		return target_power;
4156c92544dSBjoern A. Zeeb 
416*8ba4d145SBjoern A. Zeeb 	txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask));
4176c92544dSBjoern A. Zeeb 
4186c92544dSBjoern A. Zeeb 	val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
4196c92544dSBjoern A. Zeeb 	mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
4206c92544dSBjoern A. Zeeb 			       target_power, txs_delta, &max_power);
4216c92544dSBjoern A. Zeeb 
4226c92544dSBjoern A. Zeeb 	val = mt76_get_of_array(np, "rates-ofdm",
4236c92544dSBjoern A. Zeeb 				&len, ARRAY_SIZE(dest->ofdm));
4246c92544dSBjoern A. Zeeb 	mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
4256c92544dSBjoern A. Zeeb 			       target_power, txs_delta, &max_power);
4266c92544dSBjoern A. Zeeb 
4276c92544dSBjoern A. Zeeb 	val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
4286c92544dSBjoern A. Zeeb 	mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
4296c92544dSBjoern A. Zeeb 				     ARRAY_SIZE(dest->mcs), val, len,
4306c92544dSBjoern A. Zeeb 				     target_power, txs_delta, &max_power);
4316c92544dSBjoern A. Zeeb 
4326c92544dSBjoern A. Zeeb 	val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
4336c92544dSBjoern A. Zeeb 	mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
4346c92544dSBjoern A. Zeeb 				     ARRAY_SIZE(dest->ru), val, len,
4356c92544dSBjoern A. Zeeb 				     target_power, txs_delta, &max_power);
436cbb3ec25SBjoern A. Zeeb 
4376c92544dSBjoern A. Zeeb #endif
4386c92544dSBjoern A. Zeeb 	return max_power;
4396c92544dSBjoern A. Zeeb }
4406c92544dSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
4416c92544dSBjoern A. Zeeb 
4426c92544dSBjoern A. Zeeb int
mt76_eeprom_init(struct mt76_dev * dev,int len)4436c92544dSBjoern A. Zeeb mt76_eeprom_init(struct mt76_dev *dev, int len)
4446c92544dSBjoern A. Zeeb {
4456c92544dSBjoern A. Zeeb 	dev->eeprom.size = len;
4466c92544dSBjoern A. Zeeb 	dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
4476c92544dSBjoern A. Zeeb 	if (!dev->eeprom.data)
4486c92544dSBjoern A. Zeeb 		return -ENOMEM;
4496c92544dSBjoern A. Zeeb 
450*8ba4d145SBjoern A. Zeeb 	return !mt76_get_of_eeprom(dev, dev->eeprom.data, len);
4516c92544dSBjoern A. Zeeb }
4526c92544dSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_eeprom_init);
453