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