blob: ee77ed77100a70707969195a21cb6a6eec605e69 [file] [log] [blame]
developer22c7ab62022-01-24 11:13:32 +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 20480
developer2bf395f2022-01-26 20:50:22 +080010char *eeprom_file;
developer22c7ab62022-01-24 11:13:32 +080011
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{
developerf30d4472022-05-30 16:40:23 +080039#define READ_LEN_LIMIT 20000
developer22c7ab62022-01-24 11:13:32 +080040 char buf[1024];
developerf30d4472022-05-30 16:40:23 +080041 ssize_t len, limit = 0;
developer22c7ab62022-01-24 11:13:32 +080042 FILE *f;
43 int fd, ret;
44
45 f = mtd_open(an->mtd_part);
46 if (!f) {
developerf30d4472022-05-30 16:40:23 +080047 atenl_err("Failed to open MTD device\n");
developer22c7ab62022-01-24 11:13:32 +080048 return -1;
49 }
50
developer2bf395f2022-01-26 20:50:22 +080051 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
developer22c7ab62022-01-24 11:13:32 +080052 if (fd < 0)
53 goto out;
54
55 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
56 ssize_t w;
57
58retry:
59 w = write(fd, buf, len);
developerf30d4472022-05-30 16:40:23 +080060 if (w > 0) {
61 limit += len;
62
63 if (limit >= READ_LEN_LIMIT)
64 break;
developer22c7ab62022-01-24 11:13:32 +080065 continue;
developerf30d4472022-05-30 16:40:23 +080066 }
developer22c7ab62022-01-24 11:13:32 +080067
68 if (errno == EINTR)
69 goto retry;
70
71 perror("write");
developer2bf395f2022-01-26 20:50:22 +080072 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +080073 close(fd);
74 fd = -1;
75 goto out;
76 }
77
78 ret = lseek(fd, 0, SEEK_SET);
79 if (ret) {
80 fclose(f);
81 close(fd);
82 return ret;
83 }
84
85out:
86 fclose(f);
87 return fd;
88}
89
90static int
91atenl_efuse_create_file(struct atenl *an)
92{
93 char fname[64], buf[1024];
94 ssize_t len;
95 int fd_ori, fd, ret;
96
97 snprintf(fname, sizeof(fname),
98 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom", get_band_val(an, 0, phy_idx));
99 fd_ori = open(fname, O_RDONLY);
100 if (fd_ori < 0)
101 return -1;
102
developer2bf395f2022-01-26 20:50:22 +0800103 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
developer22c7ab62022-01-24 11:13:32 +0800104 if (fd < 0)
105 goto out;
106
107 while ((len = read(fd_ori, buf, sizeof(buf))) > 0) {
108 ssize_t w;
109
110retry:
111 w = write(fd, buf, len);
112 if (w > 0)
113 continue;
114
115 if (errno == EINTR)
116 goto retry;
117
118 perror("write");
developer2bf395f2022-01-26 20:50:22 +0800119 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800120 close(fd);
121 fd = -1;
122 goto out;
123 }
124
125 ret = lseek(fd, 0, SEEK_SET);
126 if (ret) {
127 close(fd_ori);
128 close(fd);
129 return ret;
130 }
131
132out:
133 close(fd_ori);
134 return fd;
135}
136
137static bool
138atenl_eeprom_file_exists(void)
139{
140 struct stat st;
141
developer2bf395f2022-01-26 20:50:22 +0800142 return stat(eeprom_file, &st) == 0;
developer22c7ab62022-01-24 11:13:32 +0800143}
144
145static int
146atenl_eeprom_init_file(struct atenl *an, bool flash_mode)
147{
148 int fd;
149
150 if (!atenl_eeprom_file_exists()) {
151 if (flash_mode)
developerf30d4472022-05-30 16:40:23 +0800152 atenl_dbg("%s: init eeprom with flash mode\n", __func__);
developer22c7ab62022-01-24 11:13:32 +0800153 else
developerf30d4472022-05-30 16:40:23 +0800154 atenl_dbg("%s: init eeprom with efuse mode\n", __func__);
developer22c7ab62022-01-24 11:13:32 +0800155
156 if (flash_mode)
157 return atenl_flash_create_file(an);
158
159 return atenl_efuse_create_file(an);
160 }
161
developer2bf395f2022-01-26 20:50:22 +0800162 fd = open(eeprom_file, O_RDWR);
developer22c7ab62022-01-24 11:13:32 +0800163 if (fd < 0)
164 perror("open");
165
developer22c7ab62022-01-24 11:13:32 +0800166 return fd;
167}
168
169static void
developerf30d4472022-05-30 16:40:23 +0800170atenl_eeprom_init_chip_id(struct atenl *an)
171{
172 an->chip_id = *(u16 *)an->eeprom_data;
173
174 if (is_mt7915(an)) {
175 an->adie_id = 0x7975;
176 } else if (is_mt7916(an)) {
177 an->adie_id = 0x7976;
178 } else if (is_mt7986(an)) {
179 bool is_7975 = false;
180 u32 val;
181 u8 sub_id;
182
183 atenl_reg_read(an, 0x18050000, &val);
184
185 switch (val & 0xf) {
186 case MT7975_ONE_ADIE_SINGLE_BAND:
187 is_7975 = true;
188 /* fallthrough */
189 case MT7976_ONE_ADIE_SINGLE_BAND:
190 sub_id = 0xa;
191 break;
192 case MT7976_ONE_ADIE_DBDC:
193 sub_id = 0x7;
194 break;
195 case MT7975_DUAL_ADIE_DBDC:
196 is_7975 = true;
197 /* fallthrough */
198 case MT7976_DUAL_ADIE_DBDC:
199 default:
200 sub_id = 0xf;
201 break;
202 }
203
204 an->sub_chip_id = sub_id;
205 an->adie_id = is_7975 ? 0x7975 : 0x7976;
206 }
207}
208
209static void
developer22c7ab62022-01-24 11:13:32 +0800210atenl_eeprom_init_max_size(struct atenl *an)
211{
212 switch (an->chip_id) {
213 case 0x7915:
214 an->eeprom_size = 3584;
215 break;
216 case 0x7906:
217 case 0x7916:
218 case 0x7986:
219 an->eeprom_size = 4096;
220 break;
221 default:
222 break;
223 }
224}
225
226static void
227atenl_eeprom_init_band_cap(struct atenl *an)
228{
229 u8 *eeprom = an->eeprom_data;
230
231 if (is_mt7915(an)) {
232 u8 val = eeprom[MT_EE_WIFI_CONF];
233 u8 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
234 struct atenl_band *anb = &an->anb[0];
235
236 /* MT7915A */
237 if (band_sel == MT_EE_BAND_SEL_DEFAULT) {
238 anb->valid = true;
239 anb->cap = BAND_TYPE_2G_5G;
240 return;
241 }
242
243 /* MT7915D */
244 if (band_sel == MT_EE_BAND_SEL_2GHZ) {
245 anb->valid = true;
246 anb->cap = BAND_TYPE_2G;
247 }
248
249 val = eeprom[MT_EE_WIFI_CONF + 1];
250 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
251 anb++;
252
253 if (band_sel == MT_EE_BAND_SEL_5GHZ) {
254 anb->valid = true;
255 anb->cap = BAND_TYPE_5G;
256 }
257 } else if (is_mt7916(an) || is_mt7986(an)) {
258 struct atenl_band *anb;
259 u8 val, band_sel;
260 int i;
261
262 for (i = 0; i < 2; i++) {
263 val = eeprom[MT_EE_WIFI_CONF + i];
264 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
265 anb = &an->anb[i];
266
267 anb->valid = true;
268 switch (band_sel) {
269 case MT_EE_BAND_SEL_2G:
270 anb->cap = BAND_TYPE_2G;
271 break;
272 case MT_EE_BAND_SEL_5G:
273 anb->cap = BAND_TYPE_5G;
274 break;
275 case MT_EE_BAND_SEL_6G:
276 anb->cap = BAND_TYPE_6G;
277 break;
278 case MT_EE_BAND_SEL_5G_6G:
279 anb->cap = BAND_TYPE_5G_6G;
280 break;
281 default:
282 break;
283 }
284 }
285 }
286}
287
288static void
289atenl_eeprom_init_antenna_cap(struct atenl *an)
290{
291 if (is_mt7915(an)) {
292 if (an->anb[0].cap == BAND_TYPE_2G_5G)
293 an->anb[0].chainmask = 0xf;
294 else {
295 an->anb[0].chainmask = 0x3;
296 an->anb[1].chainmask = 0xc;
297 }
298 } else if (is_mt7916(an)) {
299 an->anb[0].chainmask = 0x3;
300 an->anb[1].chainmask = 0x3;
301 } else if (is_mt7986(an)) {
302 an->anb[0].chainmask = 0xf;
303 an->anb[1].chainmask = 0xf;
304 }
305}
306
307int atenl_eeprom_init(struct atenl *an, u8 phy_idx)
308{
309 bool flash_mode;
310 int eeprom_fd;
developer2bf395f2022-01-26 20:50:22 +0800311 char buf[30];
developer22c7ab62022-01-24 11:13:32 +0800312
313 set_band_val(an, 0, phy_idx, phy_idx);
developer2bf395f2022-01-26 20:50:22 +0800314 snprintf(buf, sizeof(buf), "/tmp/atenl-eeprom-phy%u", phy_idx);
315 eeprom_file = strdup(buf);
developer22c7ab62022-01-24 11:13:32 +0800316
317 atenl_nl_check_mtd(an);
318 flash_mode = an->mtd_part != NULL;
319
320 eeprom_fd = atenl_eeprom_init_file(an, flash_mode);
321 if (eeprom_fd < 0)
322 return -1;
323
324 an->eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
325 MAP_SHARED, eeprom_fd, an->mtd_offset);
326 if (!an->eeprom_data) {
327 perror("mmap");
328 close(eeprom_fd);
329 return -1;
330 }
331
332 an->eeprom_fd = eeprom_fd;
developerf30d4472022-05-30 16:40:23 +0800333 atenl_eeprom_init_chip_id(an);
developer22c7ab62022-01-24 11:13:32 +0800334 atenl_eeprom_init_max_size(an);
335 atenl_eeprom_init_band_cap(an);
336 atenl_eeprom_init_antenna_cap(an);
337
338 if (get_band_val(an, 1, valid))
339 set_band_val(an, 1, phy_idx, phy_idx + 1);
340
341 return 0;
342}
343
344void atenl_eeprom_close(struct atenl *an)
345{
346 msync(an->eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
347 munmap(an->eeprom_data, EEPROM_PART_SIZE);
348 close(an->eeprom_fd);
349
developerf30d4472022-05-30 16:40:23 +0800350 if (!an->cmd_mode) {
developer2bf395f2022-01-26 20:50:22 +0800351 if (remove(eeprom_file))
developer22c7ab62022-01-24 11:13:32 +0800352 perror("remove");
developer2bf395f2022-01-26 20:50:22 +0800353 }
354
355 free(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800356}
357
358int atenl_eeprom_write_mtd(struct atenl *an)
359{
360 bool flash_mode = an->mtd_part != NULL;
361 pid_t pid;
362
363 if (!flash_mode)
364 return 0;
365
366 pid = fork();
367 if (pid < 0) {
368 perror("Fork");
369 return EXIT_FAILURE;
370 } else if (pid == 0) {
371 char *part = strdup(an->mtd_part);
developer2bf395f2022-01-26 20:50:22 +0800372 char *cmd[] = {"mtd", "write", eeprom_file, part, NULL};
developer22c7ab62022-01-24 11:13:32 +0800373 int ret;
374
375 ret = execvp("mtd", cmd);
376 if (ret < 0) {
developerf30d4472022-05-30 16:40:23 +0800377 atenl_err("%s: exec error\n", __func__);
developer22c7ab62022-01-24 11:13:32 +0800378 exit(0);
379 }
380 } else {
381 wait(&pid);
382 }
383
384 return 0;
385}
386
developerf30d4472022-05-30 16:40:23 +0800387/* Directly read values from driver's eeprom.
developer22c7ab62022-01-24 11:13:32 +0800388 * It's usally used to get calibrated data from driver.
389 */
390int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
391{
392 u8 *eeprom_data = an->eeprom_data + offset;
393 char fname[64], buf[1024];
394 int fd_ori, ret;
395 ssize_t rd;
396
397 snprintf(fname, sizeof(fname),
398 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
399 get_band_val(an, 0, phy_idx));
400 fd_ori = open(fname, O_RDONLY);
401 if (fd_ori < 0)
402 return -1;
403
404 ret = lseek(fd_ori, offset, SEEK_SET);
405 if (ret < 0)
406 goto out;
407
408 while ((rd = read(fd_ori, buf, sizeof(buf))) > 0 && len) {
409 if (len < rd) {
410 memcpy(eeprom_data, buf, len);
411 break;
412 } else {
413 memcpy(eeprom_data, buf, rd);
414 eeprom_data += rd;
415 len -= rd;
416 }
417 }
418
419 ret = 0;
420out:
421 close(fd_ori);
422 return ret;
423}
424
425/* Update all eeprom values to driver before writing efuse */
426static void
427atenl_eeprom_sync_to_driver(struct atenl *an)
428{
429 int i;
430
developer2bf395f2022-01-26 20:50:22 +0800431 for (i = 0; i < an->eeprom_size; i += 16)
developer22c7ab62022-01-24 11:13:32 +0800432 atenl_nl_write_eeprom(an, i, &an->eeprom_data[i], 16);
433}
434
435void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd)
436{
437 bool flash_mode;
438
439 an->cmd_mode = true;
440
441 atenl_eeprom_init(an, phy_idx);
442 flash_mode = an->mtd_part != NULL;
443
444 if (!strncmp(cmd, "sync eeprom all", 15)) {
445 atenl_eeprom_write_mtd(an);
446 } else if (!strncmp(cmd, "eeprom", 6)) {
447 char *s = strchr(cmd, ' ');
448
449 if (!s) {
developerf30d4472022-05-30 16:40:23 +0800450 atenl_err("eeprom: please type a correct command\n");
developer22c7ab62022-01-24 11:13:32 +0800451 return;
452 }
453
454 s++;
455 if (!strncmp(s, "reset", 5)) {
developer2bf395f2022-01-26 20:50:22 +0800456 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800457 } else if (!strncmp(s, "file", 4)) {
developer2bf395f2022-01-26 20:50:22 +0800458 atenl_info("%s\n", eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800459 atenl_info("Flash mode: %d\n", flash_mode);
460 } else if (!strncmp(s, "set", 3)) {
461 u32 offset, val;
462
463 s = strchr(s, ' ');
464 if (!s)
465 return;
466 s++;
467
468 if (!sscanf(s, "%x=%x", &offset, &val) ||
469 offset > EEPROM_PART_SIZE)
470 return;
471
472 an->eeprom_data[offset] = val;
473 atenl_info("set offset 0x%x to 0x%x\n", offset, val);
developer48fbcb62022-03-10 14:24:55 +0800474 } else if (!strncmp(s, "update buffermode", 17)) {
developer2bf395f2022-01-26 20:50:22 +0800475 atenl_eeprom_sync_to_driver(an);
developer22c7ab62022-01-24 11:13:32 +0800476 atenl_nl_update_buffer_mode(an);
477 } else if (!strncmp(s, "write", 5)) {
478 s = strchr(s, ' ');
479 if (!s)
480 return;
481 s++;
482
developer48fbcb62022-03-10 14:24:55 +0800483 if (!strncmp(s, "flash", 5)) {
developer22c7ab62022-01-24 11:13:32 +0800484 atenl_eeprom_write_mtd(an);
developer48fbcb62022-03-10 14:24:55 +0800485 } else if (!strncmp(s, "to efuse", 8)) {
486 atenl_eeprom_sync_to_driver(an);
487 atenl_nl_write_efuse_all(an);
488 }
developer22c7ab62022-01-24 11:13:32 +0800489 } else if (!strncmp(s, "read", 4)) {
490 u32 offset;
491
492 s = strchr(s, ' ');
493 if (!s)
494 return;
495 s++;
496
497 if (!sscanf(s, "%x", &offset) ||
498 offset > EEPROM_PART_SIZE)
499 return;
500
501 atenl_info("val = 0x%x (%u)\n", an->eeprom_data[offset],
502 an->eeprom_data[offset]);
developer48fbcb62022-03-10 14:24:55 +0800503 } else {
504 atenl_err("Unknown eeprom command: %s\n", cmd);
505 }
developer22c7ab62022-01-24 11:13:32 +0800506 } else {
507 atenl_err("Unknown command: %s\n", cmd);
508 }
509}