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