]> Git Repo - linux.git/blob - drivers/staging/brcm80211/brcmsmac/bcmsrom.c
memsw: remove noswapaccount kernel parameter
[linux.git] / drivers / staging / brcm80211 / brcmsmac / bcmsrom.c
1 /*
2  * Copyright (c) 2010 Broadcom Corporation
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/etherdevice.h>
19 #include <bcmdefs.h>
20 #include <linux/module.h>
21 #include <linux/pci.h>
22 #include <stdarg.h>
23 #include <bcmutils.h>
24 #include <hndsoc.h>
25 #include <sbchipc.h>
26 #include <bcmdevs.h>
27 #include <pcicfg.h>
28 #include <aiutils.h>
29 #include <bcmsrom.h>
30 #include <bcmsrom_tbl.h>
31
32 #include <bcmnvram.h>
33 #include <bcmotp.h>
34
35 #define SROM_OFFSET(sih) ((sih->ccrev > 31) ? \
36         (((sih->cccaps & CC_CAP_SROM) == 0) ? NULL : \
37          ((u8 *)curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP)) : \
38         ((u8 *)curmap + PCI_BAR0_SPROM_OFFSET))
39
40 #if defined(BCMDBG)
41 #define WRITE_ENABLE_DELAY      500     /* 500 ms after write enable/disable toggle */
42 #define WRITE_WORD_DELAY        20      /* 20 ms between each word write */
43 #endif
44
45 typedef struct varbuf {
46         char *base;             /* pointer to buffer base */
47         char *buf;              /* pointer to current position */
48         unsigned int size;      /* current (residual) size in bytes */
49 } varbuf_t;
50 extern char *_vars;
51 extern uint _varsz;
52
53 static int initvars_srom_si(si_t *sih, void *curmap, char **vars, uint *count);
54 static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off, varbuf_t *b);
55 static int initvars_srom_pci(si_t *sih, void *curmap, char **vars, uint *count);
56 static int initvars_flash_si(si_t *sih, char **vars, uint *count);
57 static int sprom_read_pci(si_t *sih, u16 *sprom,
58                           uint wordoff, u16 *buf, uint nwords, bool check_crc);
59 #if defined(BCMNVRAMR)
60 static int otp_read_pci(si_t *sih, u16 *buf, uint bufsz);
61 #endif
62 static u16 srom_cc_cmd(si_t *sih, void *ccregs, u32 cmd,
63                           uint wordoff, u16 data);
64
65 static int initvars_table(char *start, char *end,
66                           char **vars, uint *count);
67 static int initvars_flash(si_t *sih, char **vp,
68                           uint len);
69
70 /* Initialization of varbuf structure */
71 static void varbuf_init(varbuf_t *b, char *buf, uint size)
72 {
73         b->size = size;
74         b->base = b->buf = buf;
75 }
76
77 /* append a null terminated var=value string */
78 static int varbuf_append(varbuf_t *b, const char *fmt, ...)
79 {
80         va_list ap;
81         int r;
82         size_t len;
83         char *s;
84
85         if (b->size < 2)
86                 return 0;
87
88         va_start(ap, fmt);
89         r = vsnprintf(b->buf, b->size, fmt, ap);
90         va_end(ap);
91
92         /* C99 snprintf behavior returns r >= size on overflow,
93          * others return -1 on overflow.
94          * All return -1 on format error.
95          * We need to leave room for 2 null terminations, one for the current var
96          * string, and one for final null of the var table. So check that the
97          * strlen written, r, leaves room for 2 chars.
98          */
99         if ((r == -1) || (r > (int)(b->size - 2))) {
100                 b->size = 0;
101                 return 0;
102         }
103
104         /* Remove any earlier occurrence of the same variable */
105         s = strchr(b->buf, '=');
106         if (s != NULL) {
107                 len = (size_t) (s - b->buf);
108                 for (s = b->base; s < b->buf;) {
109                         if ((memcmp(s, b->buf, len) == 0) && s[len] == '=') {
110                                 len = strlen(s) + 1;
111                                 memmove(s, (s + len),
112                                         ((b->buf + r + 1) - (s + len)));
113                                 b->buf -= len;
114                                 b->size += (unsigned int)len;
115                                 break;
116                         }
117
118                         while (*s++)
119                                 ;
120                 }
121         }
122
123         /* skip over this string's null termination */
124         r++;
125         b->size -= r;
126         b->buf += r;
127
128         return r;
129 }
130
131 /*
132  * Initialize local vars from the right source for this platform.
133  * Return 0 on success, nonzero on error.
134  */
135 int srom_var_init(si_t *sih, uint bustype, void *curmap,
136                   char **vars, uint *count)
137 {
138         uint len;
139
140         len = 0;
141
142         if (vars == NULL || count == NULL)
143                 return 0;
144
145         *vars = NULL;
146         *count = 0;
147
148         switch (bustype) {
149         case SI_BUS:
150         case JTAG_BUS:
151                 return initvars_srom_si(sih, curmap, vars, count);
152
153         case PCI_BUS:
154                 if (curmap == NULL)
155                         return -1;
156
157                 return initvars_srom_pci(sih, curmap, vars, count);
158
159         default:
160                 break;
161         }
162         return -1;
163 }
164
165 /* In chips with chipcommon rev 32 and later, the srom is in chipcommon,
166  * not in the bus cores.
167  */
168 static u16
169 srom_cc_cmd(si_t *sih, void *ccregs, u32 cmd,
170             uint wordoff, u16 data)
171 {
172         chipcregs_t *cc = (chipcregs_t *) ccregs;
173         uint wait_cnt = 1000;
174
175         if ((cmd == SRC_OP_READ) || (cmd == SRC_OP_WRITE)) {
176                 W_REG(&cc->sromaddress, wordoff * 2);
177                 if (cmd == SRC_OP_WRITE)
178                         W_REG(&cc->sromdata, data);
179         }
180
181         W_REG(&cc->sromcontrol, SRC_START | cmd);
182
183         while (wait_cnt--) {
184                 if ((R_REG(&cc->sromcontrol) & SRC_BUSY) == 0)
185                         break;
186         }
187
188         if (!wait_cnt) {
189                 return 0xffff;
190         }
191         if (cmd == SRC_OP_READ)
192                 return (u16) R_REG(&cc->sromdata);
193         else
194                 return 0xffff;
195 }
196
197 static inline void ltoh16_buf(u16 *buf, unsigned int size)
198 {
199         for (size /= 2; size; size--)
200                 *(buf + size) = le16_to_cpu(*(buf + size));
201 }
202
203 static inline void htol16_buf(u16 *buf, unsigned int size)
204 {
205         for (size /= 2; size; size--)
206                 *(buf + size) = cpu_to_le16(*(buf + size));
207 }
208
209 /*
210  * Read in and validate sprom.
211  * Return 0 on success, nonzero on error.
212  */
213 static int
214 sprom_read_pci(si_t *sih, u16 *sprom, uint wordoff,
215                u16 *buf, uint nwords, bool check_crc)
216 {
217         int err = 0;
218         uint i;
219         void *ccregs = NULL;
220
221         /* read the sprom */
222         for (i = 0; i < nwords; i++) {
223
224                 if (sih->ccrev > 31 && ISSIM_ENAB(sih)) {
225                         /* use indirect since direct is too slow on QT */
226                         if ((sih->cccaps & CC_CAP_SROM) == 0)
227                                 return 1;
228
229                         ccregs = (void *)((u8 *) sprom - CC_SROM_OTP);
230                         buf[i] =
231                             srom_cc_cmd(sih, ccregs, SRC_OP_READ,
232                                         wordoff + i, 0);
233
234                 } else {
235                         if (ISSIM_ENAB(sih))
236                                 buf[i] = R_REG(&sprom[wordoff + i]);
237
238                         buf[i] = R_REG(&sprom[wordoff + i]);
239                 }
240
241         }
242
243         /* bypass crc checking for simulation to allow srom hack */
244         if (ISSIM_ENAB(sih))
245                 return err;
246
247         if (check_crc) {
248
249                 if (buf[0] == 0xffff) {
250                         /* The hardware thinks that an srom that starts with 0xffff
251                          * is blank, regardless of the rest of the content, so declare
252                          * it bad.
253                          */
254                         return 1;
255                 }
256
257                 /* fixup the endianness so crc8 will pass */
258                 htol16_buf(buf, nwords * 2);
259                 if (bcm_crc8((u8 *) buf, nwords * 2, CRC8_INIT_VALUE) !=
260                     CRC8_GOOD_VALUE) {
261                         /* DBG only pci always read srom4 first, then srom8/9 */
262                         err = 1;
263                 }
264                 /* now correct the endianness of the byte array */
265                 ltoh16_buf(buf, nwords * 2);
266         }
267         return err;
268 }
269
270 #if defined(BCMNVRAMR)
271 static int otp_read_pci(si_t *sih, u16 *buf, uint bufsz)
272 {
273         u8 *otp;
274         uint sz = OTP_SZ_MAX / 2;       /* size in words */
275         int err = 0;
276
277         otp = kzalloc(OTP_SZ_MAX, GFP_ATOMIC);
278         if (otp == NULL) {
279                 return -EBADE;
280         }
281
282         err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz);
283
284         memcpy(buf, otp, bufsz);
285
286         kfree(otp);
287
288         /* Check CRC */
289         if (buf[0] == 0xffff) {
290                 /* The hardware thinks that an srom that starts with 0xffff
291                  * is blank, regardless of the rest of the content, so declare
292                  * it bad.
293                  */
294                 return 1;
295         }
296
297         /* fixup the endianness so crc8 will pass */
298         htol16_buf(buf, bufsz);
299         if (bcm_crc8((u8 *) buf, SROM4_WORDS * 2, CRC8_INIT_VALUE) !=
300             CRC8_GOOD_VALUE) {
301                 err = 1;
302         }
303         /* now correct the endianness of the byte array */
304         ltoh16_buf(buf, bufsz);
305
306         return err;
307 }
308 #endif                          /* defined(BCMNVRAMR) */
309 /*
310 * Create variable table from memory.
311 * Return 0 on success, nonzero on error.
312 */
313 static int initvars_table(char *start, char *end,
314                           char **vars, uint *count)
315 {
316         int c = (int)(end - start);
317
318         /* do it only when there is more than just the null string */
319         if (c > 1) {
320                 char *vp = kmalloc(c, GFP_ATOMIC);
321                 if (!vp)
322                         return -ENOMEM;
323                 memcpy(vp, start, c);
324                 *vars = vp;
325                 *count = c;
326         } else {
327                 *vars = NULL;
328                 *count = 0;
329         }
330
331         return 0;
332 }
333
334 /*
335  * Find variables with <devpath> from flash. 'base' points to the beginning
336  * of the table upon enter and to the end of the table upon exit when success.
337  * Return 0 on success, nonzero on error.
338  */
339 static int initvars_flash(si_t *sih, char **base, uint len)
340 {
341         char *vp = *base;
342         char *flash;
343         int err;
344         char *s;
345         uint l, dl, copy_len;
346         char devpath[SI_DEVPATH_BUFSZ];
347
348         /* allocate memory and read in flash */
349         flash = kmalloc(NVRAM_SPACE, GFP_ATOMIC);
350         if (!flash)
351                 return -ENOMEM;
352         err = nvram_getall(flash, NVRAM_SPACE);
353         if (err)
354                 goto exit;
355
356         ai_devpath(sih, devpath, sizeof(devpath));
357
358         /* grab vars with the <devpath> prefix in name */
359         dl = strlen(devpath);
360         for (s = flash; s && *s; s += l + 1) {
361                 l = strlen(s);
362
363                 /* skip non-matching variable */
364                 if (strncmp(s, devpath, dl))
365                         continue;
366
367                 /* is there enough room to copy? */
368                 copy_len = l - dl + 1;
369                 if (len < copy_len) {
370                         err = -EOVERFLOW;
371                         goto exit;
372                 }
373
374                 /* no prefix, just the name=value */
375                 strncpy(vp, &s[dl], copy_len);
376                 vp += copy_len;
377                 len -= copy_len;
378         }
379
380         /* add null string as terminator */
381         if (len < 1) {
382                 err = -EOVERFLOW;
383                 goto exit;
384         }
385         *vp++ = '\0';
386
387         *base = vp;
388
389  exit:  kfree(flash);
390         return err;
391 }
392
393 /*
394  * Initialize nonvolatile variable table from flash.
395  * Return 0 on success, nonzero on error.
396  */
397 static int initvars_flash_si(si_t *sih, char **vars, uint *count)
398 {
399         char *vp, *base;
400         int err;
401
402         base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
403         if (!vp)
404                 return -ENOMEM;
405
406         err = initvars_flash(sih, &vp, MAXSZ_NVRAM_VARS);
407         if (err == 0)
408                 err = initvars_table(base, vp, vars, count);
409
410         kfree(base);
411
412         return err;
413 }
414
415 /* Parse SROM and create name=value pairs. 'srom' points to
416  * the SROM word array. 'off' specifies the offset of the
417  * first word 'srom' points to, which should be either 0 or
418  * SROM3_SWRG_OFF (full SROM or software region).
419  */
420
421 static uint mask_shift(u16 mask)
422 {
423         uint i;
424         for (i = 0; i < (sizeof(mask) << 3); i++) {
425                 if (mask & (1 << i))
426                         return i;
427         }
428         return 0;
429 }
430
431 static uint mask_width(u16 mask)
432 {
433         int i;
434         for (i = (sizeof(mask) << 3) - 1; i >= 0; i--) {
435                 if (mask & (1 << i))
436                         return (uint) (i - mask_shift(mask) + 1);
437         }
438         return 0;
439 }
440
441 static void _initvars_srom_pci(u8 sromrev, u16 *srom, uint off, varbuf_t *b)
442 {
443         u16 w;
444         u32 val;
445         const sromvar_t *srv;
446         uint width;
447         uint flags;
448         u32 sr = (1 << sromrev);
449
450         varbuf_append(b, "sromrev=%d", sromrev);
451
452         for (srv = pci_sromvars; srv->name != NULL; srv++) {
453                 const char *name;
454
455                 if ((srv->revmask & sr) == 0)
456                         continue;
457
458                 if (srv->off < off)
459                         continue;
460
461                 flags = srv->flags;
462                 name = srv->name;
463
464                 /* This entry is for mfgc only. Don't generate param for it, */
465                 if (flags & SRFL_NOVAR)
466                         continue;
467
468                 if (flags & SRFL_ETHADDR) {
469                         u8 ea[ETH_ALEN];
470
471                         ea[0] = (srom[srv->off - off] >> 8) & 0xff;
472                         ea[1] = srom[srv->off - off] & 0xff;
473                         ea[2] = (srom[srv->off + 1 - off] >> 8) & 0xff;
474                         ea[3] = srom[srv->off + 1 - off] & 0xff;
475                         ea[4] = (srom[srv->off + 2 - off] >> 8) & 0xff;
476                         ea[5] = srom[srv->off + 2 - off] & 0xff;
477
478                         varbuf_append(b, "%s=%pM", name, ea);
479                 } else {
480                         w = srom[srv->off - off];
481                         val = (w & srv->mask) >> mask_shift(srv->mask);
482                         width = mask_width(srv->mask);
483
484                         while (srv->flags & SRFL_MORE) {
485                                 srv++;
486                                 if (srv->off == 0 || srv->off < off)
487                                         continue;
488
489                                 w = srom[srv->off - off];
490                                 val +=
491                                     ((w & srv->mask) >> mask_shift(srv->
492                                                                    mask)) <<
493                                     width;
494                                 width += mask_width(srv->mask);
495                         }
496
497                         if ((flags & SRFL_NOFFS)
498                             && ((int)val == (1 << width) - 1))
499                                 continue;
500
501                         if (flags & SRFL_CCODE) {
502                                 if (val == 0)
503                                         varbuf_append(b, "ccode=");
504                                 else
505                                         varbuf_append(b, "ccode=%c%c",
506                                                       (val >> 8), (val & 0xff));
507                         }
508                         /* LED Powersave duty cycle has to be scaled:
509                          *(oncount >> 24) (offcount >> 8)
510                          */
511                         else if (flags & SRFL_LEDDC) {
512                                 u32 w32 = (((val >> 8) & 0xff) << 24) | /* oncount */
513                                     (((val & 0xff)) << 8);      /* offcount */
514                                 varbuf_append(b, "leddc=%d", w32);
515                         } else if (flags & SRFL_PRHEX)
516                                 varbuf_append(b, "%s=0x%x", name, val);
517                         else if ((flags & SRFL_PRSIGN)
518                                  && (val & (1 << (width - 1))))
519                                 varbuf_append(b, "%s=%d", name,
520                                               (int)(val | (~0 << width)));
521                         else
522                                 varbuf_append(b, "%s=%u", name, val);
523                 }
524         }
525
526         if (sromrev >= 4) {
527                 /* Do per-path variables */
528                 uint p, pb, psz;
529
530                 if (sromrev >= 8) {
531                         pb = SROM8_PATH0;
532                         psz = SROM8_PATH1 - SROM8_PATH0;
533                 } else {
534                         pb = SROM4_PATH0;
535                         psz = SROM4_PATH1 - SROM4_PATH0;
536                 }
537
538                 for (p = 0; p < MAX_PATH_SROM; p++) {
539                         for (srv = perpath_pci_sromvars; srv->name != NULL;
540                              srv++) {
541                                 if ((srv->revmask & sr) == 0)
542                                         continue;
543
544                                 if (pb + srv->off < off)
545                                         continue;
546
547                                 /* This entry is for mfgc only. Don't generate param for it, */
548                                 if (srv->flags & SRFL_NOVAR)
549                                         continue;
550
551                                 w = srom[pb + srv->off - off];
552                                 val = (w & srv->mask) >> mask_shift(srv->mask);
553                                 width = mask_width(srv->mask);
554
555                                 /* Cheating: no per-path var is more than 1 word */
556
557                                 if ((srv->flags & SRFL_NOFFS)
558                                     && ((int)val == (1 << width) - 1))
559                                         continue;
560
561                                 if (srv->flags & SRFL_PRHEX)
562                                         varbuf_append(b, "%s%d=0x%x", srv->name,
563                                                       p, val);
564                                 else
565                                         varbuf_append(b, "%s%d=%d", srv->name,
566                                                       p, val);
567                         }
568                         pb += psz;
569                 }
570         }
571 }
572
573 /*
574  * Initialize nonvolatile variable table from sprom.
575  * Return 0 on success, nonzero on error.
576  */
577 static int initvars_srom_pci(si_t *sih, void *curmap, char **vars, uint *count)
578 {
579         u16 *srom, *sromwindow;
580         u8 sromrev = 0;
581         u32 sr;
582         varbuf_t b;
583         char *vp, *base = NULL;
584         bool flash = false;
585         int err = 0;
586
587         /*
588          * Apply CRC over SROM content regardless SROM is present or not,
589          * and use variable <devpath>sromrev's existence in flash to decide
590          * if we should return an error when CRC fails or read SROM variables
591          * from flash.
592          */
593         srom = kmalloc(SROM_MAX, GFP_ATOMIC);
594         if (!srom)
595                 return -2;
596
597         sromwindow = (u16 *) SROM_OFFSET(sih);
598         if (ai_is_sprom_available(sih)) {
599                 err =
600                     sprom_read_pci(sih, sromwindow, 0, srom, SROM_WORDS,
601                                    true);
602
603                 if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) ||
604                     (((sih->buscoretype == PCIE_CORE_ID)
605                       && (sih->buscorerev >= 6))
606                      || ((sih->buscoretype == PCI_CORE_ID)
607                          && (sih->buscorerev >= 0xe)))) {
608                         /* sromrev >= 4, read more */
609                         err =
610                             sprom_read_pci(sih, sromwindow, 0, srom,
611                                            SROM4_WORDS, true);
612                         sromrev = srom[SROM4_CRCREV] & 0xff;
613                 } else if (err == 0) {
614                         /* srom is good and is rev < 4 */
615                         /* top word of sprom contains version and crc8 */
616                         sromrev = srom[SROM_CRCREV] & 0xff;
617                         /* bcm4401 sroms misprogrammed */
618                         if (sromrev == 0x10)
619                                 sromrev = 1;
620                 }
621         }
622 #if defined(BCMNVRAMR)
623         /* Use OTP if SPROM not available */
624         else {
625                 err = otp_read_pci(sih, srom, SROM_MAX);
626                 if (err == 0)
627                         /* OTP only contain SROM rev8/rev9 for now */
628                         sromrev = srom[SROM4_CRCREV] & 0xff;
629                 else
630                         err = 1;
631         }
632 #else
633         else
634                 err = 1;
635 #endif
636
637         /*
638          * We want internal/wltest driver to come up with default
639          * sromvars so we can program a blank SPROM/OTP.
640          */
641         if (err) {
642                 char *value;
643                 u32 val;
644                 val = 0;
645
646                 value = ai_getdevpathvar(sih, "sromrev");
647                 if (value) {
648                         sromrev = (u8) simple_strtoul(value, NULL, 0);
649                         flash = true;
650                         goto varscont;
651                 }
652
653                 value = ai_getnvramflvar(sih, "sromrev");
654                 if (value) {
655                         err = 0;
656                         goto errout;
657                 }
658
659                 {
660                         err = -1;
661                         goto errout;
662                 }
663         }
664
665  varscont:
666         /* Bitmask for the sromrev */
667         sr = 1 << sromrev;
668
669         /* srom version check: Current valid versions: 1, 2, 3, 4, 5, 8, 9 */
670         if ((sr & 0x33e) == 0) {
671                 err = -2;
672                 goto errout;
673         }
674
675         base = vp = kmalloc(MAXSZ_NVRAM_VARS, GFP_ATOMIC);
676         if (!vp) {
677                 err = -2;
678                 goto errout;
679         }
680
681         /* read variables from flash */
682         if (flash) {
683                 err = initvars_flash(sih, &vp, MAXSZ_NVRAM_VARS);
684                 if (err)
685                         goto errout;
686                 goto varsdone;
687         }
688
689         varbuf_init(&b, base, MAXSZ_NVRAM_VARS);
690
691         /* parse SROM into name=value pairs. */
692         _initvars_srom_pci(sromrev, srom, 0, &b);
693
694         /* final nullbyte terminator */
695         vp = b.buf;
696         *vp++ = '\0';
697
698  varsdone:
699         err = initvars_table(base, vp, vars, count);
700
701  errout:
702         if (base)
703                 kfree(base);
704
705         kfree(srom);
706         return err;
707 }
708
709
710 static int initvars_srom_si(si_t *sih, void *curmap, char **vars, uint *varsz)
711 {
712         /* Search flash nvram section for srom variables */
713         return initvars_flash_si(sih, vars, varsz);
714 }
This page took 0.070084 seconds and 4 git commands to generate.