blob: 1c1cb6c4da66b26436a36c91adc9b30cafef2a11 [file] [log] [blame]
developerb9b4cd12022-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
9#define EEPROM_PART_SIZE 0x64000
10char *eeprom_file;
11
12static FILE *mtd_open(const char *mtd)
13{
14 char line[128], name[64];
15 FILE *fp;
16 int i;
17
18 fp = fopen("/proc/mtd", "r");
19 if (!fp)
20 return NULL;
21
22 snprintf(name, sizeof(name), "\"%s\"", mtd);
23 while (fgets(line, sizeof(line), fp)) {
24 if (!sscanf(line, "mtd%d:", &i) || !strstr(line, name))
25 continue;
26
27 snprintf(line, sizeof(line), "/dev/mtd%d", i);
28 fclose(fp);
29 return fopen(line, "r");
30 }
31 fclose(fp);
32
33 return NULL;
34}
35
36static int
37atenl_flash_create_file(struct atenl *an)
38{
39#define READ_LEN_LIMIT 0x64000
40 char buf[1024];
41 ssize_t len, limit = 0;
42 FILE *f;
43 int fd, ret;
44
45 f = mtd_open(an->mtd_part);
46 if (!f) {
47 atenl_err("Failed to open MTD device\n");
48 return -1;
49 }
50 fseek(f, an->mtd_offset, SEEK_SET);
51
52 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
53 if (fd < 0)
54 goto out;
55
56 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
57 ssize_t w;
58
59retry:
60 w = write(fd, buf, len);
61 if (w > 0) {
62 limit += len;
63
64 if (limit >= READ_LEN_LIMIT)
65 break;
66 continue;
67 }
68
69 if (errno == EINTR)
70 goto retry;
71
72 perror("write");
73 unlink(eeprom_file);
74 close(fd);
75 fd = -1;
76 goto out;
77 }
78
79 ret = lseek(fd, 0, SEEK_SET);
80 if (ret) {
81 fclose(f);
82 close(fd);
83 return ret;
84 }
85
86out:
87 fclose(f);
88 return fd;
89}
90
91static int
92atenl_efuse_create_file(struct atenl *an)
93{
94 char fname[64], buf[1024];
95 ssize_t len;
96 int fd_ori, fd, ret;
97
98 snprintf(fname, sizeof(fname),
99 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom", get_band_val(an, 0, phy_idx));
100 fd_ori = open(fname, O_RDONLY);
101 if (fd_ori < 0)
102 return -1;
103
104 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
105 if (fd < 0)
106 goto out;
107
108 while ((len = read(fd_ori, buf, sizeof(buf))) > 0) {
109 ssize_t w;
110
111retry:
112 w = write(fd, buf, len);
113 if (w > 0)
114 continue;
115
116 if (errno == EINTR)
117 goto retry;
118
119 perror("write");
120 unlink(eeprom_file);
121 close(fd);
122 fd = -1;
123 goto out;
124 }
125
126 ret = lseek(fd, 0, SEEK_SET);
127 if (ret) {
128 close(fd_ori);
129 close(fd);
130 return ret;
131 }
132
133out:
134 close(fd_ori);
135 return fd;
136}
137
138static bool
139atenl_eeprom_file_exists(void)
140{
141 struct stat st;
142
143 return stat(eeprom_file, &st) == 0;
144}
145
146static int
147atenl_eeprom_init_file(struct atenl *an, bool flash_mode)
148{
149 int fd;
150
151 if (!atenl_eeprom_file_exists()) {
152 if (flash_mode)
153 atenl_dbg("%s: init eeprom with flash mode\n", __func__);
154 else
155 atenl_dbg("%s: init eeprom with efuse mode\n", __func__);
156
157 if (flash_mode)
158 return atenl_flash_create_file(an);
159
160 return atenl_efuse_create_file(an);
161 }
162
163 fd = open(eeprom_file, O_RDWR);
164 if (fd < 0)
165 perror("open");
166
167 return fd;
168}
169
170static void
171atenl_eeprom_init_chip_id(struct atenl *an)
172{
173 an->chip_id = *(u16 *)an->eeprom_data;
174
175 if (is_mt7915(an)) {
176 an->adie_id = 0x7975;
177 } else if (is_mt7916(an)) {
178 an->adie_id = 0x7976;
179 } else if (is_mt7986(an)) {
180 bool is_7975 = false;
181 u32 val;
182 u8 sub_id;
183
184 atenl_reg_read(an, 0x18050000, &val);
185
186 switch (val & 0xf) {
187 case MT7975_ONE_ADIE_SINGLE_BAND:
188 is_7975 = true;
189 /* fallthrough */
190 case MT7976_ONE_ADIE_SINGLE_BAND:
191 sub_id = 0xa;
192 break;
193 case MT7976_ONE_ADIE_DBDC:
194 sub_id = 0x7;
195 break;
196 case MT7975_DUAL_ADIE_DBDC:
197 is_7975 = true;
198 /* fallthrough */
199 case MT7976_DUAL_ADIE_DBDC:
200 default:
201 sub_id = 0xf;
202 break;
203 }
204
205 an->sub_chip_id = sub_id;
206 an->adie_id = is_7975 ? 0x7975 : 0x7976;
207 }
208}
209
210static void
211atenl_eeprom_init_max_size(struct atenl *an)
212{
213 switch (an->chip_id) {
214 case 0x7915:
215 an->eeprom_size = 3584;
216 an->eeprom_prek_offs = 0x62;
217 break;
218 case 0x7906:
219 case 0x7916:
220 case 0x7986:
221 an->eeprom_size = 4096;
222 an->eeprom_prek_offs = 0x19a;
223 break;
224 default:
225 break;
226 }
227}
228
229static void
230atenl_eeprom_init_band_cap(struct atenl *an)
231{
232 u8 *eeprom = an->eeprom_data;
233
234 if (is_mt7915(an)) {
235 u8 val = eeprom[MT_EE_WIFI_CONF];
236 u8 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
237 struct atenl_band *anb = &an->anb[0];
238
239 /* MT7915A */
240 if (band_sel == MT_EE_BAND_SEL_DEFAULT) {
241 anb->valid = true;
242 anb->cap = BAND_TYPE_2G_5G;
243 return;
244 }
245
246 /* MT7915D */
247 if (band_sel == MT_EE_BAND_SEL_2GHZ) {
248 anb->valid = true;
249 anb->cap = BAND_TYPE_2G;
250 }
251
252 val = eeprom[MT_EE_WIFI_CONF + 1];
253 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
254 anb++;
255
256 if (band_sel == MT_EE_BAND_SEL_5GHZ) {
257 anb->valid = true;
258 anb->cap = BAND_TYPE_5G;
259 }
260 } else if (is_mt7916(an) || is_mt7986(an)) {
261 struct atenl_band *anb;
262 u8 val, band_sel;
263 int i;
264
265 for (i = 0; i < 2; i++) {
266 val = eeprom[MT_EE_WIFI_CONF + i];
267 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
268 anb = &an->anb[i];
269
270 anb->valid = true;
271 switch (band_sel) {
272 case MT_EE_BAND_SEL_2G:
273 anb->cap = BAND_TYPE_2G;
274 break;
275 case MT_EE_BAND_SEL_5G:
276 anb->cap = BAND_TYPE_5G;
277 break;
278 case MT_EE_BAND_SEL_6G:
279 anb->cap = BAND_TYPE_6G;
280 break;
281 case MT_EE_BAND_SEL_5G_6G:
282 anb->cap = BAND_TYPE_5G_6G;
283 break;
284 default:
285 break;
286 }
287 }
288 }
289}
290
291static void
292atenl_eeprom_init_antenna_cap(struct atenl *an)
293{
294 if (is_mt7915(an)) {
295 if (an->anb[0].cap == BAND_TYPE_2G_5G)
296 an->anb[0].chainmask = 0xf;
297 else {
298 an->anb[0].chainmask = 0x3;
299 an->anb[1].chainmask = 0xc;
300 }
301 } else if (is_mt7916(an)) {
302 an->anb[0].chainmask = 0x3;
303 an->anb[1].chainmask = 0x3;
304 } else if (is_mt7986(an)) {
305 an->anb[0].chainmask = 0xf;
306 an->anb[1].chainmask = 0xf;
307 }
308}
309
310int atenl_eeprom_init(struct atenl *an, u8 phy_idx)
311{
312 bool flash_mode;
313 int eeprom_fd;
314 char buf[30];
315 u8 main_phy_idx = phy_idx;
316
317 set_band_val(an, 0, phy_idx, phy_idx);
318 atenl_nl_check_mtd(an);
319 flash_mode = an->mtd_part != NULL;
320
321 if (flash_mode)
322 main_phy_idx = an->is_main_phy ? main_phy_idx : (main_phy_idx - 1);
323
324 snprintf(buf, sizeof(buf), "/tmp/atenl-eeprom-phy%u", main_phy_idx);
325 eeprom_file = strdup(buf);
326
327 eeprom_fd = atenl_eeprom_init_file(an, flash_mode);
328 if (eeprom_fd < 0)
329 return -1;
330
331 an->eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
332 MAP_SHARED, eeprom_fd, 0);
333 if (!an->eeprom_data) {
334 perror("mmap");
335 close(eeprom_fd);
336 return -1;
337 }
338
339 an->eeprom_fd = eeprom_fd;
340 atenl_eeprom_init_chip_id(an);
341 atenl_eeprom_init_max_size(an);
342 atenl_eeprom_init_band_cap(an);
343 atenl_eeprom_init_antenna_cap(an);
344
345 if (get_band_val(an, 1, valid))
346 set_band_val(an, 1, phy_idx, phy_idx + 1);
347
348 return 0;
349}
350
351void atenl_eeprom_close(struct atenl *an)
352{
353 msync(an->eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
354 munmap(an->eeprom_data, EEPROM_PART_SIZE);
355 close(an->eeprom_fd);
356
357 if (!an->cmd_mode) {
358 if (remove(eeprom_file))
359 perror("remove");
360 }
361
362 free(eeprom_file);
363}
364
365int atenl_eeprom_update_precal(struct atenl *an, int write_offs, int size)
366{
367 u32 offs = an->eeprom_prek_offs;
368 u8 cal_indicator, *eeprom, *pre_cal;
369
370 if (!an->cal && !an->cal_info)
371 return 0;
372
373 eeprom = an->eeprom_data;
374 pre_cal = eeprom + an->eeprom_size;
375 cal_indicator = an->cal_info[4];
376
377 memcpy(eeprom + offs, &cal_indicator, sizeof(u8));
378 memcpy(pre_cal, an->cal_info, PRE_CAL_INFO);
379 pre_cal += (PRE_CAL_INFO + write_offs);
380
381 if (an->cal)
382 memcpy(pre_cal, an->cal, size);
383 else
384 memset(pre_cal, 0, size);
385
386 return 0;
387}
388
389int atenl_eeprom_write_mtd(struct atenl *an)
390{
391 bool flash_mode = an->mtd_part != NULL;
392 pid_t pid;
393 char offset[10];
394
395 if (!flash_mode)
396 return 0;
397
398 pid = fork();
399 if (pid < 0) {
400 perror("Fork");
401 return EXIT_FAILURE;
402 } else if (pid == 0) {
403 int ret;
404 char *part = strdup(an->mtd_part);
405 snprintf(offset, sizeof(offset), "%d", an->mtd_offset);
406 char *cmd[] = {"mtd", "-p", offset, "write", eeprom_file, part, NULL};
407
408 ret = execvp("mtd", cmd);
409 if (ret < 0) {
410 atenl_err("%s: exec error\n", __func__);
411 exit(0);
412 }
413 } else {
414 wait(&pid);
415 }
416
417 return 0;
418}
419
420/* Directly read values from driver's eeprom.
421 * It's usally used to get calibrated data from driver.
422 */
423int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
424{
425 u8 *eeprom_data = an->eeprom_data + offset;
426 char fname[64], buf[1024];
427 int fd_ori, ret;
428 ssize_t rd;
429
430 snprintf(fname, sizeof(fname),
431 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
432 get_band_val(an, 0, phy_idx));
433 fd_ori = open(fname, O_RDONLY);
434 if (fd_ori < 0)
435 return -1;
436
437 ret = lseek(fd_ori, offset, SEEK_SET);
438 if (ret < 0)
439 goto out;
440
441 while ((rd = read(fd_ori, buf, sizeof(buf))) > 0 && len) {
442 if (len < rd) {
443 memcpy(eeprom_data, buf, len);
444 break;
445 } else {
446 memcpy(eeprom_data, buf, rd);
447 eeprom_data += rd;
448 len -= rd;
449 }
450 }
451
452 ret = 0;
453out:
454 close(fd_ori);
455 return ret;
456}
457
458/* Update all eeprom values to driver before writing efuse */
459static void
460atenl_eeprom_sync_to_driver(struct atenl *an)
461{
462 int i;
463
464 for (i = 0; i < an->eeprom_size; i += 16)
465 atenl_nl_write_eeprom(an, i, &an->eeprom_data[i], 16);
466}
467
468void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd)
469{
470 bool flash_mode;
471
472 an->cmd_mode = true;
473
474 atenl_eeprom_init(an, phy_idx);
475 flash_mode = an->mtd_part != NULL;
476
477 if (!strncmp(cmd, "sync eeprom all", 15)) {
478 atenl_eeprom_write_mtd(an);
479 } else if (!strncmp(cmd, "eeprom", 6)) {
480 char *s = strchr(cmd, ' ');
481
482 if (!s) {
483 atenl_err("eeprom: please type a correct command\n");
484 return;
485 }
486
487 s++;
488 if (!strncmp(s, "reset", 5)) {
489 unlink(eeprom_file);
490 } else if (!strncmp(s, "file", 4)) {
491 atenl_info("%s\n", eeprom_file);
492 atenl_info("Flash mode: %d\n", flash_mode);
493 } else if (!strncmp(s, "set", 3)) {
494 u32 offset, val;
495
496 s = strchr(s, ' ');
497 if (!s)
498 return;
499 s++;
500
501 if (!sscanf(s, "%x=%x", &offset, &val) ||
502 offset > EEPROM_PART_SIZE)
503 return;
504
505 an->eeprom_data[offset] = val;
506 atenl_info("set offset 0x%x to 0x%x\n", offset, val);
507 } else if (!strncmp(s, "update buffermode", 17)) {
508 atenl_eeprom_sync_to_driver(an);
509 atenl_nl_update_buffer_mode(an);
510 } else if (!strncmp(s, "write", 5)) {
511 s = strchr(s, ' ');
512 if (!s)
513 return;
514 s++;
515
516 if (!strncmp(s, "flash", 5)) {
517 atenl_eeprom_write_mtd(an);
518 } else if (!strncmp(s, "to efuse", 8)) {
519 atenl_eeprom_sync_to_driver(an);
520 atenl_nl_write_efuse_all(an);
521 }
522 } else if (!strncmp(s, "read", 4)) {
523 u32 offset;
524
525 s = strchr(s, ' ');
526 if (!s)
527 return;
528 s++;
529
530 if (!sscanf(s, "%x", &offset) ||
531 offset > EEPROM_PART_SIZE)
532 return;
533
534 atenl_info("val = 0x%x (%u)\n", an->eeprom_data[offset],
535 an->eeprom_data[offset]);
536 } else if (!strncmp(s, "precal", 6)) {
537 s = strchr(s, ' ');
538 if (!s)
539 return;
540 s++;
541
542 if (!strncmp(s, "sync group", 10)) {
543 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_GROUP);
544 } else if (!strncmp(s, "sync dpd 2g", 11)) {
545 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_2G);
546 } else if (!strncmp(s, "sync dpd 5g", 11)) {
547 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_5G);
548 } else if (!strncmp(s, "sync dpd 6g", 11)) {
549 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_DPD_6G);
550 } else if (!strncmp(s, "group clean", 11)) {
551 atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_GROUP);
552 } else if (!strncmp(s, "dpd clean", 9)) {
553 atenl_nl_precal_sync_from_driver(an, PREK_CLEAN_DPD);
554 } else if (!strncmp(s, "sync", 4)) {
555 atenl_nl_precal_sync_from_driver(an, PREK_SYNC_ALL);
556 }
557 } else {
558 atenl_err("Unknown eeprom command: %s\n", cmd);
559 }
560 } else {
561 atenl_err("Unknown command: %s\n", cmd);
562 }
563}