]>
Commit | Line | Data |
---|---|---|
e87b5039 SG |
1 | /* |
2 | * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> | |
3 | * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> | |
4 | * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 | |
8 | * as published by the Free Software Foundation | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
db6bb5c6 | 16 | #include <linux/module.h> |
e87b5039 SG |
17 | #include <linux/of.h> |
18 | #include <linux/mtd/mtd.h> | |
19 | #include <linux/mtd/partitions.h> | |
20 | #include <linux/etherdevice.h> | |
21 | #include <asm/unaligned.h> | |
22 | #include "mt76x0.h" | |
23 | #include "eeprom.h" | |
b37bbc8c | 24 | #include "../mt76x02_phy.h" |
e87b5039 | 25 | |
17ad18fd | 26 | #define MT_MAP_READS DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16) |
e87b5039 | 27 | static int |
b2d871c0 | 28 | mt76x0_efuse_physical_size_check(struct mt76x02_dev *dev) |
e87b5039 | 29 | { |
17ad18fd | 30 | u8 data[MT_MAP_READS * 16]; |
e87b5039 SG |
31 | int ret, i; |
32 | u32 start = 0, end = 0, cnt_free; | |
33 | ||
26a9daa6 LB |
34 | ret = mt76x02_get_efuse_data(dev, MT_EE_USAGE_MAP_START, data, |
35 | sizeof(data), MT_EE_PHYSICAL_READ); | |
bd724b8f LB |
36 | if (ret) |
37 | return ret; | |
e87b5039 SG |
38 | |
39 | for (i = 0; i < MT_EFUSE_USAGE_MAP_SIZE; i++) | |
40 | if (!data[i]) { | |
41 | if (!start) | |
42 | start = MT_EE_USAGE_MAP_START + i; | |
43 | end = MT_EE_USAGE_MAP_START + i; | |
44 | } | |
45 | cnt_free = end - start + 1; | |
46 | ||
47 | if (MT_EFUSE_USAGE_MAP_SIZE - cnt_free < 5) { | |
bd724b8f LB |
48 | dev_err(dev->mt76.dev, |
49 | "driver does not support default EEPROM\n"); | |
e87b5039 SG |
50 | return -EINVAL; |
51 | } | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
b2d871c0 | 56 | static void mt76x0_set_chip_cap(struct mt76x02_dev *dev) |
e87b5039 | 57 | { |
26a9daa6 LB |
58 | u16 nic_conf0 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0); |
59 | u16 nic_conf1 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); | |
e87b5039 | 60 | |
26a9daa6 | 61 | mt76x02_eeprom_parse_hw_cap(dev); |
3d66939a | 62 | dev_dbg(dev->mt76.dev, "2GHz %d 5GHz %d\n", |
540399d5 | 63 | dev->mt76.cap.has_2ghz, dev->mt76.cap.has_5ghz); |
e87b5039 | 64 | |
d6500cf3 SG |
65 | if (dev->no_2ghz) { |
66 | dev->mt76.cap.has_2ghz = false; | |
67 | dev_dbg(dev->mt76.dev, "mask out 2GHz support\n"); | |
68 | } | |
69 | ||
86c71d3d | 70 | if (!mt76x02_field_valid(nic_conf1 & 0xff)) |
e87b5039 SG |
71 | nic_conf1 &= 0xff00; |
72 | ||
73 | if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL) | |
74 | dev_err(dev->mt76.dev, | |
3d66939a | 75 | "driver does not support HW RF ctrl\n"); |
e87b5039 | 76 | |
86c71d3d | 77 | if (!mt76x02_field_valid(nic_conf0 >> 8)) |
e87b5039 SG |
78 | return; |
79 | ||
80 | if (FIELD_GET(MT_EE_NIC_CONF_0_RX_PATH, nic_conf0) > 1 || | |
81 | FIELD_GET(MT_EE_NIC_CONF_0_TX_PATH, nic_conf0) > 1) | |
3d66939a | 82 | dev_err(dev->mt76.dev, "invalid tx-rx stream\n"); |
e87b5039 SG |
83 | } |
84 | ||
b2d871c0 | 85 | static void mt76x0_set_temp_offset(struct mt76x02_dev *dev) |
e87b5039 | 86 | { |
2c0db839 | 87 | u8 val; |
e87b5039 | 88 | |
26a9daa6 | 89 | val = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER) >> 8; |
2c0db839 | 90 | if (mt76x02_field_valid(val)) |
b2d871c0 | 91 | dev->cal.rx.temp_offset = mt76x02_sign_extend(val, 8); |
e87b5039 | 92 | else |
b2d871c0 | 93 | dev->cal.rx.temp_offset = -10; |
e87b5039 SG |
94 | } |
95 | ||
b2d871c0 | 96 | static void mt76x0_set_freq_offset(struct mt76x02_dev *dev) |
e87b5039 | 97 | { |
b2d871c0 | 98 | struct mt76x02_rx_freq_cal *caldata = &dev->cal.rx; |
77d0f465 | 99 | u8 val; |
e87b5039 | 100 | |
26a9daa6 | 101 | val = mt76x02_eeprom_get(dev, MT_EE_FREQ_OFFSET); |
77d0f465 LB |
102 | if (!mt76x02_field_valid(val)) |
103 | val = 0; | |
104 | caldata->freq_offset = val; | |
86c71d3d | 105 | |
26a9daa6 | 106 | val = mt76x02_eeprom_get(dev, MT_EE_TSSI_BOUND4) >> 8; |
77d0f465 LB |
107 | if (!mt76x02_field_valid(val)) |
108 | val = 0; | |
e87b5039 | 109 | |
77d0f465 | 110 | caldata->freq_offset -= mt76x02_sign_extend(val, 8); |
e87b5039 SG |
111 | } |
112 | ||
b2d871c0 | 113 | void mt76x0_read_rx_gain(struct mt76x02_dev *dev) |
e87b5039 | 114 | { |
2daa6758 | 115 | struct ieee80211_channel *chan = dev->mt76.chandef.chan; |
b2d871c0 | 116 | struct mt76x02_rx_freq_cal *caldata = &dev->cal.rx; |
564d7f0a | 117 | s8 val, lna_5g[3], lna_2g; |
2daa6758 | 118 | u16 rssi_offset; |
564d7f0a | 119 | int i; |
e87b5039 | 120 | |
26a9daa6 LB |
121 | mt76x02_get_rx_gain(dev, chan->band, &rssi_offset, &lna_2g, lna_5g); |
122 | caldata->lna_gain = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan); | |
e87b5039 | 123 | |
564d7f0a LB |
124 | for (i = 0; i < ARRAY_SIZE(caldata->rssi_offset); i++) { |
125 | val = rssi_offset >> (8 * i); | |
126 | if (val < -10 || val > 10) | |
127 | val = 0; | |
e87b5039 | 128 | |
564d7f0a | 129 | caldata->rssi_offset[i] = val; |
e87b5039 SG |
130 | } |
131 | } | |
132 | ||
26a9daa6 | 133 | static s8 mt76x0_get_delta(struct mt76x02_dev *dev) |
e87b5039 | 134 | { |
26a9daa6 | 135 | struct cfg80211_chan_def *chandef = &dev->mt76.chandef; |
b37bbc8c | 136 | u8 val; |
e87b5039 | 137 | |
b37bbc8c LB |
138 | if (chandef->width == NL80211_CHAN_WIDTH_80) { |
139 | val = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER) >> 8; | |
140 | } else if (chandef->width == NL80211_CHAN_WIDTH_40) { | |
141 | u16 data; | |
e87b5039 | 142 | |
b37bbc8c LB |
143 | data = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40); |
144 | if (chandef->chan->band == NL80211_BAND_5GHZ) | |
145 | val = data >> 8; | |
146 | else | |
147 | val = data; | |
148 | } else { | |
e87b5039 | 149 | return 0; |
b37bbc8c | 150 | } |
e87b5039 | 151 | |
b37bbc8c | 152 | return mt76x02_rate_power_val(val); |
e87b5039 SG |
153 | } |
154 | ||
b2d871c0 | 155 | void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev) |
e87b5039 | 156 | { |
b37bbc8c LB |
157 | struct ieee80211_channel *chan = dev->mt76.chandef.chan; |
158 | bool is_2ghz = chan->band == NL80211_BAND_2GHZ; | |
159 | struct mt76_rate_power *t = &dev->mt76.rate_power; | |
b37bbc8c | 160 | u16 val, addr; |
07e54852 | 161 | s8 delta; |
b37bbc8c LB |
162 | |
163 | memset(t, 0, sizeof(*t)); | |
164 | ||
165 | /* cck 1M, 2M, 5.5M, 11M */ | |
26a9daa6 | 166 | val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_BYRATE_BASE); |
b37bbc8c LB |
167 | t->cck[0] = t->cck[1] = s6_to_s8(val); |
168 | t->cck[2] = t->cck[3] = s6_to_s8(val >> 8); | |
169 | ||
170 | /* ofdm 6M, 9M, 12M, 18M */ | |
171 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 2 : 0x120; | |
26a9daa6 | 172 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
173 | t->ofdm[0] = t->ofdm[1] = s6_to_s8(val); |
174 | t->ofdm[2] = t->ofdm[3] = s6_to_s8(val >> 8); | |
175 | ||
176 | /* ofdm 24M, 36M, 48M, 54M */ | |
177 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 4 : 0x122; | |
26a9daa6 | 178 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
179 | t->ofdm[4] = t->ofdm[5] = s6_to_s8(val); |
180 | t->ofdm[6] = t->ofdm[7] = s6_to_s8(val >> 8); | |
181 | ||
182 | /* ht-vht mcs 1ss 0, 1, 2, 3 */ | |
183 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 6 : 0x124; | |
26a9daa6 | 184 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
185 | t->ht[0] = t->ht[1] = t->vht[0] = t->vht[1] = s6_to_s8(val); |
186 | t->ht[2] = t->ht[3] = t->vht[2] = t->vht[3] = s6_to_s8(val >> 8); | |
187 | ||
188 | /* ht-vht mcs 1ss 4, 5, 6 */ | |
189 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 8 : 0x126; | |
26a9daa6 | 190 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
191 | t->ht[4] = t->ht[5] = t->vht[4] = t->vht[5] = s6_to_s8(val); |
192 | t->ht[6] = t->vht[6] = s6_to_s8(val >> 8); | |
193 | ||
194 | /* ht-vht mcs 1ss 0, 1, 2, 3 stbc */ | |
195 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 14 : 0xec; | |
26a9daa6 | 196 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
197 | t->stbc[0] = t->stbc[1] = s6_to_s8(val); |
198 | t->stbc[2] = t->stbc[3] = s6_to_s8(val >> 8); | |
199 | ||
200 | /* ht-vht mcs 1ss 4, 5, 6 stbc */ | |
201 | addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 16 : 0xee; | |
26a9daa6 | 202 | val = mt76x02_eeprom_get(dev, addr); |
b37bbc8c LB |
203 | t->stbc[4] = t->stbc[5] = s6_to_s8(val); |
204 | t->stbc[6] = t->stbc[7] = s6_to_s8(val >> 8); | |
205 | ||
206 | /* vht mcs 8, 9 5GHz */ | |
26a9daa6 | 207 | val = mt76x02_eeprom_get(dev, 0x132); |
b37bbc8c LB |
208 | t->vht[7] = s6_to_s8(val); |
209 | t->vht[8] = s6_to_s8(val >> 8); | |
210 | ||
07e54852 | 211 | delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev); |
b37bbc8c | 212 | mt76x02_add_rate_power_offset(t, delta); |
e87b5039 SG |
213 | } |
214 | ||
05672636 | 215 | void mt76x0_get_power_info(struct mt76x02_dev *dev, s8 *tp) |
e87b5039 | 216 | { |
f2a2e819 LB |
217 | struct mt76x0_chan_map { |
218 | u8 chan; | |
219 | u8 offset; | |
220 | } chan_map[] = { | |
05672636 LB |
221 | { 2, 0 }, { 4, 2 }, { 6, 4 }, { 8, 6 }, |
222 | { 10, 8 }, { 12, 10 }, { 14, 12 }, { 38, 0 }, | |
223 | { 44, 2 }, { 48, 4 }, { 54, 6 }, { 60, 8 }, | |
224 | { 64, 10 }, { 102, 12 }, { 108, 14 }, { 112, 16 }, | |
225 | { 118, 18 }, { 124, 20 }, { 128, 22 }, { 134, 24 }, | |
226 | { 140, 26 }, { 151, 28 }, { 157, 30 }, { 161, 32 }, | |
227 | { 167, 34 }, { 171, 36 }, { 175, 38 }, | |
f2a2e819 LB |
228 | }; |
229 | struct ieee80211_channel *chan = dev->mt76.chandef.chan; | |
230 | u8 offset, addr; | |
05672636 | 231 | int i, idx = 0; |
f2a2e819 | 232 | u16 data; |
e87b5039 | 233 | |
07e54852 LB |
234 | if (mt76x0_tssi_enabled(dev)) { |
235 | s8 target_power; | |
236 | ||
237 | if (chan->band == NL80211_BAND_5GHZ) | |
238 | data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER); | |
239 | else | |
240 | data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER); | |
241 | target_power = (data & 0xff) - dev->mt76.rate_power.ofdm[7]; | |
05672636 | 242 | *tp = target_power + mt76x0_get_delta(dev); |
07e54852 LB |
243 | |
244 | return; | |
245 | } | |
246 | ||
f2a2e819 | 247 | for (i = 0; i < ARRAY_SIZE(chan_map); i++) { |
05672636 LB |
248 | if (chan->hw_value <= chan_map[i].chan) { |
249 | idx = (chan->hw_value == chan_map[i].chan); | |
f2a2e819 LB |
250 | offset = chan_map[i].offset; |
251 | break; | |
252 | } | |
e87b5039 | 253 | } |
f2a2e819 LB |
254 | if (i == ARRAY_SIZE(chan_map)) |
255 | offset = chan_map[0].offset; | |
e87b5039 | 256 | |
f2a2e819 LB |
257 | if (chan->band == NL80211_BAND_2GHZ) { |
258 | addr = MT_EE_TX_POWER_DELTA_BW80 + offset; | |
259 | } else { | |
260 | switch (chan->hw_value) { | |
05672636 LB |
261 | case 42: |
262 | offset = 2; | |
263 | break; | |
f2a2e819 LB |
264 | case 58: |
265 | offset = 8; | |
266 | break; | |
267 | case 106: | |
268 | offset = 14; | |
269 | break; | |
05672636 | 270 | case 122: |
f2a2e819 LB |
271 | offset = 20; |
272 | break; | |
273 | case 155: | |
274 | offset = 30; | |
275 | break; | |
276 | default: | |
277 | break; | |
278 | } | |
279 | addr = MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE + 2 + offset; | |
e87b5039 SG |
280 | } |
281 | ||
26a9daa6 | 282 | data = mt76x02_eeprom_get(dev, addr); |
05672636 LB |
283 | *tp = data >> (8 * idx); |
284 | if (*tp < 0 || *tp > 0x3f) | |
285 | *tp = 5; | |
e87b5039 SG |
286 | } |
287 | ||
b2d871c0 | 288 | static int mt76x0_check_eeprom(struct mt76x02_dev *dev) |
8d98c153 LB |
289 | { |
290 | u16 val; | |
291 | ||
292 | val = get_unaligned_le16(dev->mt76.eeprom.data); | |
293 | if (!val) | |
294 | val = get_unaligned_le16(dev->mt76.eeprom.data + | |
295 | MT_EE_PCI_ID); | |
296 | ||
297 | switch (val) { | |
298 | case 0x7650: | |
299 | case 0x7610: | |
300 | return 0; | |
301 | default: | |
302 | dev_err(dev->mt76.dev, "EEPROM data check failed: %04x\n", | |
303 | val); | |
304 | return -EINVAL; | |
305 | } | |
306 | } | |
307 | ||
b2d871c0 | 308 | static int mt76x0_load_eeprom(struct mt76x02_dev *dev) |
8d98c153 LB |
309 | { |
310 | int found; | |
311 | ||
312 | found = mt76_eeprom_init(&dev->mt76, MT76X0_EEPROM_SIZE); | |
313 | if (found < 0) | |
314 | return found; | |
315 | ||
316 | if (found && !mt76x0_check_eeprom(dev)) | |
317 | return 0; | |
318 | ||
319 | found = mt76x0_efuse_physical_size_check(dev); | |
320 | if (found < 0) | |
321 | return found; | |
322 | ||
26a9daa6 | 323 | return mt76x02_get_efuse_data(dev, 0, dev->mt76.eeprom.data, |
8d98c153 LB |
324 | MT76X0_EEPROM_SIZE, MT_EE_READ); |
325 | } | |
326 | ||
b2d871c0 | 327 | int mt76x0_eeprom_init(struct mt76x02_dev *dev) |
e87b5039 | 328 | { |
f2a2e819 LB |
329 | u8 version, fae; |
330 | u16 data; | |
8d98c153 | 331 | int err; |
e87b5039 | 332 | |
8d98c153 LB |
333 | err = mt76x0_load_eeprom(dev); |
334 | if (err < 0) | |
335 | return err; | |
f2a2e819 | 336 | |
26a9daa6 | 337 | data = mt76x02_eeprom_get(dev, MT_EE_VERSION); |
f2a2e819 LB |
338 | version = data >> 8; |
339 | fae = data; | |
e87b5039 | 340 | |
f2a2e819 | 341 | if (version > MT76X0U_EE_MAX_VER) |
e87b5039 SG |
342 | dev_warn(dev->mt76.dev, |
343 | "Warning: unsupported EEPROM version %02hhx\n", | |
f2a2e819 | 344 | version); |
e87b5039 | 345 | dev_info(dev->mt76.dev, "EEPROM ver:%02hhx fae:%02hhx\n", |
f2a2e819 | 346 | version, fae); |
e87b5039 | 347 | |
8d66af49 | 348 | mt76x02_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR); |
3d66939a | 349 | mt76x0_set_chip_cap(dev); |
77d0f465 | 350 | mt76x0_set_freq_offset(dev); |
2c0db839 | 351 | mt76x0_set_temp_offset(dev); |
e87b5039 | 352 | |
f2a2e819 | 353 | return 0; |
e87b5039 | 354 | } |
c2a4d9fb SG |
355 | |
356 | MODULE_LICENSE("Dual BSD/GPL"); |