aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-07-31 00:23:53 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2008-08-29 01:48:57 -0400
commitf139cfa7cdccd0b315fad098889897b5fcd389b0 (patch)
tree365985f84f6dade6125f7d9a0e8cffcf30b87fa0
parenta7581a01fbc69771a2b391de4220ba670c0aa261 (diff)
crypto: tcrypt - Avoid using contiguous pages
If tcrypt is to be used as a run-time integrity test, it needs to be more resilient in a hostile environment. For a start allocating 32K of physically contiguous memory is definitely out. This patch teaches it to use separate pages instead. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r--crypto/tcrypt.c304
-rw-r--r--crypto/tcrypt.h8
2 files changed, 157 insertions, 155 deletions
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 66368022e0bf..b6d4b5ce00a3 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -31,10 +31,10 @@
31#include "tcrypt.h" 31#include "tcrypt.h"
32 32
33/* 33/*
34 * Need to kmalloc() memory for testing. 34 * Need slab memory for testing (size in number of pages).
35 */ 35 */
36#define TVMEMSIZE 16384 36#define TVMEMSIZE 4
37#define XBUFSIZE 32768 37#define XBUFSIZE 8
38 38
39/* 39/*
40 * Indexes into the xbuf to simulate cross-page access. 40 * Indexes into the xbuf to simulate cross-page access.
@@ -67,9 +67,9 @@ static unsigned int IDX[8] = { IDX1, IDX2, IDX3, IDX4, IDX5, IDX6, IDX7, IDX8 };
67static unsigned int sec; 67static unsigned int sec;
68 68
69static int mode; 69static int mode;
70static char *xbuf; 70static char *xbuf[XBUFSIZE];
71static char *axbuf; 71static char *axbuf[XBUFSIZE];
72static char *tvmem; 72static char *tvmem[TVMEMSIZE];
73 73
74static char *check[] = { 74static char *check[] = {
75 "des", "md5", "des3_ede", "rot13", "sha1", "sha224", "sha256", 75 "des", "md5", "des3_ede", "rot13", "sha1", "sha224", "sha256",
@@ -133,9 +133,7 @@ static void test_hash(char *algo, struct hash_testvec *template,
133 printk("test %u:\n", i + 1); 133 printk("test %u:\n", i + 1);
134 memset(result, 0, 64); 134 memset(result, 0, 64);
135 135
136 hash_buff = kzalloc(template[i].psize, GFP_KERNEL); 136 hash_buff = xbuf[0];
137 if (!hash_buff)
138 continue;
139 137
140 memcpy(hash_buff, template[i].plaintext, template[i].psize); 138 memcpy(hash_buff, template[i].plaintext, template[i].psize);
141 sg_init_one(&sg[0], hash_buff, template[i].psize); 139 sg_init_one(&sg[0], hash_buff, template[i].psize);
@@ -146,7 +144,6 @@ static void test_hash(char *algo, struct hash_testvec *template,
146 template[i].ksize); 144 template[i].ksize);
147 if (ret) { 145 if (ret) {
148 printk("setkey() failed ret=%d\n", ret); 146 printk("setkey() failed ret=%d\n", ret);
149 kfree(hash_buff);
150 goto out; 147 goto out;
151 } 148 }
152 } 149 }
@@ -167,7 +164,6 @@ static void test_hash(char *algo, struct hash_testvec *template,
167 /* fall through */ 164 /* fall through */
168 default: 165 default:
169 printk("digest () failed ret=%d\n", ret); 166 printk("digest () failed ret=%d\n", ret);
170 kfree(hash_buff);
171 goto out; 167 goto out;
172 } 168 }
173 169
@@ -176,14 +172,10 @@ static void test_hash(char *algo, struct hash_testvec *template,
176 memcmp(result, template[i].digest, 172 memcmp(result, template[i].digest,
177 crypto_ahash_digestsize(tfm)) ? 173 crypto_ahash_digestsize(tfm)) ?
178 "fail" : "pass"); 174 "fail" : "pass");
179 kfree(hash_buff);
180 } 175 }
181 176
182 printk("testing %s across pages\n", algo); 177 printk("testing %s across pages\n", algo);
183 178
184 /* setup the dummy buffer first */
185 memset(xbuf, 0, XBUFSIZE);
186
187 j = 0; 179 j = 0;
188 for (i = 0; i < tcount; i++) { 180 for (i = 0; i < tcount; i++) {
189 if (template[i].np) { 181 if (template[i].np) {
@@ -194,12 +186,13 @@ static void test_hash(char *algo, struct hash_testvec *template,
194 temp = 0; 186 temp = 0;
195 sg_init_table(sg, template[i].np); 187 sg_init_table(sg, template[i].np);
196 for (k = 0; k < template[i].np; k++) { 188 for (k = 0; k < template[i].np; k++) {
197 memcpy(&xbuf[IDX[k]], 189 sg_set_buf(&sg[k],
198 template[i].plaintext + temp, 190 memcpy(xbuf[IDX[k] >> PAGE_SHIFT] +
199 template[i].tap[k]); 191 offset_in_page(IDX[k]),
192 template[i].plaintext + temp,
193 template[i].tap[k]),
194 template[i].tap[k]);
200 temp += template[i].tap[k]; 195 temp += template[i].tap[k];
201 sg_set_buf(&sg[k], &xbuf[IDX[k]],
202 template[i].tap[k]);
203 } 196 }
204 197
205 if (template[i].ksize) { 198 if (template[i].ksize) {
@@ -298,15 +291,8 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
298 /* some tepmplates have no input data but they will 291 /* some tepmplates have no input data but they will
299 * touch input 292 * touch input
300 */ 293 */
301 input = kzalloc(template[i].ilen + template[i].rlen, GFP_KERNEL); 294 input = xbuf[0];
302 if (!input) 295 assoc = axbuf[0];
303 continue;
304
305 assoc = kzalloc(template[i].alen, GFP_KERNEL);
306 if (!assoc) {
307 kfree(input);
308 continue;
309 }
310 296
311 memcpy(input, template[i].input, template[i].ilen); 297 memcpy(input, template[i].input, template[i].ilen);
312 memcpy(assoc, template[i].assoc, template[i].alen); 298 memcpy(assoc, template[i].assoc, template[i].alen);
@@ -320,10 +306,7 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
320 crypto_aead_set_flags( 306 crypto_aead_set_flags(
321 tfm, CRYPTO_TFM_REQ_WEAK_KEY); 307 tfm, CRYPTO_TFM_REQ_WEAK_KEY);
322 308
323 if (template[i].key) 309 key = template[i].key;
324 key = template[i].key;
325 else
326 key = kzalloc(template[i].klen, GFP_KERNEL);
327 310
328 ret = crypto_aead_setkey(tfm, key, 311 ret = crypto_aead_setkey(tfm, key,
329 template[i].klen); 312 template[i].klen);
@@ -332,7 +315,7 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
332 crypto_aead_get_flags(tfm)); 315 crypto_aead_get_flags(tfm));
333 316
334 if (!template[i].fail) 317 if (!template[i].fail)
335 goto next_one; 318 continue;
336 } 319 }
337 320
338 authsize = abs(template[i].rlen - template[i].ilen); 321 authsize = abs(template[i].rlen - template[i].ilen);
@@ -341,7 +324,7 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
341 printk(KERN_INFO 324 printk(KERN_INFO
342 "failed to set authsize = %u\n", 325 "failed to set authsize = %u\n",
343 authsize); 326 authsize);
344 goto next_one; 327 continue;
345 } 328 }
346 329
347 sg_init_one(&sg[0], input, 330 sg_init_one(&sg[0], input,
@@ -373,7 +356,7 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
373 default: 356 default:
374 printk(KERN_INFO "%s () failed err=%d\n", 357 printk(KERN_INFO "%s () failed err=%d\n",
375 e, -ret); 358 e, -ret);
376 goto next_one; 359 continue;
377 } 360 }
378 361
379 q = input; 362 q = input;
@@ -382,16 +365,10 @@ static void test_aead(char *algo, int enc, struct aead_testvec *template,
382 printk(KERN_INFO "enc/dec: %s\n", 365 printk(KERN_INFO "enc/dec: %s\n",
383 memcmp(q, template[i].result, 366 memcmp(q, template[i].result,
384 template[i].rlen) ? "fail" : "pass"); 367 template[i].rlen) ? "fail" : "pass");
385next_one:
386 if (!template[i].key)
387 kfree(key);
388 kfree(assoc);
389 kfree(input);
390 } 368 }
391 } 369 }
392 370
393 printk(KERN_INFO "\ntesting %s %s across pages (chunking)\n", algo, e); 371 printk(KERN_INFO "\ntesting %s %s across pages (chunking)\n", algo, e);
394 memset(axbuf, 0, XBUFSIZE);
395 372
396 for (i = 0, j = 0; i < tcount; i++) { 373 for (i = 0, j = 0; i < tcount; i++) {
397 if (template[i].np) { 374 if (template[i].np) {
@@ -418,18 +395,30 @@ next_one:
418 goto out; 395 goto out;
419 } 396 }
420 397
421 memset(xbuf, 0, XBUFSIZE); 398 authsize = abs(template[i].rlen - template[i].ilen);
399
422 sg_init_table(sg, template[i].np); 400 sg_init_table(sg, template[i].np);
423 for (k = 0, temp = 0; k < template[i].np; k++) { 401 for (k = 0, temp = 0; k < template[i].np; k++) {
424 memcpy(&xbuf[IDX[k]], 402 if (WARN_ON(offset_in_page(IDX[k]) +
425 template[i].input + temp, 403 template[i].tap[k] > PAGE_SIZE))
404 goto out;
405
406 q = xbuf[IDX[k] >> PAGE_SHIFT] +
407 offset_in_page(IDX[k]);
408
409 memcpy(q, template[i].input + temp,
426 template[i].tap[k]); 410 template[i].tap[k]);
411
412 n = template[i].tap[k];
413 if (k == template[i].np - 1 && enc)
414 n += authsize;
415 if (offset_in_page(q) + n < PAGE_SIZE)
416 q[n] = 0;
417
418 sg_set_buf(&sg[k], q, template[i].tap[k]);
427 temp += template[i].tap[k]; 419 temp += template[i].tap[k];
428 sg_set_buf(&sg[k], &xbuf[IDX[k]],
429 template[i].tap[k]);
430 } 420 }
431 421
432 authsize = abs(template[i].rlen - template[i].ilen);
433 ret = crypto_aead_setauthsize(tfm, authsize); 422 ret = crypto_aead_setauthsize(tfm, authsize);
434 if (ret) { 423 if (ret) {
435 printk(KERN_INFO 424 printk(KERN_INFO
@@ -438,17 +427,24 @@ next_one:
438 goto out; 427 goto out;
439 } 428 }
440 429
441 if (enc) 430 if (enc) {
431 if (WARN_ON(sg[k - 1].offset +
432 sg[k - 1].length + authsize >
433 PAGE_SIZE))
434 goto out;
435
442 sg[k - 1].length += authsize; 436 sg[k - 1].length += authsize;
437 }
443 438
444 sg_init_table(asg, template[i].anp); 439 sg_init_table(asg, template[i].anp);
445 for (k = 0, temp = 0; k < template[i].anp; k++) { 440 for (k = 0, temp = 0; k < template[i].anp; k++) {
446 memcpy(&axbuf[IDX[k]], 441 sg_set_buf(&asg[k],
447 template[i].assoc + temp, 442 memcpy(axbuf[IDX[k] >> PAGE_SHIFT] +
448 template[i].atap[k]); 443 offset_in_page(IDX[k]),
449 temp += template[i].atap[k]; 444 template[i].assoc + temp,
450 sg_set_buf(&asg[k], &axbuf[IDX[k]], 445 template[i].atap[k]),
451 template[i].atap[k]); 446 template[i].atap[k]);
447 temp += template[i].atap[k];
452 } 448 }
453 449
454 aead_request_set_crypt(req, sg, sg, 450 aead_request_set_crypt(req, sg, sg,
@@ -481,7 +477,8 @@ next_one:
481 477
482 for (k = 0, temp = 0; k < template[i].np; k++) { 478 for (k = 0, temp = 0; k < template[i].np; k++) {
483 printk(KERN_INFO "page %u\n", k); 479 printk(KERN_INFO "page %u\n", k);
484 q = &xbuf[IDX[k]]; 480 q = xbuf[IDX[k] >> PAGE_SHIFT] +
481 offset_in_page(IDX[k]);
485 482
486 n = template[i].tap[k]; 483 n = template[i].tap[k];
487 if (k == template[i].np - 1) 484 if (k == template[i].np - 1)
@@ -499,7 +496,8 @@ next_one:
499 else 496 else
500 n = 0; 497 n = 0;
501 } else { 498 } else {
502 for (n = 0; q[n]; n++) 499 for (n = 0; offset_in_page(q + n) &&
500 q[n]; n++)
503 ; 501 ;
504 } 502 }
505 if (n) { 503 if (n) {
@@ -558,12 +556,6 @@ static void test_cipher(char *algo, int enc,
558 556
559 j = 0; 557 j = 0;
560 for (i = 0; i < tcount; i++) { 558 for (i = 0; i < tcount; i++) {
561
562 data = kzalloc(template[i].ilen, GFP_KERNEL);
563 if (!data)
564 continue;
565
566 memcpy(data, template[i].input, template[i].ilen);
567 if (template[i].iv) 559 if (template[i].iv)
568 memcpy(iv, template[i].iv, MAX_IVLEN); 560 memcpy(iv, template[i].iv, MAX_IVLEN);
569 else 561 else
@@ -574,6 +566,9 @@ static void test_cipher(char *algo, int enc,
574 printk("test %u (%d bit key):\n", 566 printk("test %u (%d bit key):\n",
575 j, template[i].klen * 8); 567 j, template[i].klen * 8);
576 568
569 data = xbuf[0];
570 memcpy(data, template[i].input, template[i].ilen);
571
577 crypto_ablkcipher_clear_flags(tfm, ~0); 572 crypto_ablkcipher_clear_flags(tfm, ~0);
578 if (template[i].wk) 573 if (template[i].wk)
579 crypto_ablkcipher_set_flags( 574 crypto_ablkcipher_set_flags(
@@ -585,10 +580,8 @@ static void test_cipher(char *algo, int enc,
585 printk("setkey() failed flags=%x\n", 580 printk("setkey() failed flags=%x\n",
586 crypto_ablkcipher_get_flags(tfm)); 581 crypto_ablkcipher_get_flags(tfm));
587 582
588 if (!template[i].fail) { 583 if (!template[i].fail)
589 kfree(data);
590 goto out; 584 goto out;
591 }
592 } 585 }
593 586
594 sg_init_one(&sg[0], data, template[i].ilen); 587 sg_init_one(&sg[0], data, template[i].ilen);
@@ -613,7 +606,6 @@ static void test_cipher(char *algo, int enc,
613 /* fall through */ 606 /* fall through */
614 default: 607 default:
615 printk("%s () failed err=%d\n", e, -ret); 608 printk("%s () failed err=%d\n", e, -ret);
616 kfree(data);
617 goto out; 609 goto out;
618 } 610 }
619 611
@@ -624,7 +616,6 @@ static void test_cipher(char *algo, int enc,
624 memcmp(q, template[i].result, 616 memcmp(q, template[i].result,
625 template[i].rlen) ? "fail" : "pass"); 617 template[i].rlen) ? "fail" : "pass");
626 } 618 }
627 kfree(data);
628 } 619 }
629 620
630 printk("\ntesting %s %s across pages (chunking)\n", algo, e); 621 printk("\ntesting %s %s across pages (chunking)\n", algo, e);
@@ -642,7 +633,6 @@ static void test_cipher(char *algo, int enc,
642 printk("test %u (%d bit key):\n", 633 printk("test %u (%d bit key):\n",
643 j, template[i].klen * 8); 634 j, template[i].klen * 8);
644 635
645 memset(xbuf, 0, XBUFSIZE);
646 crypto_ablkcipher_clear_flags(tfm, ~0); 636 crypto_ablkcipher_clear_flags(tfm, ~0);
647 if (template[i].wk) 637 if (template[i].wk)
648 crypto_ablkcipher_set_flags( 638 crypto_ablkcipher_set_flags(
@@ -661,12 +651,23 @@ static void test_cipher(char *algo, int enc,
661 temp = 0; 651 temp = 0;
662 sg_init_table(sg, template[i].np); 652 sg_init_table(sg, template[i].np);
663 for (k = 0; k < template[i].np; k++) { 653 for (k = 0; k < template[i].np; k++) {
664 memcpy(&xbuf[IDX[k]], 654 if (WARN_ON(offset_in_page(IDX[k]) +
665 template[i].input + temp, 655 template[i].tap[k] > PAGE_SIZE))
666 template[i].tap[k]); 656 goto out;
657
658 q = xbuf[IDX[k] >> PAGE_SHIFT] +
659 offset_in_page(IDX[k]);
660
661 memcpy(q, template[i].input + temp,
662 template[i].tap[k]);
663
664 if (offset_in_page(q) + template[i].tap[k] <
665 PAGE_SIZE)
666 q[template[i].tap[k]] = 0;
667
668 sg_set_buf(&sg[k], q, template[i].tap[k]);
669
667 temp += template[i].tap[k]; 670 temp += template[i].tap[k];
668 sg_set_buf(&sg[k], &xbuf[IDX[k]],
669 template[i].tap[k]);
670 } 671 }
671 672
672 ablkcipher_request_set_crypt(req, sg, sg, 673 ablkcipher_request_set_crypt(req, sg, sg,
@@ -696,19 +697,21 @@ static void test_cipher(char *algo, int enc,
696 temp = 0; 697 temp = 0;
697 for (k = 0; k < template[i].np; k++) { 698 for (k = 0; k < template[i].np; k++) {
698 printk("page %u\n", k); 699 printk("page %u\n", k);
699 q = &xbuf[IDX[k]]; 700 q = xbuf[IDX[k] >> PAGE_SHIFT] +
701 offset_in_page(IDX[k]);
700 hexdump(q, template[i].tap[k]); 702 hexdump(q, template[i].tap[k]);
701 printk("%s\n", 703 printk("%s\n",
702 memcmp(q, template[i].result + temp, 704 memcmp(q, template[i].result + temp,
703 template[i].tap[k]) ? "fail" : 705 template[i].tap[k]) ? "fail" :
704 "pass"); 706 "pass");
705 707
706 for (n = 0; q[template[i].tap[k] + n]; n++) 708 q += template[i].tap[k];
709 for (n = 0; offset_in_page(q + n) && q[n]; n++)
707 ; 710 ;
708 if (n) { 711 if (n) {
709 printk("Result buffer corruption %u " 712 printk("Result buffer corruption %u "
710 "bytes:\n", n); 713 "bytes:\n", n);
711 hexdump(&q[template[i].tap[k]], n); 714 hexdump(q, n);
712 } 715 }
713 temp += template[i].tap[k]; 716 temp += template[i].tap[k];
714 } 717 }
@@ -719,16 +722,13 @@ out:
719 ablkcipher_request_free(req); 722 ablkcipher_request_free(req);
720} 723}
721 724
722static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc, char *p, 725static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc,
723 int blen, int sec) 726 struct scatterlist *sg, int blen, int sec)
724{ 727{
725 struct scatterlist sg[1];
726 unsigned long start, end; 728 unsigned long start, end;
727 int bcount; 729 int bcount;
728 int ret; 730 int ret;
729 731
730 sg_init_one(sg, p, blen);
731
732 for (start = jiffies, end = start + sec * HZ, bcount = 0; 732 for (start = jiffies, end = start + sec * HZ, bcount = 0;
733 time_before(jiffies, end); bcount++) { 733 time_before(jiffies, end); bcount++) {
734 if (enc) 734 if (enc)
@@ -745,16 +745,13 @@ static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc, char *p,
745 return 0; 745 return 0;
746} 746}
747 747
748static int test_cipher_cycles(struct blkcipher_desc *desc, int enc, char *p, 748static int test_cipher_cycles(struct blkcipher_desc *desc, int enc,
749 int blen) 749 struct scatterlist *sg, int blen)
750{ 750{
751 struct scatterlist sg[1];
752 unsigned long cycles = 0; 751 unsigned long cycles = 0;
753 int ret = 0; 752 int ret = 0;
754 int i; 753 int i;
755 754
756 sg_init_one(sg, p, blen);
757
758 local_bh_disable(); 755 local_bh_disable();
759 local_irq_disable(); 756 local_irq_disable();
760 757
@@ -804,7 +801,7 @@ static void test_cipher_speed(char *algo, int enc, unsigned int sec,
804 unsigned int tcount, u8 *keysize) 801 unsigned int tcount, u8 *keysize)
805{ 802{
806 unsigned int ret, i, j, iv_len; 803 unsigned int ret, i, j, iv_len;
807 unsigned char *key, *p, iv[128]; 804 unsigned char *key, iv[128];
808 struct crypto_blkcipher *tfm; 805 struct crypto_blkcipher *tfm;
809 struct blkcipher_desc desc; 806 struct blkcipher_desc desc;
810 const char *e; 807 const char *e;
@@ -832,27 +829,28 @@ static void test_cipher_speed(char *algo, int enc, unsigned int sec,
832 829
833 b_size = block_sizes; 830 b_size = block_sizes;
834 do { 831 do {
832 struct scatterlist sg[TVMEMSIZE];
835 833
836 if ((*keysize + *b_size) > TVMEMSIZE) { 834 if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) {
837 printk("template (%u) too big for tvmem (%u)\n", 835 printk("template (%u) too big for "
838 *keysize + *b_size, TVMEMSIZE); 836 "tvmem (%lu)\n", *keysize + *b_size,
837 TVMEMSIZE * PAGE_SIZE);
839 goto out; 838 goto out;
840 } 839 }
841 840
842 printk("test %u (%d bit key, %d byte blocks): ", i, 841 printk("test %u (%d bit key, %d byte blocks): ", i,
843 *keysize * 8, *b_size); 842 *keysize * 8, *b_size);
844 843
845 memset(tvmem, 0xff, *keysize + *b_size); 844 memset(tvmem[0], 0xff, PAGE_SIZE);
846 845
847 /* set key, plain text and IV */ 846 /* set key, plain text and IV */
848 key = (unsigned char *)tvmem; 847 key = (unsigned char *)tvmem[0];
849 for (j = 0; j < tcount; j++) { 848 for (j = 0; j < tcount; j++) {
850 if (template[j].klen == *keysize) { 849 if (template[j].klen == *keysize) {
851 key = template[j].key; 850 key = template[j].key;
852 break; 851 break;
853 } 852 }
854 } 853 }
855 p = (unsigned char *)tvmem + *keysize;
856 854
857 ret = crypto_blkcipher_setkey(tfm, key, *keysize); 855 ret = crypto_blkcipher_setkey(tfm, key, *keysize);
858 if (ret) { 856 if (ret) {
@@ -861,6 +859,14 @@ static void test_cipher_speed(char *algo, int enc, unsigned int sec,
861 goto out; 859 goto out;
862 } 860 }
863 861
862 sg_init_table(sg, TVMEMSIZE);
863 sg_set_buf(sg, tvmem[0] + *keysize,
864 PAGE_SIZE - *keysize);
865 for (j = 1; j < TVMEMSIZE; j++) {
866 sg_set_buf(sg + j, tvmem[j], PAGE_SIZE);
867 memset (tvmem[j], 0xff, PAGE_SIZE);
868 }
869
864 iv_len = crypto_blkcipher_ivsize(tfm); 870 iv_len = crypto_blkcipher_ivsize(tfm);
865 if (iv_len) { 871 if (iv_len) {
866 memset(&iv, 0xff, iv_len); 872 memset(&iv, 0xff, iv_len);
@@ -868,9 +874,11 @@ static void test_cipher_speed(char *algo, int enc, unsigned int sec,
868 } 874 }
869 875
870 if (sec) 876 if (sec)
871 ret = test_cipher_jiffies(&desc, enc, p, *b_size, sec); 877 ret = test_cipher_jiffies(&desc, enc, sg,
878 *b_size, sec);
872 else 879 else
873 ret = test_cipher_cycles(&desc, enc, p, *b_size); 880 ret = test_cipher_cycles(&desc, enc, sg,
881 *b_size);
874 882
875 if (ret) { 883 if (ret) {
876 printk("%s() failed flags=%x\n", e, desc.flags); 884 printk("%s() failed flags=%x\n", e, desc.flags);
@@ -886,19 +894,16 @@ out:
886 crypto_free_blkcipher(tfm); 894 crypto_free_blkcipher(tfm);
887} 895}
888 896
889static int test_hash_jiffies_digest(struct hash_desc *desc, char *p, int blen, 897static int test_hash_jiffies_digest(struct hash_desc *desc,
898 struct scatterlist *sg, int blen,
890 char *out, int sec) 899 char *out, int sec)
891{ 900{
892 struct scatterlist sg[1];
893 unsigned long start, end; 901 unsigned long start, end;
894 int bcount; 902 int bcount;
895 int ret; 903 int ret;
896 904
897 sg_init_table(sg, 1);
898
899 for (start = jiffies, end = start + sec * HZ, bcount = 0; 905 for (start = jiffies, end = start + sec * HZ, bcount = 0;
900 time_before(jiffies, end); bcount++) { 906 time_before(jiffies, end); bcount++) {
901 sg_set_buf(sg, p, blen);
902 ret = crypto_hash_digest(desc, sg, blen, out); 907 ret = crypto_hash_digest(desc, sg, blen, out);
903 if (ret) 908 if (ret)
904 return ret; 909 return ret;
@@ -910,18 +915,15 @@ static int test_hash_jiffies_digest(struct hash_desc *desc, char *p, int blen,
910 return 0; 915 return 0;
911} 916}
912 917
913static int test_hash_jiffies(struct hash_desc *desc, char *p, int blen, 918static int test_hash_jiffies(struct hash_desc *desc, struct scatterlist *sg,
914 int plen, char *out, int sec) 919 int blen, int plen, char *out, int sec)
915{ 920{
916 struct scatterlist sg[1];
917 unsigned long start, end; 921 unsigned long start, end;
918 int bcount, pcount; 922 int bcount, pcount;
919 int ret; 923 int ret;
920 924
921 if (plen == blen) 925 if (plen == blen)
922 return test_hash_jiffies_digest(desc, p, blen, out, sec); 926 return test_hash_jiffies_digest(desc, sg, blen, out, sec);
923
924 sg_init_table(sg, 1);
925 927
926 for (start = jiffies, end = start + sec * HZ, bcount = 0; 928 for (start = jiffies, end = start + sec * HZ, bcount = 0;
927 time_before(jiffies, end); bcount++) { 929 time_before(jiffies, end); bcount++) {
@@ -929,7 +931,6 @@ static int test_hash_jiffies(struct hash_desc *desc, char *p, int blen,
929 if (ret) 931 if (ret)
930 return ret; 932 return ret;
931 for (pcount = 0; pcount < blen; pcount += plen) { 933 for (pcount = 0; pcount < blen; pcount += plen) {
932 sg_set_buf(sg, p + pcount, plen);
933 ret = crypto_hash_update(desc, sg, plen); 934 ret = crypto_hash_update(desc, sg, plen);
934 if (ret) 935 if (ret)
935 return ret; 936 return ret;
@@ -946,22 +947,18 @@ static int test_hash_jiffies(struct hash_desc *desc, char *p, int blen,
946 return 0; 947 return 0;
947} 948}
948 949
949static int test_hash_cycles_digest(struct hash_desc *desc, char *p, int blen, 950static int test_hash_cycles_digest(struct hash_desc *desc,
950 char *out) 951 struct scatterlist *sg, int blen, char *out)
951{ 952{
952 struct scatterlist sg[1];
953 unsigned long cycles = 0; 953 unsigned long cycles = 0;
954 int i; 954 int i;
955 int ret; 955 int ret;
956 956
957 sg_init_table(sg, 1);
958
959 local_bh_disable(); 957 local_bh_disable();
960 local_irq_disable(); 958 local_irq_disable();
961 959
962 /* Warm-up run. */ 960 /* Warm-up run. */
963 for (i = 0; i < 4; i++) { 961 for (i = 0; i < 4; i++) {
964 sg_set_buf(sg, p, blen);
965 ret = crypto_hash_digest(desc, sg, blen, out); 962 ret = crypto_hash_digest(desc, sg, blen, out);
966 if (ret) 963 if (ret)
967 goto out; 964 goto out;
@@ -973,7 +970,6 @@ static int test_hash_cycles_digest(struct hash_desc *desc, char *p, int blen,
973 970
974 start = get_cycles(); 971 start = get_cycles();
975 972
976 sg_set_buf(sg, p, blen);
977 ret = crypto_hash_digest(desc, sg, blen, out); 973 ret = crypto_hash_digest(desc, sg, blen, out);
978 if (ret) 974 if (ret)
979 goto out; 975 goto out;
@@ -996,18 +992,15 @@ out:
996 return 0; 992 return 0;
997} 993}
998 994
999static int test_hash_cycles(struct hash_desc *desc, char *p, int blen, 995static int test_hash_cycles(struct hash_desc *desc, struct scatterlist *sg,
1000 int plen, char *out) 996 int blen, int plen, char *out)
1001{ 997{
1002 struct scatterlist sg[1];
1003 unsigned long cycles = 0; 998 unsigned long cycles = 0;
1004 int i, pcount; 999 int i, pcount;
1005 int ret; 1000 int ret;
1006 1001
1007 if (plen == blen) 1002 if (plen == blen)
1008 return test_hash_cycles_digest(desc, p, blen, out); 1003 return test_hash_cycles_digest(desc, sg, blen, out);
1009
1010 sg_init_table(sg, 1);
1011 1004
1012 local_bh_disable(); 1005 local_bh_disable();
1013 local_irq_disable(); 1006 local_irq_disable();
@@ -1018,7 +1011,6 @@ static int test_hash_cycles(struct hash_desc *desc, char *p, int blen,
1018 if (ret) 1011 if (ret)
1019 goto out; 1012 goto out;
1020 for (pcount = 0; pcount < blen; pcount += plen) { 1013 for (pcount = 0; pcount < blen; pcount += plen) {
1021 sg_set_buf(sg, p + pcount, plen);
1022 ret = crypto_hash_update(desc, sg, plen); 1014 ret = crypto_hash_update(desc, sg, plen);
1023 if (ret) 1015 if (ret)
1024 goto out; 1016 goto out;
@@ -1038,7 +1030,6 @@ static int test_hash_cycles(struct hash_desc *desc, char *p, int blen,
1038 if (ret) 1030 if (ret)
1039 goto out; 1031 goto out;
1040 for (pcount = 0; pcount < blen; pcount += plen) { 1032 for (pcount = 0; pcount < blen; pcount += plen) {
1041 sg_set_buf(sg, p + pcount, plen);
1042 ret = crypto_hash_update(desc, sg, plen); 1033 ret = crypto_hash_update(desc, sg, plen);
1043 if (ret) 1034 if (ret)
1044 goto out; 1035 goto out;
@@ -1068,6 +1059,7 @@ out:
1068static void test_hash_speed(char *algo, unsigned int sec, 1059static void test_hash_speed(char *algo, unsigned int sec,
1069 struct hash_speed *speed) 1060 struct hash_speed *speed)
1070{ 1061{
1062 struct scatterlist sg[TVMEMSIZE];
1071 struct crypto_hash *tfm; 1063 struct crypto_hash *tfm;
1072 struct hash_desc desc; 1064 struct hash_desc desc;
1073 char output[1024]; 1065 char output[1024];
@@ -1093,23 +1085,27 @@ static void test_hash_speed(char *algo, unsigned int sec,
1093 goto out; 1085 goto out;
1094 } 1086 }
1095 1087
1088 sg_init_table(sg, TVMEMSIZE);
1089 for (i = 0; i < TVMEMSIZE; i++) {
1090 sg_set_buf(sg + i, tvmem[i], PAGE_SIZE);
1091 memset(tvmem[i], 0xff, PAGE_SIZE);
1092 }
1093
1096 for (i = 0; speed[i].blen != 0; i++) { 1094 for (i = 0; speed[i].blen != 0; i++) {
1097 if (speed[i].blen > TVMEMSIZE) { 1095 if (speed[i].blen > TVMEMSIZE * PAGE_SIZE) {
1098 printk("template (%u) too big for tvmem (%u)\n", 1096 printk("template (%u) too big for tvmem (%lu)\n",
1099 speed[i].blen, TVMEMSIZE); 1097 speed[i].blen, TVMEMSIZE * PAGE_SIZE);
1100 goto out; 1098 goto out;
1101 } 1099 }
1102 1100
1103 printk("test%3u (%5u byte blocks,%5u bytes per update,%4u updates): ", 1101 printk("test%3u (%5u byte blocks,%5u bytes per update,%4u updates): ",
1104 i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); 1102 i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen);
1105 1103
1106 memset(tvmem, 0xff, speed[i].blen);
1107
1108 if (sec) 1104 if (sec)
1109 ret = test_hash_jiffies(&desc, tvmem, speed[i].blen, 1105 ret = test_hash_jiffies(&desc, sg, speed[i].blen,
1110 speed[i].plen, output, sec); 1106 speed[i].plen, output, sec);
1111 else 1107 else
1112 ret = test_hash_cycles(&desc, tvmem, speed[i].blen, 1108 ret = test_hash_cycles(&desc, sg, speed[i].blen,
1113 speed[i].plen, output); 1109 speed[i].plen, output);
1114 1110
1115 if (ret) { 1111 if (ret) {
@@ -1128,7 +1124,6 @@ static void test_comp(char *algo, struct comp_testvec *ctemplate,
1128 unsigned int i; 1124 unsigned int i;
1129 char result[COMP_BUF_SIZE]; 1125 char result[COMP_BUF_SIZE];
1130 struct crypto_comp *tfm; 1126 struct crypto_comp *tfm;
1131 unsigned int tsize;
1132 1127
1133 printk("\ntesting %s compression\n", algo); 1128 printk("\ntesting %s compression\n", algo);
1134 1129
@@ -1159,14 +1154,6 @@ static void test_comp(char *algo, struct comp_testvec *ctemplate,
1159 1154
1160 printk("\ntesting %s decompression\n", algo); 1155 printk("\ntesting %s decompression\n", algo);
1161 1156
1162 tsize = sizeof(struct comp_testvec);
1163 tsize *= dtcount;
1164 if (tsize > TVMEMSIZE) {
1165 printk("template (%u) too big for tvmem (%u)\n", tsize,
1166 TVMEMSIZE);
1167 goto out;
1168 }
1169
1170 for (i = 0; i < dtcount; i++) { 1157 for (i = 0; i < dtcount; i++) {
1171 int ilen, ret, dlen = COMP_BUF_SIZE; 1158 int ilen, ret, dlen = COMP_BUF_SIZE;
1172 1159
@@ -1185,7 +1172,7 @@ static void test_comp(char *algo, struct comp_testvec *ctemplate,
1185 memcmp(result, dtemplate[i].output, dlen) ? "fail" : "pass", 1172 memcmp(result, dtemplate[i].output, dlen) ? "fail" : "pass",
1186 ilen, dlen); 1173 ilen, dlen);
1187 } 1174 }
1188out: 1175
1189 crypto_free_comp(tfm); 1176 crypto_free_comp(tfm);
1190} 1177}
1191 1178
@@ -1917,18 +1904,25 @@ static void do_test(void)
1917static int __init tcrypt_mod_init(void) 1904static int __init tcrypt_mod_init(void)
1918{ 1905{
1919 int err = -ENOMEM; 1906 int err = -ENOMEM;
1907 int i;
1920 1908
1921 tvmem = kmalloc(TVMEMSIZE, GFP_KERNEL); 1909 for (i = 0; i < TVMEMSIZE; i++) {
1922 if (tvmem == NULL) 1910 tvmem[i] = (void *)__get_free_page(GFP_KERNEL);
1923 return err; 1911 if (!tvmem[i])
1912 goto err_free_tv;
1913 }
1924 1914
1925 xbuf = kmalloc(XBUFSIZE, GFP_KERNEL); 1915 for (i = 0; i < XBUFSIZE; i++) {
1926 if (xbuf == NULL) 1916 xbuf[i] = (void *)__get_free_page(GFP_KERNEL);
1927 goto err_free_tv; 1917 if (!xbuf[i])
1918 goto err_free_xbuf;
1919 }
1928 1920
1929 axbuf = kmalloc(XBUFSIZE, GFP_KERNEL); 1921 for (i = 0; i < XBUFSIZE; i++) {
1930 if (axbuf == NULL) 1922 axbuf[i] = (void *)__get_free_page(GFP_KERNEL);
1931 goto err_free_xbuf; 1923 if (!axbuf[i])
1924 goto err_free_axbuf;
1925 }
1932 1926
1933 do_test(); 1927 do_test();
1934 1928
@@ -1940,11 +1934,15 @@ static int __init tcrypt_mod_init(void)
1940 */ 1934 */
1941 err = -EAGAIN; 1935 err = -EAGAIN;
1942 1936
1943 kfree(axbuf); 1937err_free_axbuf:
1944 err_free_xbuf: 1938 for (i = 0; i < XBUFSIZE && axbuf[i]; i++)
1945 kfree(xbuf); 1939 free_page((unsigned long)axbuf[i]);
1946 err_free_tv: 1940err_free_xbuf:
1947 kfree(tvmem); 1941 for (i = 0; i < XBUFSIZE && xbuf[i]; i++)
1942 free_page((unsigned long)xbuf[i]);
1943err_free_tv:
1944 for (i = 0; i < TVMEMSIZE && tvmem[i]; i++)
1945 free_page((unsigned long)tvmem[i]);
1948 1946
1949 return err; 1947 return err;
1950} 1948}
diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h
index 801e0c288862..c6254a1776fe 100644
--- a/crypto/tcrypt.h
+++ b/crypto/tcrypt.h
@@ -39,7 +39,7 @@ struct cipher_testvec {
39 char *iv; 39 char *iv;
40 char *input; 40 char *input;
41 char *result; 41 char *result;
42 unsigned char tap[MAX_TAP]; 42 unsigned short tap[MAX_TAP];
43 int np; 43 int np;
44 unsigned char fail; 44 unsigned char fail;
45 unsigned char wk; /* weak key flag */ 45 unsigned char wk; /* weak key flag */
@@ -5111,6 +5111,8 @@ static struct cipher_testvec aes_ctr_enc_tv_template[] = {
5111 "\x4b\xef\x31\x18\xea\xac\xb1\x84" 5111 "\x4b\xef\x31\x18\xea\xac\xb1\x84"
5112 "\x21\xed\xda\x86", 5112 "\x21\xed\xda\x86",
5113 .rlen = 4100, 5113 .rlen = 4100,
5114 .np = 2,
5115 .tap = { 4064, 36 },
5114 }, 5116 },
5115}; 5117};
5116 5118
@@ -8126,7 +8128,9 @@ static struct cipher_testvec salsa20_stream_enc_tv_template[] = {
8126 "\xfc\x3f\x09\x7a\x0b\xdc\xc5\x1b" 8128 "\xfc\x3f\x09\x7a\x0b\xdc\xc5\x1b"
8127 "\x87\x13\xc6\x5b\x59\x8d\xf2\xc8" 8129 "\x87\x13\xc6\x5b\x59\x8d\xf2\xc8"
8128 "\xaf\xdf\x11\x95", 8130 "\xaf\xdf\x11\x95",
8129 .rlen = 4100, 8131 .rlen = 4100,
8132 .np = 2,
8133 .tap = { 4064, 36 },
8130 }, 8134 },
8131}; 8135};
8132 8136
rd 0x%x\n", init_crd, crd); while ((init_crd != crd) && count) { msleep(5); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); count--; } crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); if (init_crd != crd) { DP(NETIF_MSG_LINK, "BUG! init_crd 0x%x != crd 0x%x\n", init_crd, crd); return -EINVAL; } if (flow_ctrl & BNX2X_FLOW_CTRL_RX || line_speed == SPEED_10 || line_speed == SPEED_100 || line_speed == SPEED_1000 || line_speed == SPEED_2500) { REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 1); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, 0); /* update init credit */ init_crd = 778; /* (800-18-4) */ } else { u32 thresh = (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD)/16; REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, thresh); /* update init credit */ switch (line_speed) { case SPEED_10000: init_crd = thresh + 553 - 22; break; case SPEED_12000: init_crd = thresh + 664 - 22; break; case SPEED_13000: init_crd = thresh + 742 - 22; break; case SPEED_16000: init_crd = thresh + 778 - 22; break; default: DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; break; } } REG_WR(bp, PBF_REG_P0_INIT_CRD + port*4, init_crd); DP(NETIF_MSG_LINK, "PBF updated to speed %d credit %d\n", line_speed, init_crd); /* probe the credit changes */ REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x1); msleep(5); REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x0); /* enable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x0); return 0; } static u32 bnx2x_get_emac_base(u32 ext_phy_type, u8 port) { u32 emac_base; switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: emac_base = GRCBASE_EMAC0; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: emac_base = (port) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; break; default: emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; break; } return emac_base; } u8 bnx2x_cl45_write(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 val) { u32 tmp, saved_mode; u8 i, rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(ext_phy_type, port); /* set clause 45 mode, slow down the MDIO clock to 2.5MHz * (a value of 49==0x31) and make sure that the AUTO poll is off */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); tmp = saved_mode & ~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT); tmp |= (EMAC_MDIO_MODE_CLAUSE_45 | (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, tmp); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ tmp = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } else { /* data */ tmp = ((phy_addr << 21) | (devad << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } u8 bnx2x_cl45_read(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 *ret_val) { u32 val, saved_mode; u16 i; u8 rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(ext_phy_type, port); /* set clause 45 mode, slow down the MDIO clock to 2.5MHz * (a value of 49==0x31) and make sure that the AUTO poll is off */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); val = saved_mode & ((EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT)); val |= (EMAC_MDIO_MODE_CLAUSE_45 | (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, val); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ val = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } else { /* data */ val = ((phy_addr << 21) | (devad << 16) | EMAC_MDIO_COMM_COMMAND_READ_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { *ret_val = (u16)(val & EMAC_MDIO_COMM_DATA); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } static void bnx2x_set_aer_mmd(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ser_lane; u16 offset; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); offset = (vars->phy_flags & PHY_XGXS_FLAG) ? (params->phy_addr + ser_lane) : 0; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_AER_BLOCK, MDIO_AER_BLOCK_AER_REG, 0x3800 + offset); } static void bnx2x_set_master_ln(struct link_params *params) { struct bnx2x *bp = params->bp; u16 new_master_ln, ser_lane; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); /* set the master_ln for AN */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TEST_MODE_LANE, &new_master_ln); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2 , MDIO_XGXS_BLOCK2_TEST_MODE_LANE, (new_master_ln | ser_lane)); } static u8 bnx2x_reset_unicore(struct link_params *params) { struct bnx2x *bp = params->bp; u16 mii_control; u16 i; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); /* reset the unicore */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_RESET)); /* wait for the reset to self clear */ for (i = 0; i < MDIO_ACCESS_TIMEOUT; i++) { udelay(5); /* the reset erased the previous bank value */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); if (!(mii_control & MDIO_COMBO_IEEO_MII_CONTROL_RESET)) { udelay(5); return 0; } } DP(NETIF_MSG_LINK, "BUG! XGXS is still in reset!\n"); return -EINVAL; } static void bnx2x_set_swap_lanes(struct link_params *params) { struct bnx2x *bp = params->bp; /* Each two bits represents a lane number: No swap is 0123 => 0x1b no need to enable the swap */ u16 ser_lane, rx_lane_swap, tx_lane_swap; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); rx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_RX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_RX_SHIFT); tx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_TX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_TX_SHIFT); if (rx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, (rx_lane_swap | MDIO_XGXS_BLOCK2_RX_LN_SWAP_ENABLE | MDIO_XGXS_BLOCK2_RX_LN_SWAP_FORCE_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, 0); } if (tx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, (tx_lane_swap | MDIO_XGXS_BLOCK2_TX_LN_SWAP_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, 0); } } static void bnx2x_set_parallel_detection(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; u16 control2; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, &control2); control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, control2); if (phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "XGXS\n"); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK_CNT); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, &control2); control2 |= MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, control2); /* Disable parallel detection of HiG */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_CX4_XGXS | MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_HIGIG_XGXS); } } static void bnx2x_set_autoneg(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 reg_val; /* CL37 Autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val); /* CL37 Autoneg Enabled */ if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_AN_EN; else /* CL37 Autoneg Disabled */ reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* Enable/Disable Autodetection */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &reg_val); reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_SIGNAL_DETECT_EN; if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; else reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, reg_val); /* Enable TetonII and BAM autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, &reg_val); if (vars->line_speed == SPEED_AUTO_NEG) { /* Enable BAM aneg Mode and TetonII aneg Mode */ reg_val |= (MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } else { /* TetonII and BAM Autoneg Disabled */ reg_val &= ~(MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, reg_val); /* Enable Clause 73 Aneg */ if ((vars->line_speed == SPEED_AUTO_NEG) && (SUPPORT_CL73)) { /* Enable BAM Station Manager */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_BAM_CTRL1, (MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_EN | MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_STATION_MNGR_EN | MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_NP_AFTER_BP_EN)); /* Merge CL73 and CL37 aneg resolution */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_BAM_CTRL3, &reg_val); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_BAM_CTRL3, (reg_val | MDIO_CL73_USERB0_CL73_BAM_CTRL3_USE_CL73_HCD_MR)); /* Set the CL73 AN speed */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, &reg_val); /* In the SerDes we support only the 1G. In the XGXS we support the 10G KX4 but we currently do not support the KR */ if (vars->phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "XGXS\n"); /* 10G KX4 */ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4; } else { DP(NETIF_MSG_LINK, "SerDes\n"); /* 1000M KX */ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX; } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, reg_val); /* CL73 Autoneg Enabled */ reg_val = MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN; } else { /* CL73 Autoneg Disabled */ reg_val = 0; } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, reg_val); } /* program SerDes, forced speed */ static void bnx2x_program_serdes(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 reg_val; /* program duplex, disable autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val); reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN); if (params->req_duplex == DUPLEX_FULL) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* program speed - needed only if the speed is greater than 1G (2.5G or 10G) */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, &reg_val); /* clearing the speed value before setting the right speed */ DP(NETIF_MSG_LINK, "MDIO_REG_BANK_SERDES_DIGITAL = 0x%x\n", reg_val); reg_val &= ~(MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_MASK | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (!((vars->line_speed == SPEED_1000) || (vars->line_speed == SPEED_100) || (vars->line_speed == SPEED_10))) { reg_val |= (MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_156_25M | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (vars->line_speed == SPEED_10000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_10G_CX4; if (vars->line_speed == SPEED_13000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_13G; } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, reg_val); } static void bnx2x_set_brcm_cl37_advertisment(struct link_params *params) { struct bnx2x *bp = params->bp; u16 val = 0; /* configure the 48 bits for BAM AN */ /* set extended capabilities */ if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) val |= MDIO_OVER_1G_UP1_2_5G; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= MDIO_OVER_1G_UP1_10G; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP1, val); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP3, 0); } static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u32 *ieee_fc) { *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX; /* resolve pause mode and advertisement * Please refer to Table 28B-3 of the 802.3ab-1999 spec */ switch (params->req_flow_ctrl) { case BNX2X_FLOW_CTRL_AUTO: if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH) { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } else { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } break; case BNX2X_FLOW_CTRL_TX: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; break; case BNX2X_FLOW_CTRL_RX: case BNX2X_FLOW_CTRL_BOTH: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; break; case BNX2X_FLOW_CTRL_NONE: default: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE; break; } } static void bnx2x_set_ieee_aneg_advertisment(struct link_params *params, u32 ieee_fc) { struct bnx2x *bp = params->bp; /* for AN, we are always publishing full duplex */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, (u16)ieee_fc); } static void bnx2x_restart_autoneg(struct link_params *params) { struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg\n"); if (SUPPORT_CL73) { /* enable and restart clause 73 aneg */ u16 an_ctrl; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, &an_ctrl); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, (an_ctrl | MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN | MDIO_CL73_IEEEB0_CL73_AN_CONTROL_RESTART_AN)); } else { /* Enable and restart BAM/CL37 aneg */ u16 mii_control; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg mii_control before = 0x%x\n", mii_control); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN)); } } static void bnx2x_initialize_sgmii_process(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 control1; /* in SGMII mode, the unicore is always slave */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &control1); control1 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT; /* set sgmii mode (and not fiber) */ control1 &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_MSTR_MODE); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, control1); /* if forced speed */ if (!(vars->line_speed == SPEED_AUTO_NEG)) { /* set speed, disable autoneg */ u16 mii_control; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); mii_control &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_MASK| MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX); switch (vars->line_speed) { case SPEED_100: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_100; break; case SPEED_1000: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_1000; break; case SPEED_10: /* there is nothing to set for 10M */ break; default: /* invalid speed for SGMII */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", vars->line_speed); break; } /* setting the full duplex */ if (params->req_duplex == DUPLEX_FULL) mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, mii_control); } else { /* AN mode */ /* enable and restart AN */ bnx2x_restart_autoneg(params); } } /* * link management */ static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result) { /* LD LP */ switch (pause_result) { /* ASYM P ASYM P */ case 0xb: /* 1 0 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_TX; break; case 0xe: /* 1 1 1 0 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_RX; break; case 0x5: /* 0 1 0 1 */ case 0x7: /* 0 1 1 1 */ case 0xd: /* 1 1 0 1 */ case 0xf: /* 1 1 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH; break; default: break; } } static u8 bnx2x_ext_phy_resove_fc(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u16 ld_pause; /* local */ u16 lp_pause; /* link partner */ u16 an_complete; /* AN complete */ u16 pause_result; u8 ret = 0; u32 ext_phy_type; u8 port = params->port; ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read twice */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); if (an_complete & MDIO_AN_REG_STATUS_AN_COMPLETE) { ret = 1; bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LP_AUTO_NEG, &lp_pause); pause_result = (ld_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 8; pause_result |= (lp_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 10; DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x \n", pause_result); bnx2x_pause_resolve(vars, pause_result); if (vars->flow_ctrl == BNX2X_FLOW_CTRL_NONE && ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 7; bnx2x_pause_resolve(vars, pause_result); DP(NETIF_MSG_LINK, "Ext PHY CL37 pause result 0x%x \n", pause_result); } } return ret; } static void bnx2x_flow_ctrl_resolve(struct link_params *params, struct link_vars *vars, u32 gp_status) { struct bnx2x *bp = params->bp; u16 ld_pause; /* local driver */ u16 lp_pause; /* link partner */ u16 pause_result; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; /* resolve from gp_status in case of AN complete and not sgmii */ if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) && (!(vars->phy_flags & PHY_SGMII_FLAG)) && (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, &ld_pause); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7; DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result); bnx2x_pause_resolve(vars, pause_result); } else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (bnx2x_ext_phy_resove_fc(params, vars))) { return; } else { if (params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) vars->flow_ctrl = params->req_fc_auto_adv; else vars->flow_ctrl = params->req_flow_ctrl; } DP(NETIF_MSG_LINK, "flow_ctrl 0x%x\n", vars->flow_ctrl); } static u8 bnx2x_link_settings_status(struct link_params *params, struct link_vars *vars, u32 gp_status) { struct bnx2x *bp = params->bp; u16 new_line_speed; u8 rc = 0; vars->link_status = 0; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) { DP(NETIF_MSG_LINK, "phy link up gp_status=0x%x\n", gp_status); vars->phy_link_up = 1; vars->link_status |= LINK_STATUS_LINK_UP; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS) vars->duplex = DUPLEX_FULL; else vars->duplex = DUPLEX_HALF; bnx2x_flow_ctrl_resolve(params, vars, gp_status); switch (gp_status & GP_STATUS_SPEED_MASK) { case GP_STATUS_10M: new_line_speed = SPEED_10; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_10TFD; else vars->link_status |= LINK_10THD; break; case GP_STATUS_100M: new_line_speed = SPEED_100; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_100TXFD; else vars->link_status |= LINK_100TXHD; break; case GP_STATUS_1G: case GP_STATUS_1G_KX: new_line_speed = SPEED_1000; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_1000TFD; else vars->link_status |= LINK_1000THD; break; case GP_STATUS_2_5G: new_line_speed = SPEED_2500; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_2500TFD; else vars->link_status |= LINK_2500THD; break; case GP_STATUS_5G: case GP_STATUS_6G: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; break; case GP_STATUS_10G_KX4: case GP_STATUS_10G_HIG: case GP_STATUS_10G_CX4: new_line_speed = SPEED_10000; vars->link_status |= LINK_10GTFD; break; case GP_STATUS_12G_HIG: new_line_speed = SPEED_12000; vars->link_status |= LINK_12GTFD; break; case GP_STATUS_12_5G: new_line_speed = SPEED_12500; vars->link_status |= LINK_12_5GTFD; break; case GP_STATUS_13G: new_line_speed = SPEED_13000; vars->link_status |= LINK_13GTFD; break; case GP_STATUS_15G: new_line_speed = SPEED_15000; vars->link_status |= LINK_15GTFD; break; case GP_STATUS_16G: new_line_speed = SPEED_16000; vars->link_status |= LINK_16GTFD; break; default: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; break; } /* Upon link speed change set the NIG into drain mode. Comes to deals with possible FIFO glitch due to clk change when speed is decreased without link down indicator */ if (new_line_speed != vars->line_speed) { REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); msleep(1); } vars->line_speed = new_line_speed; vars->link_status |= LINK_STATUS_SERDES_LINK; if ((params->req_line_speed == SPEED_AUTO_NEG) && ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705))) { vars->autoneg = AUTO_NEG_ENABLED; if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { vars->autoneg |= AUTO_NEG_COMPLETE; vars->link_status |= LINK_STATUS_AUTO_NEGOTIATE_COMPLETE; } vars->autoneg |= AUTO_NEG_PARALLEL_DETECTION_USED; vars->link_status |= LINK_STATUS_PARALLEL_DETECTION_USED; } if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) vars->link_status |= LINK_STATUS_TX_FLOW_CONTROL_ENABLED; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) vars->link_status |= LINK_STATUS_RX_FLOW_CONTROL_ENABLED; } else { /* link_down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->autoneg = AUTO_NEG_DISABLED; vars->mac_type = MAC_TYPE_NONE; } DP(NETIF_MSG_LINK, "gp_status 0x%x phy_link_up %x line_speed %x \n", gp_status, vars->phy_link_up, vars->line_speed); DP(NETIF_MSG_LINK, "duplex %x flow_ctrl 0x%x" " autoneg 0x%x\n", vars->duplex, vars->flow_ctrl, vars->autoneg); DP(NETIF_MSG_LINK, "link_status 0x%x\n", vars->link_status); return rc; } static void bnx2x_set_sgmii_tx_driver(struct link_params *params) { struct bnx2x *bp = params->bp; u16 lp_up2; u16 tx_driver; /* read precomp */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_LP_UP2, &lp_up2); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_TX0, MDIO_TX0_TX_DRIVER, &tx_driver); /* bits [10:7] at lp_up2, positioned at [15:12] */ lp_up2 = (((lp_up2 & MDIO_OVER_1G_LP_UP2_PREEMPHASIS_MASK) >> MDIO_OVER_1G_LP_UP2_PREEMPHASIS_SHIFT) << MDIO_TX0_TX_DRIVER_PREEMPHASIS_SHIFT); if ((lp_up2 != 0) && (lp_up2 != (tx_driver & MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK))) { /* replace tx_driver bits [15:12] */ tx_driver &= ~MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK; tx_driver |= lp_up2; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_TX0, MDIO_TX0_TX_DRIVER, tx_driver); } } static u8 bnx2x_emac_program(struct link_params *params, u32 line_speed, u32 duplex) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 mode = 0; DP(NETIF_MSG_LINK, "setting link speed & duplex\n"); bnx2x_bits_dis(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_MII_10M | EMAC_MODE_HALF_DUPLEX)); switch (line_speed) { case SPEED_10: mode |= EMAC_MODE_PORT_MII_10M; break; case SPEED_100: mode |= EMAC_MODE_PORT_MII; break; case SPEED_1000: mode |= EMAC_MODE_PORT_GMII; break; case SPEED_2500: mode |= (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_GMII); break; default: /* 10G not valid for EMAC */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; } if (duplex == DUPLEX_HALF) mode |= EMAC_MODE_HALF_DUPLEX; bnx2x_bits_en(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, mode); bnx2x_set_led(bp, params->port, LED_MODE_OPER, line_speed, params->hw_led_mode, params->chip_id); return 0; } /*****************************************************************************/ /* External Phy section */ /*****************************************************************************/ static void bnx2x_hw_reset(struct bnx2x *bp, u8 port) { bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); msleep(1); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); } static void bnx2x_ext_phy_reset(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); DP(NETIF_MSG_LINK, "Port %x: bnx2x_ext_phy_reset\n", params->port); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* The PHY reset is controled by GPIO 1 * Give it 1ms of reset pulse */ if (vars->phy_flags & PHY_XGXS_FLAG) { switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8705/8706\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_hw_reset(bp, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0xa040); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: /* Unset Low Power Mode and SW reset */ /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); DP(NETIF_MSG_LINK, "XGXS 8072\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 emac_base; emac_base = (params->port) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); DP(NETIF_MSG_LINK, "XGXS 8073\n"); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DP(NETIF_MSG_LINK, "XGXS SFX7101\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_hw_reset(bp, params->port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected\n"); break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); bnx2x_hw_reset(bp, params->port); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } } static void bnx2x_bcm8072_external_rom_boot(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); u16 fw_ver1, fw_ver2; /* Need to wait 200ms after reset */ msleep(200); /* Boot port from external ROM * Set ser_boot_ctl bit in the MISC_CTRL1 register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* set micro reset = 0 */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); /* Wait 100ms */ msleep(100); /* Print the PHY FW version */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &fw_ver2); DP(NETIF_MSG_LINK, "8072 FW version 0x%x:0x%x\n", fw_ver1, fw_ver2); } static u8 bnx2x_8073_is_snr_needed(struct link_params *params) { /* This is only required for 8073A1, version 102 only */ struct bnx2x *bp = params->bp; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u16 val; /* Read 8073 HW revision*/ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc801, &val); if (val != 1) { /* No need to workaround in 8073 A1 */ return 0; } bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &val); /* SNR should be applied only for version 0x102 */ if (val != 0x102) return 0; return 1; } static u8 bnx2x_bcm8073_xaui_wa(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u16 val, cnt, cnt1 ; bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc801, &val); if (val > 0) { /* No need to workaround in 8073 A1 */ return 0; } /* XAUI workaround in 8073 A0: */ /* After loading the boot ROM and restarting Autoneg, poll Dev1, Reg $C820: */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc820, &val); /* If bit [14] = 0 or bit [13] = 0, continue on with system initialization (XAUI work-around not required, as these bits indicate 2.5G or 1G link up). */ if (!(val & (1<<14)) || !(val & (1<<13))) { DP(NETIF_MSG_LINK, "XAUI work-around not required\n"); return 0; } else if (!(val & (1<<15))) { DP(NETIF_MSG_LINK, "clc bit 15 went off\n"); /* If bit 15 is 0, then poll Dev1, Reg $C841 until it's MSB (bit 15) goes to 1 (indicating that the XAUI workaround has completed), then continue on with system initialization.*/ for (cnt1 = 0; cnt1 < 1000; cnt1++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc841, &val); if (val & (1<<15)) { DP(NETIF_MSG_LINK, "XAUI workaround has completed\n"); return 0; } msleep(3); } break; } msleep(3); } DP(NETIF_MSG_LINK, "Warning: XAUI work-around timeout !!!\n"); return -EINVAL; } static void bnx2x_bcm8073_external_rom_boot(struct bnx2x *bp, u8 port, u8 ext_phy_addr) { u16 fw_ver1, fw_ver2; /* Boot port from external ROM */ /* EDC grst */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x0001); /* ucode reboot and rst */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x008c); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Release srst bit */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &fw_ver2); DP(NETIF_MSG_LINK, "8073 FW version 0x%x:0x%x\n", fw_ver1, fw_ver2); } static void bnx2x_bcm807x_force_10G(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Force KR or KX */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0x000b); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x0000); } static void bnx2x_bcm8073_set_xaui_low_power_mode(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc801, &val); if (val == 0) { /* Mustn't set low power mode in 8073 A0 */ return; } /* Disable PLL sequencer (use read-modify-write to clear bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val &= ~(1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); /* PLL controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805E, 0x1077); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805D, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805C, 0x030B); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805B, 0x1240); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805A, 0x2490); /* Tx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A7, 0x0C74); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A6, 0x9041); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A5, 0x4640); /* Rx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FE, 0x01C4); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FD, 0x9249); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FC, 0x2015); /* Enable PLL sequencer (use read-modify-write to set bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val |= (1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); } static void bnx2x_8073_set_pause_cl37(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 cl37_val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &cl37_val); cl37_val &= ~MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } DP(NETIF_MSG_LINK, "Ext phy AN advertize cl37 0x%x\n", cl37_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, cl37_val); msleep(500); } static void bnx2x_ext_phy_set_pause(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read modify write pause advertizing */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val); val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { val |= MDIO_AN_REG_ADV_PAUSE_PAUSE; } DP(NETIF_MSG_LINK, "Ext phy AN advertize 0x%x\n", val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, val); } static void bnx2x_init_internal_phy(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; if (!(vars->phy_flags & PHY_SGMII_FLAG)) { u16 bank, rx_eq; rx_eq = ((params->serdes_config & PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_MASK) >> PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_SHIFT); DP(NETIF_MSG_LINK, "setting rx eq to 0x%x\n", rx_eq); for (bank = MDIO_REG_BANK_RX0; bank <= MDIO_REG_BANK_RX_ALL; bank += (MDIO_REG_BANK_RX1-MDIO_REG_BANK_RX0)) { CL45_WR_OVER_CL22(bp, port, params->phy_addr, bank , MDIO_RX0_RX_EQ_BOOST, ((rx_eq & MDIO_RX0_RX_EQ_BOOST_EQUALIZER_CTRL_MASK) | MDIO_RX0_RX_EQ_BOOST_OFFSET_CTRL)); } /* forced speed requested? */ if (vars->line_speed != SPEED_AUTO_NEG) { DP(NETIF_MSG_LINK, "not SGMII, no AN\n"); /* disable autoneg */ bnx2x_set_autoneg(params, vars); /* program speed and duplex */ bnx2x_program_serdes(params, vars); } else { /* AN_mode */ DP(NETIF_MSG_LINK, "not SGMII, AN\n"); /* AN enabled */ bnx2x_set_brcm_cl37_advertisment(params); /* program duplex & pause advertisement (for aneg) */ bnx2x_set_ieee_aneg_advertisment(params, vars->ieee_fc); /* enable autoneg */ bnx2x_set_autoneg(params, vars); /* enable and restart AN */ bnx2x_restart_autoneg(params); } } else { /* SGMII mode */ DP(NETIF_MSG_LINK, "SGMII\n"); bnx2x_initialize_sgmii_process(params, vars); } } static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 cnt; u16 ctrl = 0; u16 val = 0; u8 rc = 0; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Make sure that the soft reset is off (expect for the 8072: * due to the lock, it will be done inside the specific * handling) */ if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) { /* Wait for soft reset to get cleared upto 1 sec */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) break; msleep(1); } DP(NETIF_MSG_LINK, "control reg 0x%x (after %d ms)\n", ctrl, cnt); } switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL, 0x8288); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, 0x7fbf); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CMU_PLL_BYPASS, 0x0100); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_CNTL, 0x1); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8706\n"); msleep(10); /* Force speed */ /* First enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x0400); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x0004); if (params->req_line_speed == SPEED_10000) { DP(NETIF_MSG_LINK, "XGXS 8706 force 10Gbps\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_DIGITAL_CTRL, 0x400); } else { /* Force 1Gbps using autoneg with 1G advertisment */ /* Allow CL37 through CL73 */ DP(NETIF_MSG_LINK, "XGXS 8706 AutoNeg\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_CL73, 0x040c); /* Enable Full-Duplex advertisment on CL37 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, 0x0020); /* Enable CL37 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); /* 1G support */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, (1<<5)); /* Enable clause 73 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 tmp1; u16 rx_alarm_ctrl_val; u16 lasi_ctrl_val; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { rx_alarm_ctrl_val = 0x400; lasi_ctrl_val = 0x0004; } else { rx_alarm_ctrl_val = (1<<2); lasi_ctrl_val = 0x0004; } /* enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, rx_alarm_ctrl_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, lasi_ctrl_val); bnx2x_8073_set_pause_cl37(params, vars); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072){ bnx2x_bcm8072_external_rom_boot(params); } else { /* In case of 8073 with long xaui lines, don't set the 8073 xaui low power*/ bnx2x_bcm8073_set_xaui_low_power_mode(params); } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, 0xca13, &tmp1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1); DP(NETIF_MSG_LINK, "Before rom RX_ALARM(port1):" "0x%x\n", tmp1); /* If this is forced speed, set to KR or KX * (all other are not supported) */ if (params->loopback_mode == LOOPBACK_EXT) { bnx2x_bcm807x_force_10G(params); DP(NETIF_MSG_LINK, "Forced speed 10G on 807X\n"); break; } else { bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0002); } if (params->req_line_speed != SPEED_AUTO_NEG) { if (params->req_line_speed == SPEED_10000) { val = (1<<7); } else if (params->req_line_speed == SPEED_2500) { val = (1<<5); /* Note that 2.5G works only when used with 1G advertisment */ } else val = (1<<5); } else { val = 0; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= (1<<7); /* Note that 2.5G works only when used with 1G advertisment */ if (params->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_1G | PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G)) val |= (1<<5); DP(NETIF_MSG_LINK, "807x autoneg val = 0x%x\n", val); } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, val); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8329, &tmp1); if (((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) && (params->req_line_speed == SPEED_AUTO_NEG)) || (params->req_line_speed == SPEED_2500)) { u16 phy_ver; /* Allow 2.5G for A1 and above */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, 0xc801, &phy_ver); DP(NETIF_MSG_LINK, "Add 2.5G\n"); if (phy_ver > 0) tmp1 |= 1; else tmp1 &= 0xfffe; } else { DP(NETIF_MSG_LINK, "Disable 2.5G\n"); tmp1 &= 0xfffe; } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8329, tmp1); } /* Add support for CL37 (passive mode) II */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &tmp1); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, (tmp1 | ((params->req_duplex == DUPLEX_FULL) ? 0x20 : 0x40))); /* Add support for CL37 (passive mode) III */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { /* The SNR will improve about 2db by changing BW and FEE main tap. Rest commands are executed after link is up*/ /*Change FFE main cursor to 5 in EDC register*/ if (bnx2x_8073_is_snr_needed(params)) bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, 0xFB0C); /* Enable FEC (Forware Error Correction) Request in the AN */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, &tmp1); tmp1 |= (1<<15); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, tmp1); } bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ msleep(500); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); DP(NETIF_MSG_LINK, "807x Autoneg Restart: " "Advertise 1G=%x, 10G=%x\n", ((val & (1<<5)) > 0), ((val & (1<<7)) > 0)); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DP(NETIF_MSG_LINK, "Setting the SFX7101 LASI indication\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x1); DP(NETIF_MSG_LINK, "Setting the SFX7101 LED to blink on traffic\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7107_LED_CNTL, (1<<3)); bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, &val); val |= 0x200; bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, val); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } return rc; } static u8 bnx2x_ext_phy_is_link_up(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 val1 = 0, val2; u16 rx_sd, pcs_status; u8 ext_phy_link_up = 0; u8 port = params->port; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); DP(NETIF_MSG_LINK, "8705 rx_sd 0x%x\n", rx_sd); ext_phy_link_up = (rx_sd & 0x1); if (ext_phy_link_up) vars->line_speed = SPEED_10000; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8706\n"); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8706 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8706 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &pcs_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); DP(NETIF_MSG_LINK, "8706 rx_sd 0x%x" " pcs_status 0x%x 1Gbps link_status 0x%x\n", rx_sd, pcs_status, val2); /* link is up if both bit 0 of pmd_rx_sd and * bit 0 of pcs_status are set, or if the autoneg bit 1 is set */ ext_phy_link_up = ((rx_sd & pcs_status & 0x1) || (val2 & (1<<1))); if (ext_phy_link_up) { if (val2 & (1<<1)) vars->line_speed = SPEED_1000; else vars->line_speed = SPEED_10000; } /* clear LASI indication*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 link_status = 0; u16 an1000_status = 0; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val2); DP(NETIF_MSG_LINK, "870x LASI status 0x%x->0x%x\n", val1, val2); } else { /* In 8073, port1 is directed through emac0 and * port0 is directed through emac1 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8703 LASI status 0x%x\n", val1); } /* clear the interrupt LASI status register */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "807x PCS status 0x%x->0x%x\n", val2, val1); /* Clear MSG-OUT */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, 0xca13, &val1); /* Check the LASI */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); DP(NETIF_MSG_LINK, "KR 0x9003 0x%x\n", val2); /* Check the link status */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); DP(NETIF_MSG_LINK, "KR PCS status 0x%x\n", val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); ext_phy_link_up = ((val1 & 4) == 4); DP(NETIF_MSG_LINK, "PMA_REG_STATUS=0x%x\n", val1); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { if (ext_phy_link_up && ((params->req_line_speed != SPEED_10000))) { if (bnx2x_bcm8073_xaui_wa(params) != 0) { ext_phy_link_up = 0; break; } } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8304, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8304, &an1000_status); /* Check the link status on 1.1.2 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "KR PMA status 0x%x->0x%x," "an_link_status=0x%x\n", val2, val1, an1000_status); ext_phy_link_up = (((val1 & 4) == 4) || (an1000_status & (1<<1))); if (ext_phy_link_up && bnx2x_8073_is_snr_needed(params)) { /* The SNR will improve about 2dbby changing the BW and FEE main tap.*/ /* The 1st write to change FFE main tap is set before restart AN */ /* Change PLL Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PLL_BANDWIDTH, 0x26BC); /* Change CDR Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CDR_BANDWIDTH, 0x0333); } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, 0xc820, &link_status); /* Bits 0..2 --> speed detected, bits 13..15--> link is down */ if ((link_status & (1<<2)) && (!(link_status & (1<<15)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } else if ((link_status & (1<<1)) && (!(link_status & (1<<14)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_2500; DP(NETIF_MSG_LINK, "port %x: External link" " up in 2.5G\n", params->port); } else if ((link_status & (1<<0)) && (!(link_status & (1<<13)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else { ext_phy_link_up = 0; DP(NETIF_MSG_LINK, "port %x: External link" " is down\n", params->port); } } else { /* See if 1G link is up for the 8072 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8304, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x8304, &an1000_status); if (an1000_status & (1<<1)) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else if (ext_phy_link_up) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } } break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T LASI status 0x%x->0x%x\n", val2, val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T PMA status 0x%x->0x%x\n", val2, val1); ext_phy_link_up = ((val1 & 4) == 4); /* if link is up * print the AN outcome of the SFX7101 PHY */ if (ext_phy_link_up) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_MASTER_STATUS, &val2); vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "SFX7101 AN status 0x%x->Master=%x\n", val2, (val2 & (1<<14))); } break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); ext_phy_link_up = 1; break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } } return ext_phy_link_up; } static void bnx2x_link_int_enable(struct link_params *params) { u8 port = params->port; u32 ext_phy_type; u32 mask; struct bnx2x *bp = params->bp; /* setting the status to report on link up for either XGXS or SerDes */ if (params->switch_cfg == SWITCH_CFG_10G) { mask = (NIG_MASK_XGXS0_LINK10G | NIG_MASK_XGXS0_LINK_STATUS); DP(NETIF_MSG_LINK, "enabled XGXS interrupt\n"); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } else { /* SerDes */ mask = NIG_MASK_SERDES0_LINK_STATUS; DP(NETIF_MSG_LINK, "enabled SerDes interrupt\n"); ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } bnx2x_bits_en(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, mask); DP(NETIF_MSG_LINK, "port %x, is_xgxs=%x, int_status 0x%x\n", port, (params->switch_cfg == SWITCH_CFG_10G), REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4)); DP(NETIF_MSG_LINK, " int_mask 0x%x, MI_INT %x, SERDES_LINK %x\n", REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), REG_RD(bp, NIG_REG_SERDES0_STATUS_LINK_STATUS+port*0x3c)); DP(NETIF_MSG_LINK, " 10G %x, XGXS_LINK %x\n", REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); } /* * link management */ static void bnx2x_link_int_ack(struct link_params *params, struct link_vars *vars, u8 is_10g) { struct bnx2x *bp = params->bp; u8 port = params->port; /* first reset all status * we assume only one line will be change at a time */ bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); if (vars->phy_link_up) { if (is_10g) { /* Disable the 10G link interrupt * by writing 1 to the status register */ DP(NETIF_MSG_LINK, "10G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_XGXS0_LINK10G); } else if (params->switch_cfg == SWITCH_CFG_10G) { /* Disable the link interrupt * by writing 1 to the relevant lane * in the status register */ u32 ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); DP(NETIF_MSG_LINK, "1G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, ((1 << ser_lane) << NIG_STATUS_XGXS0_LINK_STATUS_SIZE)); } else { /* SerDes */ DP(NETIF_MSG_LINK, "SerDes phy link up\n"); /* Disable the link interrupt * by writing 1 to the status register */ bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_SERDES0_LINK_STATUS); } } else { /* link_down */ } } static u8 bnx2x_format_ver(u32 num, u8 *str, u16 len) { u8 *str_ptr = str; u32 mask = 0xf0000000; u8 shift = 8*4; u8 digit; if (len < 10) { /* Need more than 10chars for this format */ *str_ptr = '\0'; return -EINVAL; } while (shift > 0) { shift -= 4; digit = ((num & mask) >> shift); if (digit < 0xa) *str_ptr = digit + '0'; else *str_ptr = digit - 0xa + 'a'; str_ptr++; mask = mask >> 4; if (shift == 4*4) { *str_ptr = ':'; str_ptr++; } } *str_ptr = '\0'; return 0; } static void bnx2x_turn_on_ef(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 ext_phy_type) { u32 cnt = 0; u16 ctrl = 0; /* Enable EMAC0 in to enable MDIO */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port)); msleep(5); /* take ext phy out of reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_HIGH, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_HIGH, port); /* wait for 5ms */ msleep(5); for (cnt = 0; cnt < 1000; cnt++) { msleep(1); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) { DP(NETIF_MSG_LINK, "Reset completed\n\n"); break; } } } static void bnx2x_turn_off_sf(struct bnx2x *bp, u8 port) { /* put sf to reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_LOW, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_LOW, port); } u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, u8 *version, u16 len) { struct bnx2x *bp = params->bp; u32 ext_phy_type = 0; u16 val = 0; u8 ext_phy_addr = 0 ; u8 status = 0 ; u32 ver_num; if (version == NULL || params == NULL) return -EINVAL; /* reset the returned value to zero */ ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: if (len < 5) return -EINVAL; /* Take ext phy out of reset */ if (!driver_loaded) bnx2x_turn_on_ef(bp, params->port, ext_phy_addr, ext_phy_type); /* wait for 1ms */ msleep(1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER1, &val); version[2] = (val & 0xFF); version[3] = ((val & 0xFF00)>>8); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER2, &val); version[0] = (val & 0xFF); version[1] = ((val & 0xFF00)>>8); version[4] = '\0'; if (!driver_loaded) bnx2x_turn_off_sf(bp, params->port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { /* Take ext phy out of reset */ if (!driver_loaded) bnx2x_turn_on_ef(bp, params->port, ext_phy_addr, ext_phy_type); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &val); ver_num = val<<16; bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &val); ver_num |= val; status = bnx2x_format_ver(ver_num, version, len); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &val); ver_num = val<<16; bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &val); ver_num |= val; status = bnx2x_format_ver(ver_num, version, len); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "bnx2x_get_ext_phy_fw_version:" " type is FAILURE!\n"); status = -EINVAL; break; default: break; } return status; } static void bnx2x_set_xgxs_loopback(struct link_params *params, struct link_vars *vars, u8 is_10g) { u8 port = params->port; struct bnx2x *bp = params->bp; if (is_10g) { u32 md_devad; DP(NETIF_MSG_LINK, "XGXS 10G loopback enable\n"); /* change the uni_phy_addr in the nig */ md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18)); REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, 0x5); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_AER_BLOCK + (MDIO_AER_BLOCK_AER_REG & 0xf)), 0x2800); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_CL73_IEEEB0 + (MDIO_CL73_IEEEB0_CL73_AN_CONTROL & 0xf)), 0x6041); msleep(200); /* set aer mmd back */ bnx2x_set_aer_mmd(params, vars); /* and md_devad */ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, md_devad); } else { u16 mii_control; DP(NETIF_MSG_LINK, "XGXS 1G loopback enable\n"); CL45_RD_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); CL45_WR_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_LOOPBACK)); } } static void bnx2x_ext_phy_loopback(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u32 ext_phy_type; if (params->switch_cfg == SWITCH_CFG_10G) { ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* CL37 Autoneg Enabled */ ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: DP(NETIF_MSG_LINK, "ext_phy_loopback: We should not get here\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8705\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8706\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* SFX7101_XGXS_TEST1 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_SFX7101_XGXS_TEST1, 0x100); DP(NETIF_MSG_LINK, "ext_phy_loopback: set ext phy loopback\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: break; } /* switch external PHY type */ } else { /* serdes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); ext_phy_addr = (params->ext_phy_config & PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT; } } /* *------------------------------------------------------------------------ * bnx2x_override_led_value - * * Override the led value of the requsted led * *------------------------------------------------------------------------ */ u8 bnx2x_override_led_value(struct bnx2x *bp, u8 port, u32 led_idx, u32 value) { u32 reg_val; /* If port 0 then use EMAC0, else use EMAC1*/ u32 emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_override_led_value() port %x led_idx %d value %d\n", port, led_idx, value); switch (led_idx) { case 0: /* 10MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 10M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_10MB_OVERRIDE) : (reg_val & ~EMAC_LED_10MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 1: /*100MB led */ /*Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 100M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_100MB_OVERRIDE) : (reg_val & ~EMAC_LED_100MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 2: /* 1000MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 1000M_OVERRIDE bit, otherwise reset it. */ reg_val = (value == 1) ? (reg_val | EMAC_LED_1000MB_OVERRIDE) : (reg_val & ~EMAC_LED_1000MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 3: /* 2500MB led */ /* Read the current value of the LED register in the EMAC block*/ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 2500M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_2500MB_OVERRIDE) : (reg_val & ~EMAC_LED_2500MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 4: /*10G led */ if (port == 0) { REG_WR(bp, NIG_REG_LED_10G_P0, value); } else { REG_WR(bp, NIG_REG_LED_10G_P1, value); } break; case 5: /* TRAFFIC led */ /* Find if the traffic control is via BMAC or EMAC */ if (port == 0) reg_val = REG_RD(bp, NIG_REG_NIG_EMAC0_EN); else reg_val = REG_RD(bp, NIG_REG_NIG_EMAC1_EN); /* Override the traffic led in the EMAC:*/ if (reg_val == 1) { /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the TRAFFIC_OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the TRAFFIC bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_TRAFFIC) : (reg_val & ~EMAC_LED_TRAFFIC); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); } else { /* Override the traffic led in the BMAC: */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, value); } break; default: DP(NETIF_MSG_LINK, "bnx2x_override_led_value() unknown led index %d " "(should be 0-5)\n", led_idx); return -EINVAL; } return 0; } u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, u16 hw_led_mode, u32 chip_id) { u8 rc = 0; u32 tmp; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_set_led: port %x, mode %d\n", port, mode); DP(NETIF_MSG_LINK, "speed 0x%x, hw_led_mode 0x%x\n", speed, hw_led_mode); switch (mode) { case LED_MODE_OFF: REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, SHARED_HW_CFG_LED_MAC1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp | EMAC_LED_OVERRIDE)); break; case LED_MODE_OPER: REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode); REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0); /* Set blinking rate to ~15.9Hz */ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4, LED_BLINK_RATE_VAL); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE))); if (!CHIP_IS_E1H(bp) && ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || (speed == SPEED_10))) { /* On Everest 1 Ax chip versions for speeds less than 10G LED scheme is different */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_TRAFFIC_P0 + port*4, 1); } break; default: rc = -EINVAL; DP(NETIF_MSG_LINK, "bnx2x_set_led: Invalid led mode %d\n", mode); break; } return rc; } u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 gp_status = 0; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); /* link is up only if both local phy and external phy are up */ if ((gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) && bnx2x_ext_phy_is_link_up(params, vars)) return 0; return -ESRCH; } static u8 bnx2x_link_initialize(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 rc = 0; u8 non_ext_phy; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Activate the external PHY */ bnx2x_ext_phy_reset(params, vars); bnx2x_set_aer_mmd(params, vars); if (vars->phy_flags & PHY_XGXS_FLAG) bnx2x_set_master_ln(params); rc = bnx2x_reset_unicore(params); /* reset the SerDes and wait for reset bit return low */ if (rc != 0) return rc; bnx2x_set_aer_mmd(params, vars); /* setting the masterLn_def again after the reset */ if (vars->phy_flags & PHY_XGXS_FLAG) { bnx2x_set_master_ln(params); bnx2x_set_swap_lanes(params); } if (vars->phy_flags & PHY_XGXS_FLAG) { if ((params->req_line_speed && ((params->req_line_speed == SPEED_100) || (params->req_line_speed == SPEED_10))) || (!params->req_line_speed && (params->speed_cap_mask >= PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) && (params->speed_cap_mask < PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) )) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; } } /* In case of external phy existance, the line speed would be the line speed linked up by the external phy. In case it is direct only, then the line_speed during initialization will be equal to the req_line_speed*/ vars->line_speed = params->req_line_speed; bnx2x_calc_ieee_aneg_adv(params, &vars->ieee_fc); /* init ext phy and enable link state int */ non_ext_phy = ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (params->loopback_mode == LOOPBACK_XGXS_10) || (params->loopback_mode == LOOPBACK_EXT_PHY)); if (non_ext_phy || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705)) { if (params->req_line_speed == SPEED_AUTO_NEG) bnx2x_set_parallel_detection(params, vars->phy_flags); bnx2x_init_internal_phy(params, vars); } if (!non_ext_phy) rc |= bnx2x_ext_phy_init(params, vars); bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); return rc; } u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 val; DP(NETIF_MSG_LINK, "Phy Initialization started \n"); DP(NETIF_MSG_LINK, "req_speed = %d, req_flowctrl=%d\n", params->req_line_speed, params->req_flow_ctrl); vars->link_status = 0; vars->phy_link_up = 0; vars->link_up = 0; vars->line_speed = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_NONE; if (params->switch_cfg == SWITCH_CFG_1G) vars->phy_flags = PHY_SERDES_FLAG; else vars->phy_flags = PHY_XGXS_FLAG; /* disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); bnx2x_emac_init(params, vars); if (CHIP_REV_IS_FPGA(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); /* enable on E1.5 FPGA */ if (CHIP_IS_E1H(bp)) { vars->flow_ctrl |= (BNX2X_FLOW_CTRL_TX | BNX2X_FLOW_CTRL_RX); vars->link_status |= (LINK_STATUS_TX_FLOW_CONTROL_ENABLED | LINK_STATUS_RX_FLOW_CONTROL_ENABLED); } bnx2x_emac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (CHIP_REV_IS_EMUL(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); bnx2x_bmac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* Disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (params->loopback_mode == LOOPBACK_BMAC) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_BMAC; vars->phy_flags = PHY_XGXS_FLAG; bnx2x_phy_deassert(params, vars->phy_flags); /* set bmac loopback */ bnx2x_bmac_enable(params, vars, 1); REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else if (params->loopback_mode == LOOPBACK_EMAC) { vars->link_up = 1; vars->line_speed = SPEED_1000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_EMAC; vars->phy_flags = PHY_XGXS_FLAG; bnx2x_phy_deassert(params, vars->phy_flags); /* set bmac loopback */ bnx2x_emac_enable(params, vars, 1); bnx2x_emac_program(params, vars->line_speed, vars->duplex); REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else if ((params->loopback_mode == LOOPBACK_XGXS_10) || (params->loopback_mode == LOOPBACK_EXT_PHY)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->phy_flags = PHY_XGXS_FLAG; val = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR+ params->port*0x18); params->phy_addr = (u8)val; bnx2x_phy_deassert(params, vars->phy_flags); bnx2x_link_initialize(params, vars); vars->mac_type = MAC_TYPE_BMAC; bnx2x_bmac_enable(params, vars, 0); if (params->loopback_mode == LOOPBACK_XGXS_10) { /* set 10G XGXS loopback */ bnx2x_set_xgxs_loopback(params, vars, 1); } else { /* set external phy loopback */ bnx2x_ext_phy_loopback(params); } REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else /* No loopback */ { bnx2x_phy_deassert(params, vars->phy_flags); switch (params->switch_cfg) { case SWITCH_CFG_1G: vars->phy_flags |= PHY_SERDES_FLAG; if ((params->ext_phy_config & PORT_HW_CFG_SERDES_EXT_PHY_TYPE_MASK) == PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482) { vars->phy_flags |= PHY_SGMII_FLAG; } val = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR+ params->port*0x10); params->phy_addr = (u8)val; break; case SWITCH_CFG_10G: vars->phy_flags |= PHY_XGXS_FLAG; val = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR+ params->port*0x18); params->phy_addr = (u8)val; break; default: DP(NETIF_MSG_LINK, "Invalid switch_cfg\n"); return -EINVAL; break; } bnx2x_link_initialize(params, vars); msleep(30); bnx2x_link_int_enable(params); } return 0; } u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_config = params->ext_phy_config; u16 hw_led_mode = params->hw_led_mode; u32 chip_id = params->chip_id; u8 port = params->port; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config); /* disable attentions */ vars->link_status = 0; bnx2x_update_mng(params, vars->link_status); bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); /* disable nig egress interface */ REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0); /* Stop BigMac rx */ bnx2x_bmac_rx_disable(bp, port); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); msleep(10); /* The PHY reset is controled by GPIO 1 * Hold it as vars low */ /* clear link led */ bnx2x_set_led(bp, port, LED_MODE_OFF, 0, hw_led_mode, chip_id); if (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) { if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) { /* HW reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); DP(NETIF_MSG_LINK, "reset external PHY\n"); } else if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { DP(NETIF_MSG_LINK, "Setting 8073 port %d into " "low power mode\n", port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); } } /* reset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, (0x1ff << (port*16))); /* reset BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* disable nig ingress interface */ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0); REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0); vars->link_up = 0; return 0; } static u8 bnx2x_update_link_down(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port); bnx2x_set_led(bp, port, LED_MODE_OFF, 0, params->hw_led_mode, params->chip_id); /* indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; /* update shared memory */ vars->link_status = 0; vars->line_speed = 0; bnx2x_update_mng(params, vars->link_status); /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); msleep(10); /* reset BigMac */ bnx2x_bmac_rx_disable(bp, params->port); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); return 0; } static u8 bnx2x_update_link_up(struct link_params *params, struct link_vars *vars, u8 link_10g, u32 gp_status) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 rc = 0; vars->link_status |= LINK_STATUS_LINK_UP; if (link_10g) { bnx2x_bmac_enable(params, vars, 0); bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_10000, params->hw_led_mode, params->chip_id); } else { bnx2x_emac_enable(params, vars, 0); rc = bnx2x_emac_program(params, vars->line_speed, vars->duplex); /* AN complete? */ if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { if (!(vars->phy_flags & PHY_SGMII_FLAG)) bnx2x_set_sgmii_tx_driver(params); } } /* PBF - link up */ rc |= bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); msleep(20); return rc; } /* This function should called upon link interrupt */ /* In case vars->link_up, driver needs to 1. Update the pbf 2. Disable drain 3. Update the shared memory 4. Indicate link up 5. Set LEDs Otherwise, 1. Update shared memory 2. Reset BigMac 3. Report link down 4. Unset LEDs */ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 gp_status; u8 link_10g; u8 ext_phy_link_up, rc = 0; u32 ext_phy_type; DP(NETIF_MSG_LINK, "port %x, XGXS?%x, int_status 0x%x\n", port, (vars->phy_flags & PHY_XGXS_FLAG), REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4)); DP(NETIF_MSG_LINK, "int_mask 0x%x MI_INT %x, SERDES_LINK %x\n", REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), REG_RD(bp, NIG_REG_SERDES0_STATUS_LINK_STATUS + port*0x3c)); DP(NETIF_MSG_LINK, " 10G %x, XGXS_LINK %x\n", REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Check external link change only for non-direct */ ext_phy_link_up = bnx2x_ext_phy_is_link_up(params, vars); /* Read gp_status */ CL45_RD_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); rc = bnx2x_link_settings_status(params, vars, gp_status); if (rc != 0) return rc; /* anything 10 and over uses the bmac */ link_10g = ((vars->line_speed == SPEED_10000) || (vars->line_speed == SPEED_12000) || (vars->line_speed == SPEED_12500) || (vars->line_speed == SPEED_13000) || (vars->line_speed == SPEED_15000) || (vars->line_speed == SPEED_16000)); bnx2x_link_int_ack(params, vars, link_10g); /* In case external phy link is up, and internal link is down ( not initialized yet probably after link initialization, it needs to be initialized. Note that after link down-up as result of cable plug, the xgxs link would probably become up again without the need to initialize it*/ if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) && (ext_phy_link_up && !vars->phy_link_up)) bnx2x_init_internal_phy(params, vars); /* link is up only if both local phy and external phy are up */ vars->link_up = (ext_phy_link_up && vars->phy_link_up); if (vars->link_up) rc = bnx2x_update_link_up(params, vars, link_10g, gp_status); else rc = bnx2x_update_link_down(params, vars); return rc; } static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 ext_phy_addr[PORT_MAX]; u16 val; s8 port; /* PART1 - Reset both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { /* Extract the ext phy address for the port */ u32 ext_phy_config = REG_RD(bp, shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[port].external_phy_config)); /* disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); ext_phy_addr[port] = ((ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); /* Need to take the phy out of low power mode in order to write to access its registers */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); /* Reset the phy */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); } /* Add delay of 150ms after reset */ msleep(150); /* PART2 - Download firmware to both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { u16 fw_ver1; bnx2x_bcm8073_external_rom_boot(bp, port, ext_phy_addr[port]); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); if (fw_ver1 == 0 || fw_ver1 == 0x4321) { DP(NETIF_MSG_LINK, "bnx2x_8073_common_init_phy port %x:" "Download failed. fw version = 0x%x\n", port, fw_ver1); return -EINVAL; } /* Only set bit 10 = 1 (Tx power down) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, &val); /* Phase1 of TX_POWER_DOWN reset */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, (val | 1<<10)); } /* Toggle Transmitter: Power down and then up with 600ms delay between */ msleep(600); /* PART3 - complete TX_POWER_DOWN process, and set GPIO2 back to low */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { /* Phase2 of POWER_DOWN_RESET*/ /* Release bit 10 (Release Tx power down) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, &val); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, (val & (~(1<<10)))); msleep(15); /* Read modify write the SPI-ROM version select register */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, &val); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, (val | (1<<12))); /* set GPIO2 back to LOW */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); } return 0; } u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 rc = 0; u32 ext_phy_type; DP(NETIF_MSG_LINK, "bnx2x_common_init_phy\n"); /* Read the ext_phy_type for arbitrary port(0) */ ext_phy_type = XGXS_EXT_PHY_TYPE( REG_RD(bp, shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[0].external_phy_config))); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { rc = bnx2x_8073_common_init_phy(bp, shmem_base); break; } default: DP(NETIF_MSG_LINK, "bnx2x_common_init_phy: ext_phy 0x%x not required\n", ext_phy_type); break; } return rc; } static void bnx2x_sfx7101_sp_sw_reset(struct bnx2x *bp, u8 port, u8 phy_addr) { u16 val, cnt; bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, &val); for (cnt = 0; cnt < 10; cnt++) { msleep(50); /* Writes a self-clearing reset */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, (val | (1<<15))); /* Wait for clear */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, &val); if ((val & (1<<15)) == 0) break; } } #define RESERVED_SIZE 256 /* max application is 160K bytes - data at end of RAM */ #define MAX_APP_SIZE (160*1024 - RESERVED_SIZE) /* Header is 14 bytes */ #define HEADER_SIZE 14 #define DATA_OFFSET HEADER_SIZE #define SPI_START_TRANSFER(bp, port, ext_phy_addr) \ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, \ ext_phy_addr, \ MDIO_PCS_DEVAD, \ MDIO_PCS_REG_7101_SPI_CTRL_ADDR, 1) /* Programs an image to DSP's flash via the SPI port*/ static u8 bnx2x_sfx7101_flash_download(struct bnx2x *bp, u8 port, u8 ext_phy_addr, char data[], u32 size) { const u16 num_trans = size/4; /* 4 bytes can be sent at a time */ /* Doesn't include last trans!*/ const u16 last_trans_size = size%4; /* Num bytes on last trans */ u16 trans_cnt, byte_cnt; u32 data_index; u16 tmp; u16 code_started = 0; u16 image_revision1, image_revision2; u16 cnt; DP(NETIF_MSG_LINK, "bnx2x_sfx7101_flash_download file_size=%d\n", size); /* Going to flash*/ if ((size-HEADER_SIZE) > MAX_APP_SIZE) { /* This very often will be the case, because the image is built with 160Kbytes size whereas the total image size must actually be 160Kbytes-RESERVED_SIZE */ DP(NETIF_MSG_LINK, "Warning, file size was %d bytes " "truncated to %d bytes\n", size, MAX_APP_SIZE); size = MAX_APP_SIZE+HEADER_SIZE; } DP(NETIF_MSG_LINK, "File version is %c%c\n", data[0x14e], data[0x14f]); DP(NETIF_MSG_LINK, " %c%c\n", data[0x150], data[0x151]); /* Put the DSP in download mode by setting FLASH_CFG[2] to 1 and issuing a reset.*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, port); bnx2x_sfx7101_sp_sw_reset(bp, port, ext_phy_addr); /* wait 0.5 sec */ for (cnt = 0; cnt < 100; cnt++) msleep(5); /* Make sure we can access the DSP And it's in the correct mode (waiting for download) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_DSP_ACCESS, &tmp); if (tmp != 0x000A) { DP(NETIF_MSG_LINK, "DSP is not in waiting on download mode. " "Expected 0x000A, read 0x%04X\n", tmp); DP(NETIF_MSG_LINK, "Download failed\n"); return -EINVAL; } /* Mux the SPI interface away from the internal processor */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_MUX, 1); /* Reset the SPI port */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_CTRL_ADDR, 0); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_CTRL_ADDR, (1<<MDIO_PCS_REG_7101_SPI_RESET_BIT)); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_CTRL_ADDR, 0); /* Erase the flash */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_WRITE_ENABLE_CMD); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, 1); SPI_START_TRANSFER(bp, port, ext_phy_addr); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_BULK_ERASE_CMD); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, 1); SPI_START_TRANSFER(bp, port, ext_phy_addr); /* Wait 10 seconds, the maximum time for the erase to complete */ DP(NETIF_MSG_LINK, "Erasing flash, this takes 10 seconds...\n"); for (cnt = 0; cnt < 1000; cnt++) msleep(10); DP(NETIF_MSG_LINK, "Downloading flash, please wait...\n"); data_index = 0; for (trans_cnt = 0; trans_cnt < num_trans; trans_cnt++) { bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_WRITE_ENABLE_CMD); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, 1); SPI_START_TRANSFER(bp, port, ext_phy_addr); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_PAGE_PROGRAM_CMD); /* Bits 23-16 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>16)); /* Bits 15-8 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>8)); /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, ((u16)data_index)); byte_cnt = 0; while (byte_cnt < 4 && data_index < size) { bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, data[data_index++]); byte_cnt++; } bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, byte_cnt+4); SPI_START_TRANSFER(bp, port, ext_phy_addr); msleep(5); /* Wait 5 ms minimum between transs */ /* Let the user know something's going on.*/ /* a pacifier ever 4K */ if ((data_index % 1023) == 0) DP(NETIF_MSG_LINK, "Download %d%%\n", data_index/size); } DP(NETIF_MSG_LINK, "\n"); /* Transfer the last block if there is data remaining */ if (last_trans_size) { bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_WRITE_ENABLE_CMD); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, 1); SPI_START_TRANSFER(bp, port, ext_phy_addr); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_PAGE_PROGRAM_CMD); /* Bits 23-16 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>16)); /* Bits 15-8 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>8)); /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, ((u16)data_index)); byte_cnt = 0; while (byte_cnt < last_trans_size && data_index < size) { /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, data[data_index++]); byte_cnt++; } bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, byte_cnt+4); SPI_START_TRANSFER(bp, port, ext_phy_addr); } /* DSP Remove Download Mode */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_LOW, port); bnx2x_sfx7101_sp_sw_reset(bp, port, ext_phy_addr); /* wait 0.5 sec to allow it to run */ for (cnt = 0; cnt < 100; cnt++) msleep(5); bnx2x_hw_reset(bp, port); for (cnt = 0; cnt < 100; cnt++) msleep(5); /* Check that the code is started. In case the download checksum failed, the code won't be started. */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_DSP_ACCESS, &tmp); code_started = (tmp & (1<<4)); if (!code_started) { DP(NETIF_MSG_LINK, "Download failed. Please check file.\n"); return -EINVAL; } /* Verify that the file revision is now equal to the image revision within the DSP */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER1, &image_revision1); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER2, &image_revision2); if (data[0x14e] != (image_revision2&0xFF) || data[0x14f] != ((image_revision2&0xFF00)>>8) || data[0x150] != (image_revision1&0xFF) || data[0x151] != ((image_revision1&0xFF00)>>8)) { DP(NETIF_MSG_LINK, "Download failed.\n"); return -EINVAL; } DP(NETIF_MSG_LINK, "Download %d%%\n", data_index/size); return 0; } u8 bnx2x_flash_download(struct bnx2x *bp, u8 port, u32 ext_phy_config, u8 driver_loaded, char data[], u32 size) { u8 rc = 0; u32 ext_phy_type; u8 ext_phy_addr; ext_phy_addr = ((ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "Flash download not supported for this ext phy\n"); rc = -EINVAL; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* Take ext phy out of reset */ if (!driver_loaded) bnx2x_turn_on_ef(bp, port, ext_phy_addr, ext_phy_type); rc = bnx2x_sfx7101_flash_download(bp, port, ext_phy_addr, data, size); if (!driver_loaded) bnx2x_turn_off_sf(bp, port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: default: DP(NETIF_MSG_LINK, "Invalid ext phy type\n"); rc = -EINVAL; break; } return rc; }