blob: 10720d02f72805eda4090a3dea40c3fac1a9fbb8 [file] [log] [blame]
developer4616dbd2022-10-11 13:18:59 +08001#define _GNU_SOURCE
2#include <fcntl.h>
3#include <sys/mman.h>
4#include <sys/stat.h>
5#include <sys/wait.h>
6
7#include "atenl.h"
8
developer32b0e0f2023-04-11 13:34:56 +08009#define EEPROM_PART_SIZE 0xFF000
developer4616dbd2022-10-11 13:18:59 +080010char *eeprom_file;
11
developer27934b82022-11-04 16:03:09 +080012static int
13atenl_create_file(struct atenl *an, bool flash_mode)
developer4616dbd2022-10-11 13:18:59 +080014{
developer27934b82022-11-04 16:03:09 +080015 char fname[64], buf[1024];
16 ssize_t w, len, max_len, total_len = 0;
17 int fd_ori, fd, ret;
developer4616dbd2022-10-11 13:18:59 +080018
developer27934b82022-11-04 16:03:09 +080019 /* reserve space for pre-cal data in flash mode */
20 if (flash_mode) {
21 atenl_dbg("%s: init eeprom with flash mode\n", __func__);
22 max_len = EEPROM_PART_SIZE;
23 } else {
24 atenl_dbg("%s: init eeprom with efuse mode\n", __func__);
developerb9a96602023-01-10 19:53:25 +080025 max_len = 0x1e00;
developer4616dbd2022-10-11 13:18:59 +080026 }
developer4616dbd2022-10-11 13:18:59 +080027
developer27934b82022-11-04 16:03:09 +080028 snprintf(fname, sizeof(fname),
29 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
30 get_band_val(an, 0, phy_idx));
31 fd_ori = open(fname, O_RDONLY);
32 if (fd_ori < 0)
developer4616dbd2022-10-11 13:18:59 +080033 return -1;
developer4616dbd2022-10-11 13:18:59 +080034
35 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
36 if (fd < 0)
37 goto out;
38
developer27934b82022-11-04 16:03:09 +080039 while ((len = read(fd_ori, buf, sizeof(buf))) > 0) {
developer4616dbd2022-10-11 13:18:59 +080040retry:
41 w = write(fd, buf, len);
42 if (w > 0) {
developer27934b82022-11-04 16:03:09 +080043 total_len += len;
developer4616dbd2022-10-11 13:18:59 +080044 continue;
45 }
46
47 if (errno == EINTR)
48 goto retry;
49
50 perror("write");
51 unlink(eeprom_file);
52 close(fd);
53 fd = -1;
54 goto out;
55 }
56
developer27934b82022-11-04 16:03:09 +080057 /* reserve space for pre-cal data in flash mode */
58 len = sizeof(buf);
59 memset(buf, 0, len);
60 while (total_len < max_len) {
developer4616dbd2022-10-11 13:18:59 +080061 w = write(fd, buf, len);
developer4616dbd2022-10-11 13:18:59 +080062
developer27934b82022-11-04 16:03:09 +080063 if (w > 0) {
64 total_len += len;
65 continue;
66 }
developer4616dbd2022-10-11 13:18:59 +080067
developer27934b82022-11-04 16:03:09 +080068 if (errno != EINTR) {
69 perror("write");
70 unlink(eeprom_file);
71 close(fd);
72 fd = -1;
73 goto out;
74 }
developer4616dbd2022-10-11 13:18:59 +080075 }
76
developer27934b82022-11-04 16:03:09 +080077
developer4616dbd2022-10-11 13:18:59 +080078 ret = lseek(fd, 0, SEEK_SET);
79 if (ret) {
80 close(fd_ori);
81 close(fd);
82 return ret;
83 }
84
85out:
86 close(fd_ori);
87 return fd;
88}
89
90static bool
91atenl_eeprom_file_exists(void)
92{
93 struct stat st;
94
95 return stat(eeprom_file, &st) == 0;
96}
97
98static int
99atenl_eeprom_init_file(struct atenl *an, bool flash_mode)
100{
101 int fd;
102
developer27934b82022-11-04 16:03:09 +0800103 if (!atenl_eeprom_file_exists())
104 return atenl_create_file(an, flash_mode);
developer4616dbd2022-10-11 13:18:59 +0800105
106 fd = open(eeprom_file, O_RDWR);
107 if (fd < 0)
108 perror("open");
109
110 return fd;
111}
112
113static void
114atenl_eeprom_init_chip_id(struct atenl *an)
115{
116 an->chip_id = *(u16 *)an->eeprom_data;
117
118 if (is_mt7915(an)) {
119 an->adie_id = 0x7975;
120 } else if (is_mt7916(an)) {
121 an->adie_id = 0x7976;
122 } else if (is_mt7986(an)) {
123 bool is_7975 = false;
124 u32 val;
125 u8 sub_id;
126
127 atenl_reg_read(an, 0x18050000, &val);
128
129 switch (val & 0xf) {
130 case MT7975_ONE_ADIE_SINGLE_BAND:
131 is_7975 = true;
132 /* fallthrough */
133 case MT7976_ONE_ADIE_SINGLE_BAND:
134 sub_id = 0xa;
135 break;
136 case MT7976_ONE_ADIE_DBDC:
137 sub_id = 0x7;
138 break;
139 case MT7975_DUAL_ADIE_DBDC:
140 is_7975 = true;
141 /* fallthrough */
142 case MT7976_DUAL_ADIE_DBDC:
143 default:
144 sub_id = 0xf;
145 break;
146 }
147
148 an->sub_chip_id = sub_id;
149 an->adie_id = is_7975 ? 0x7975 : 0x7976;
developerb9a96602023-01-10 19:53:25 +0800150 } else if (is_mt7996(an)) {
151 /* TODO: parse info if required */
developerb0c86782023-10-27 15:40:47 +0800152 } else if (is_mt7992(an)) {
153 /* TODO: parse info if required */
developer4616dbd2022-10-11 13:18:59 +0800154 }
155}
156
157static void
158atenl_eeprom_init_max_size(struct atenl *an)
159{
160 switch (an->chip_id) {
161 case 0x7915:
162 an->eeprom_size = 3584;
163 an->eeprom_prek_offs = 0x62;
164 break;
165 case 0x7906:
166 case 0x7916:
167 case 0x7986:
168 an->eeprom_size = 4096;
169 an->eeprom_prek_offs = 0x19a;
170 break;
developerb9a96602023-01-10 19:53:25 +0800171 case 0x7990:
developerb0c86782023-10-27 15:40:47 +0800172 case 0x7992:
developerb9a96602023-01-10 19:53:25 +0800173 an->eeprom_size = 7680;
174 an->eeprom_prek_offs = 0x1a5;
developer4616dbd2022-10-11 13:18:59 +0800175 default:
176 break;
177 }
178}
179
180static void
181atenl_eeprom_init_band_cap(struct atenl *an)
182{
developerb9a96602023-01-10 19:53:25 +0800183#define EAGLE_BAND_SEL(index) MT_EE_WIFI_EAGLE_CONF##index##_BAND_SEL
developer4616dbd2022-10-11 13:18:59 +0800184 u8 *eeprom = an->eeprom_data;
185
186 if (is_mt7915(an)) {
187 u8 val = eeprom[MT_EE_WIFI_CONF];
188 u8 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
189 struct atenl_band *anb = &an->anb[0];
190
191 /* MT7915A */
192 if (band_sel == MT_EE_BAND_SEL_DEFAULT) {
193 anb->valid = true;
194 anb->cap = BAND_TYPE_2G_5G;
195 return;
196 }
197
198 /* MT7915D */
199 if (band_sel == MT_EE_BAND_SEL_2GHZ) {
200 anb->valid = true;
201 anb->cap = BAND_TYPE_2G;
202 }
203
204 val = eeprom[MT_EE_WIFI_CONF + 1];
205 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
206 anb++;
207
208 if (band_sel == MT_EE_BAND_SEL_5GHZ) {
209 anb->valid = true;
210 anb->cap = BAND_TYPE_5G;
211 }
212 } else if (is_mt7916(an) || is_mt7986(an)) {
213 struct atenl_band *anb;
214 u8 val, band_sel;
215 int i;
216
217 for (i = 0; i < 2; i++) {
218 val = eeprom[MT_EE_WIFI_CONF + i];
219 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
220 anb = &an->anb[i];
221
222 anb->valid = true;
223 switch (band_sel) {
224 case MT_EE_BAND_SEL_2G:
225 anb->cap = BAND_TYPE_2G;
226 break;
227 case MT_EE_BAND_SEL_5G:
228 anb->cap = BAND_TYPE_5G;
229 break;
230 case MT_EE_BAND_SEL_6G:
231 anb->cap = BAND_TYPE_6G;
232 break;
233 case MT_EE_BAND_SEL_5G_6G:
234 anb->cap = BAND_TYPE_5G_6G;
235 break;
236 default:
237 break;
238 }
239 }
developerb9a96602023-01-10 19:53:25 +0800240 } else if (is_mt7996(an)) {
241 struct atenl_band *anb;
242 u8 val, band_sel;
243 u8 band_sel_mask[3] = {EAGLE_BAND_SEL(0), EAGLE_BAND_SEL(1),
244 EAGLE_BAND_SEL(2)};
245 int i;
246
247 for (i = 0; i < 3; i++) {
248 val = eeprom[MT_EE_WIFI_CONF + i];
249 band_sel = FIELD_GET(band_sel_mask[i], val);
250 anb = &an->anb[i];
251
252 anb->valid = true;
253 switch (band_sel) {
254 case MT_EE_EAGLE_BAND_SEL_2GHZ:
255 anb->cap = BAND_TYPE_2G;
256 break;
257 case MT_EE_EAGLE_BAND_SEL_5GHZ:
258 anb->cap = BAND_TYPE_5G;
259 break;
260 case MT_EE_EAGLE_BAND_SEL_6GHZ:
261 anb->cap = BAND_TYPE_6G;
262 break;
263 case MT_EE_EAGLE_BAND_SEL_5GHZ_6GHZ:
264 anb->cap = BAND_TYPE_5G_6G;
265 break;
266 default:
267 break;
268 }
269 }
developerb0c86782023-10-27 15:40:47 +0800270 } else if (is_mt7992(an)) {
271 struct atenl_band *anb;
272 u8 val, band_sel;
273 u8 band_sel_mask[2] = {EAGLE_BAND_SEL(0), EAGLE_BAND_SEL(1)};
274 int i;
275
276 for (i = 0; i < 2; i++) {
277 val = eeprom[MT_EE_WIFI_CONF + i];
278 band_sel = FIELD_GET(band_sel_mask[i], val);
279 anb = &an->anb[i];
280
281 anb->valid = true;
282 switch (band_sel) {
283 case MT_EE_EAGLE_BAND_SEL_2GHZ:
284 anb->cap = BAND_TYPE_2G;
285 break;
286 case MT_EE_EAGLE_BAND_SEL_5GHZ:
287 anb->cap = BAND_TYPE_5G;
288 break;
289 case MT_EE_EAGLE_BAND_SEL_6GHZ:
290 anb->cap = BAND_TYPE_6G;
291 break;
292 case MT_EE_EAGLE_BAND_SEL_5GHZ_6GHZ:
293 anb->cap = BAND_TYPE_5G_6G;
294 break;
295 default:
296 break;
297 }
298 }
developer4616dbd2022-10-11 13:18:59 +0800299 }
300}
301
302static void
303atenl_eeprom_init_antenna_cap(struct atenl *an)
304{
developerb0c86782023-10-27 15:40:47 +0800305 switch (an->chip_id) {
306 case 0x7915:
developer4616dbd2022-10-11 13:18:59 +0800307 if (an->anb[0].cap == BAND_TYPE_2G_5G)
308 an->anb[0].chainmask = 0xf;
309 else {
310 an->anb[0].chainmask = 0x3;
311 an->anb[1].chainmask = 0xc;
312 }
developerb0c86782023-10-27 15:40:47 +0800313 break;
314 case 0x7906:
315 case 0x7916:
developer4616dbd2022-10-11 13:18:59 +0800316 an->anb[0].chainmask = 0x3;
317 an->anb[1].chainmask = 0x3;
developerb0c86782023-10-27 15:40:47 +0800318 break;
319 case 0x7986:
developer4616dbd2022-10-11 13:18:59 +0800320 an->anb[0].chainmask = 0xf;
321 an->anb[1].chainmask = 0xf;
developerb0c86782023-10-27 15:40:47 +0800322 break;
323 case 0x7990:
developerb9a96602023-01-10 19:53:25 +0800324 an->anb[0].chainmask = 0xf;
325 an->anb[1].chainmask = 0xf;
326 an->anb[2].chainmask = 0xf;
developerb0c86782023-10-27 15:40:47 +0800327 break;
328 case 0x7992:
329 an->anb[0].chainmask = 0xf;
330 an->anb[1].chainmask = 0xf;
331 break;
332 default:
333 break;
developer4616dbd2022-10-11 13:18:59 +0800334 }
335}
336
337int atenl_eeprom_init(struct atenl *an, u8 phy_idx)
338{
339 bool flash_mode;
340 int eeprom_fd;
341 char buf[30];
342 u8 main_phy_idx = phy_idx;
343
344 set_band_val(an, 0, phy_idx, phy_idx);
345 atenl_nl_check_mtd(an);
346 flash_mode = an->mtd_part != NULL;
347
developerb9a96602023-01-10 19:53:25 +0800348 // Get the first main phy index for this chip
developer4616dbd2022-10-11 13:18:59 +0800349 if (flash_mode)
developerb9a96602023-01-10 19:53:25 +0800350 main_phy_idx -= an->band_idx;
developer4616dbd2022-10-11 13:18:59 +0800351
352 snprintf(buf, sizeof(buf), "/tmp/atenl-eeprom-phy%u", main_phy_idx);
353 eeprom_file = strdup(buf);
354
355 eeprom_fd = atenl_eeprom_init_file(an, flash_mode);
356 if (eeprom_fd < 0)
357 return -1;
358
359 an->eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
360 MAP_SHARED, eeprom_fd, 0);
361 if (!an->eeprom_data) {
362 perror("mmap");
363 close(eeprom_fd);
364 return -1;
365 }
366
367 an->eeprom_fd = eeprom_fd;
368 atenl_eeprom_init_chip_id(an);
369 atenl_eeprom_init_max_size(an);
370 atenl_eeprom_init_band_cap(an);
371 atenl_eeprom_init_antenna_cap(an);
372
373 if (get_band_val(an, 1, valid))
374 set_band_val(an, 1, phy_idx, phy_idx + 1);
375
developerad9333b2023-05-22 15:16:16 +0800376 if (get_band_val(an, 2, valid))
377 set_band_val(an, 2, phy_idx, phy_idx + 2);
378
developer4616dbd2022-10-11 13:18:59 +0800379 return 0;
380}
381
382void atenl_eeprom_close(struct atenl *an)
383{
384 msync(an->eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
385 munmap(an->eeprom_data, EEPROM_PART_SIZE);
386 close(an->eeprom_fd);
387
388 if (!an->cmd_mode) {
389 if (remove(eeprom_file))
390 perror("remove");
391 }
392
393 free(eeprom_file);
394}
395
396int atenl_eeprom_update_precal(struct atenl *an, int write_offs, int size)
397{
398 u32 offs = an->eeprom_prek_offs;
399 u8 cal_indicator, *eeprom, *pre_cal;
400
401 if (!an->cal && !an->cal_info)
402 return 0;
403
404 eeprom = an->eeprom_data;
405 pre_cal = eeprom + an->eeprom_size;
406 cal_indicator = an->cal_info[4];
407
408 memcpy(eeprom + offs, &cal_indicator, sizeof(u8));
409 memcpy(pre_cal, an->cal_info, PRE_CAL_INFO);
410 pre_cal += (PRE_CAL_INFO + write_offs);
411
412 if (an->cal)
413 memcpy(pre_cal, an->cal, size);
414 else
415 memset(pre_cal, 0, size);
416
417 return 0;
418}
419
420int atenl_eeprom_write_mtd(struct atenl *an)
421{
422 bool flash_mode = an->mtd_part != NULL;
423 pid_t pid;
424 char offset[10];
425
426 if (!flash_mode)
427 return 0;
428
429 pid = fork();
430 if (pid < 0) {
431 perror("Fork");
432 return EXIT_FAILURE;
433 } else if (pid == 0) {
434 int ret;
435 char *part = strdup(an->mtd_part);
436 snprintf(offset, sizeof(offset), "%d", an->mtd_offset);
437 char *cmd[] = {"mtd", "-p", offset, "write", eeprom_file, part, NULL};
438
439 ret = execvp("mtd", cmd);
440 if (ret < 0) {
441 atenl_err("%s: exec error\n", __func__);
442 exit(0);
443 }
444 } else {
445 wait(&pid);
446 }
447
448 return 0;
449}
450
451/* Directly read values from driver's eeprom.
452 * It's usally used to get calibrated data from driver.
453 */
454int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
455{
456 u8 *eeprom_data = an->eeprom_data + offset;
457 char fname[64], buf[1024];
458 int fd_ori, ret;
459 ssize_t rd;
460
461 snprintf(fname, sizeof(fname),
462 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
463 get_band_val(an, 0, phy_idx));
464 fd_ori = open(fname, O_RDONLY);
465 if (fd_ori < 0)
466 return -1;
467
468 ret = lseek(fd_ori, offset, SEEK_SET);
469 if (ret < 0)
470 goto out;
471
472 while ((rd = read(fd_ori, buf, sizeof(buf))) > 0 && len) {
473 if (len < rd) {
474 memcpy(eeprom_data, buf, len);
475 break;
476 } else {
477 memcpy(eeprom_data, buf, rd);
478 eeprom_data += rd;
479 len -= rd;
480 }
481 }
482
483 ret = 0;
484out:
485 close(fd_ori);
486 return ret;
487}
488
489/* Update all eeprom values to driver before writing efuse */
490static void
491atenl_eeprom_sync_to_driver(struct atenl *an)
492{
493 int i;
494
495 for (i = 0; i < an->eeprom_size; i += 16)
496 atenl_nl_write_eeprom(an, i, &an->eeprom_data[i], 16);
497}
498
499void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd)
500{
501 bool flash_mode;
502
503 an->cmd_mode = true;
504
505 atenl_eeprom_init(an, phy_idx);
506 flash_mode = an->mtd_part != NULL;
507
508 if (!strncmp(cmd, "sync eeprom all", 15)) {
509 atenl_eeprom_write_mtd(an);
510 } else if (!strncmp(cmd, "eeprom", 6)) {
511 char *s = strchr(cmd, ' ');
512
513 if (!s) {
514 atenl_err("eeprom: please type a correct command\n");
515 return;
516 }
517
518 s++;
519 if (!strncmp(s, "reset", 5)) {
520 unlink(eeprom_file);
521 } else if (!strncmp(s, "file", 4)) {
522 atenl_info("%s\n", eeprom_file);
523 atenl_info("Flash mode: %d\n", flash_mode);
524 } else if (!strncmp(s, "set", 3)) {
525 u32 offset, val;
526
527 s = strchr(s, ' ');
528 if (!s)
529 return;
530 s++;
531
532 if (!sscanf(s, "%x=%x", &offset, &val) ||
533 offset > EEPROM_PART_SIZE)
534 return;
535
536 an->eeprom_data[offset] = val;
537 atenl_info("set offset 0x%x to 0x%x\n", offset, val);
538 } else if (!strncmp(s, "update buffermode", 17)) {
539 atenl_eeprom_sync_to_driver(an);
540 atenl_nl_update_buffer_mode(an);
541 } else if (!strncmp(s, "write", 5)) {
542 s = strchr(s, ' ');
543 if (!s)
544 return;
545 s++;
546
547 if (!strncmp(s, "flash", 5)) {
548 atenl_eeprom_write_mtd(an);
developerb0c86782023-10-27 15:40:47 +0800549 } else if (!strncmp(s, "to efuse", 8)) {
550 atenl_eeprom_sync_to_driver(an);
551 atenl_nl_write_efuse_all(an);
552 }
developer4616dbd2022-10-11 13:18:59 +0800553 } else if (!strncmp(s, "read", 4)) {
554 u32 offset;
555
556 s = strchr(s, ' ');
557 if (!s)
558 return;
559 s++;
560
561 if (!sscanf(s, "%x", &offset) ||
562 offset > EEPROM_PART_SIZE)
563 return;
564
565 atenl_info("val = 0x%x (%u)\n", an->eeprom_data[offset],
566 an->eeprom_data[offset]);
567 } else if (!strncmp(s, "precal", 6)) {
568 s = strchr(s, ' ');
569 if (!s)
570 return;
571 s++;
572
573 if (!strncmp(s, "sync group", 10)) {
574 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_GROUP);
575 } else if (!strncmp(s, "sync dpd 2g", 11)) {
576 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_2G);
577 } else if (!strncmp(s, "sync dpd 5g", 11)) {
578 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_5G);
579 } else if (!strncmp(s, "sync dpd 6g", 11)) {
580 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_6G);
581 } else if (!strncmp(s, "group clean", 11)) {
582 atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_GROUP);
583 } else if (!strncmp(s, "dpd clean", 9)) {
584 atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_DPD);
585 } else if (!strncmp(s, "sync", 4)) {
586 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_ALL);
587 }
developer26d6cc52023-07-31 12:27:06 +0800588 } else if (!strncmp(s, "ibf sync", 8)) {
589 atenl_get_ibf_cal_result(an);
developer4616dbd2022-10-11 13:18:59 +0800590 } else {
developer26d6cc52023-07-31 12:27:06 +0800591 atenl_err("Unknown eeprom command: %s\n", cmd);
592 }
developer4616dbd2022-10-11 13:18:59 +0800593 } else {
594 atenl_err("Unknown command: %s\n", cmd);
595 }
596}