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