blob: 73c0f8b40fbb8b950c39a2dd91e862f3d982a780 [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{
39 char buf[1024];
40 ssize_t len;
41 FILE *f;
42 int fd, ret;
43
44 f = mtd_open(an->mtd_part);
45 if (!f) {
46 fprintf(stderr, "Failed to open MTD device\n");
47 return -1;
48 }
49
developer2bf395f2022-01-26 20:50:22 +080050 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
developer22c7ab62022-01-24 11:13:32 +080051 if (fd < 0)
52 goto out;
53
54 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
55 ssize_t w;
56
57retry:
58 w = write(fd, buf, len);
59 if (w > 0)
60 continue;
61
62 if (errno == EINTR)
63 goto retry;
64
65 perror("write");
developer2bf395f2022-01-26 20:50:22 +080066 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +080067 close(fd);
68 fd = -1;
69 goto out;
70 }
71
72 ret = lseek(fd, 0, SEEK_SET);
73 if (ret) {
74 fclose(f);
75 close(fd);
76 return ret;
77 }
78
79out:
80 fclose(f);
81 return fd;
82}
83
84static int
85atenl_efuse_create_file(struct atenl *an)
86{
87 char fname[64], buf[1024];
88 ssize_t len;
89 int fd_ori, fd, ret;
90
91 snprintf(fname, sizeof(fname),
92 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom", get_band_val(an, 0, phy_idx));
93 fd_ori = open(fname, O_RDONLY);
94 if (fd_ori < 0)
95 return -1;
96
developer2bf395f2022-01-26 20:50:22 +080097 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
developer22c7ab62022-01-24 11:13:32 +080098 if (fd < 0)
99 goto out;
100
101 while ((len = read(fd_ori, buf, sizeof(buf))) > 0) {
102 ssize_t w;
103
104retry:
105 w = write(fd, buf, len);
106 if (w > 0)
107 continue;
108
109 if (errno == EINTR)
110 goto retry;
111
112 perror("write");
developer2bf395f2022-01-26 20:50:22 +0800113 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800114 close(fd);
115 fd = -1;
116 goto out;
117 }
118
119 ret = lseek(fd, 0, SEEK_SET);
120 if (ret) {
121 close(fd_ori);
122 close(fd);
123 return ret;
124 }
125
126out:
127 close(fd_ori);
128 return fd;
129}
130
131static bool
132atenl_eeprom_file_exists(void)
133{
134 struct stat st;
135
developer2bf395f2022-01-26 20:50:22 +0800136 return stat(eeprom_file, &st) == 0;
developer22c7ab62022-01-24 11:13:32 +0800137}
138
139static int
140atenl_eeprom_init_file(struct atenl *an, bool flash_mode)
141{
142 int fd;
143
144 if (!atenl_eeprom_file_exists()) {
145 if (flash_mode)
146 atenl_dbg("[%d]%s: init eeprom with flash mode\n", getpid(), __func__);
147 else
148 atenl_dbg("[%d]%s: init eeprom with efuse mode\n", getpid(), __func__);
149
150 if (flash_mode)
151 return atenl_flash_create_file(an);
152
153 return atenl_efuse_create_file(an);
154 }
155
developer2bf395f2022-01-26 20:50:22 +0800156 fd = open(eeprom_file, O_RDWR);
developer22c7ab62022-01-24 11:13:32 +0800157 if (fd < 0)
158 perror("open");
159
developer22c7ab62022-01-24 11:13:32 +0800160 return fd;
161}
162
163static void
164atenl_eeprom_init_max_size(struct atenl *an)
165{
166 switch (an->chip_id) {
167 case 0x7915:
168 an->eeprom_size = 3584;
169 break;
170 case 0x7906:
171 case 0x7916:
172 case 0x7986:
173 an->eeprom_size = 4096;
174 break;
175 default:
176 break;
177 }
178}
179
180static void
181atenl_eeprom_init_band_cap(struct atenl *an)
182{
183 u8 *eeprom = an->eeprom_data;
184
185 if (is_mt7915(an)) {
186 u8 val = eeprom[MT_EE_WIFI_CONF];
187 u8 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
188 struct atenl_band *anb = &an->anb[0];
189
190 /* MT7915A */
191 if (band_sel == MT_EE_BAND_SEL_DEFAULT) {
192 anb->valid = true;
193 anb->cap = BAND_TYPE_2G_5G;
194 return;
195 }
196
197 /* MT7915D */
198 if (band_sel == MT_EE_BAND_SEL_2GHZ) {
199 anb->valid = true;
200 anb->cap = BAND_TYPE_2G;
201 }
202
203 val = eeprom[MT_EE_WIFI_CONF + 1];
204 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
205 anb++;
206
207 if (band_sel == MT_EE_BAND_SEL_5GHZ) {
208 anb->valid = true;
209 anb->cap = BAND_TYPE_5G;
210 }
211 } else if (is_mt7916(an) || is_mt7986(an)) {
212 struct atenl_band *anb;
213 u8 val, band_sel;
214 int i;
215
216 for (i = 0; i < 2; i++) {
217 val = eeprom[MT_EE_WIFI_CONF + i];
218 band_sel = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
219 anb = &an->anb[i];
220
221 anb->valid = true;
222 switch (band_sel) {
223 case MT_EE_BAND_SEL_2G:
224 anb->cap = BAND_TYPE_2G;
225 break;
226 case MT_EE_BAND_SEL_5G:
227 anb->cap = BAND_TYPE_5G;
228 break;
229 case MT_EE_BAND_SEL_6G:
230 anb->cap = BAND_TYPE_6G;
231 break;
232 case MT_EE_BAND_SEL_5G_6G:
233 anb->cap = BAND_TYPE_5G_6G;
234 break;
235 default:
236 break;
237 }
238 }
239 }
240}
241
242static void
243atenl_eeprom_init_antenna_cap(struct atenl *an)
244{
245 if (is_mt7915(an)) {
246 if (an->anb[0].cap == BAND_TYPE_2G_5G)
247 an->anb[0].chainmask = 0xf;
248 else {
249 an->anb[0].chainmask = 0x3;
250 an->anb[1].chainmask = 0xc;
251 }
252 } else if (is_mt7916(an)) {
253 an->anb[0].chainmask = 0x3;
254 an->anb[1].chainmask = 0x3;
255 } else if (is_mt7986(an)) {
256 an->anb[0].chainmask = 0xf;
257 an->anb[1].chainmask = 0xf;
258 }
259}
260
261int atenl_eeprom_init(struct atenl *an, u8 phy_idx)
262{
263 bool flash_mode;
264 int eeprom_fd;
developer2bf395f2022-01-26 20:50:22 +0800265 char buf[30];
developer22c7ab62022-01-24 11:13:32 +0800266
267 set_band_val(an, 0, phy_idx, phy_idx);
developer2bf395f2022-01-26 20:50:22 +0800268 snprintf(buf, sizeof(buf), "/tmp/atenl-eeprom-phy%u", phy_idx);
269 eeprom_file = strdup(buf);
developer22c7ab62022-01-24 11:13:32 +0800270
271 atenl_nl_check_mtd(an);
272 flash_mode = an->mtd_part != NULL;
273
274 eeprom_fd = atenl_eeprom_init_file(an, flash_mode);
275 if (eeprom_fd < 0)
276 return -1;
277
278 an->eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
279 MAP_SHARED, eeprom_fd, an->mtd_offset);
280 if (!an->eeprom_data) {
281 perror("mmap");
282 close(eeprom_fd);
283 return -1;
284 }
285
286 an->eeprom_fd = eeprom_fd;
287 an->chip_id = *(u16 *)an->eeprom_data;
288 atenl_eeprom_init_max_size(an);
289 atenl_eeprom_init_band_cap(an);
290 atenl_eeprom_init_antenna_cap(an);
291
292 if (get_band_val(an, 1, valid))
293 set_band_val(an, 1, phy_idx, phy_idx + 1);
294
295 return 0;
296}
297
298void atenl_eeprom_close(struct atenl *an)
299{
300 msync(an->eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
301 munmap(an->eeprom_data, EEPROM_PART_SIZE);
302 close(an->eeprom_fd);
303
developer2bf395f2022-01-26 20:50:22 +0800304 if (!an->cmd_mode && an->child_pid) {
305 if (remove(eeprom_file))
developer22c7ab62022-01-24 11:13:32 +0800306 perror("remove");
developer2bf395f2022-01-26 20:50:22 +0800307 }
308
309 free(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800310}
311
312int atenl_eeprom_write_mtd(struct atenl *an)
313{
314 bool flash_mode = an->mtd_part != NULL;
315 pid_t pid;
316
317 if (!flash_mode)
318 return 0;
319
320 pid = fork();
321 if (pid < 0) {
322 perror("Fork");
323 return EXIT_FAILURE;
324 } else if (pid == 0) {
325 char *part = strdup(an->mtd_part);
developer2bf395f2022-01-26 20:50:22 +0800326 char *cmd[] = {"mtd", "write", eeprom_file, part, NULL};
developer22c7ab62022-01-24 11:13:32 +0800327 int ret;
328
329 ret = execvp("mtd", cmd);
330 if (ret < 0) {
331 fprintf(stderr, "%s: execl error\n", __func__);
332 exit(0);
333 }
334 } else {
335 wait(&pid);
336 }
337
338 return 0;
339}
340
341/* Directly read some value from driver's eeprom.
342 * It's usally used to get calibrated data from driver.
343 */
344int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len)
345{
346 u8 *eeprom_data = an->eeprom_data + offset;
347 char fname[64], buf[1024];
348 int fd_ori, ret;
349 ssize_t rd;
350
351 snprintf(fname, sizeof(fname),
352 "/sys/kernel/debug/ieee80211/phy%d/mt76/eeprom",
353 get_band_val(an, 0, phy_idx));
354 fd_ori = open(fname, O_RDONLY);
355 if (fd_ori < 0)
356 return -1;
357
358 ret = lseek(fd_ori, offset, SEEK_SET);
359 if (ret < 0)
360 goto out;
361
362 while ((rd = read(fd_ori, buf, sizeof(buf))) > 0 && len) {
363 if (len < rd) {
364 memcpy(eeprom_data, buf, len);
365 break;
366 } else {
367 memcpy(eeprom_data, buf, rd);
368 eeprom_data += rd;
369 len -= rd;
370 }
371 }
372
373 ret = 0;
374out:
375 close(fd_ori);
376 return ret;
377}
378
379/* Update all eeprom values to driver before writing efuse */
380static void
381atenl_eeprom_sync_to_driver(struct atenl *an)
382{
383 int i;
384
developer2bf395f2022-01-26 20:50:22 +0800385 for (i = 0; i < an->eeprom_size; i += 16)
developer22c7ab62022-01-24 11:13:32 +0800386 atenl_nl_write_eeprom(an, i, &an->eeprom_data[i], 16);
387}
388
389void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd)
390{
391 bool flash_mode;
392
393 an->cmd_mode = true;
394
395 atenl_eeprom_init(an, phy_idx);
396 flash_mode = an->mtd_part != NULL;
397
398 if (!strncmp(cmd, "sync eeprom all", 15)) {
399 atenl_eeprom_write_mtd(an);
400 } else if (!strncmp(cmd, "eeprom", 6)) {
401 char *s = strchr(cmd, ' ');
402
403 if (!s) {
404 fprintf(stderr, "eeprom: please type a correct command\n");
405 return;
406 }
407
408 s++;
409 if (!strncmp(s, "reset", 5)) {
developer2bf395f2022-01-26 20:50:22 +0800410 unlink(eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800411 } else if (!strncmp(s, "file", 4)) {
developer2bf395f2022-01-26 20:50:22 +0800412 atenl_info("%s\n", eeprom_file);
developer22c7ab62022-01-24 11:13:32 +0800413 atenl_info("Flash mode: %d\n", flash_mode);
414 } else if (!strncmp(s, "set", 3)) {
415 u32 offset, val;
416
417 s = strchr(s, ' ');
418 if (!s)
419 return;
420 s++;
421
422 if (!sscanf(s, "%x=%x", &offset, &val) ||
423 offset > EEPROM_PART_SIZE)
424 return;
425
426 an->eeprom_data[offset] = val;
427 atenl_info("set offset 0x%x to 0x%x\n", offset, val);
428 } else if (!strncmp(s, "update", 6)) {
developer2bf395f2022-01-26 20:50:22 +0800429 atenl_eeprom_sync_to_driver(an);
developer22c7ab62022-01-24 11:13:32 +0800430 atenl_nl_update_buffer_mode(an);
431 } else if (!strncmp(s, "write", 5)) {
432 s = strchr(s, ' ');
433 if (!s)
434 return;
435 s++;
436
developer2bf395f2022-01-26 20:50:22 +0800437 if (!strncmp(s, "flash", 5))
developer22c7ab62022-01-24 11:13:32 +0800438 atenl_eeprom_write_mtd(an);
developer22c7ab62022-01-24 11:13:32 +0800439 } else if (!strncmp(s, "read", 4)) {
440 u32 offset;
441
442 s = strchr(s, ' ');
443 if (!s)
444 return;
445 s++;
446
447 if (!sscanf(s, "%x", &offset) ||
448 offset > EEPROM_PART_SIZE)
449 return;
450
451 atenl_info("val = 0x%x (%u)\n", an->eeprom_data[offset],
452 an->eeprom_data[offset]);
453 }
454 } else {
455 atenl_err("Unknown command: %s\n", cmd);
456 }
457}