Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sungem_phy: support bcm5461 phy, autoneg.

This version moves the medium variable to the card specific structure and
changes the GMII_* to BCM54XX_* #defines.

This patch adds improved version of enable_fiber for both the 5421 and
the 5461 phy. It is now possible to specify with these wether you want
autonegotiation or not. This is needed for bladecenter switches where
some expect autonegotiation and some dont seem to like this at all.
Depending on this flag it sets phy->autoneg accordingly for the fiber mode.

More importantly it implements proper read_link and poll_link functions
for both phys which can handle both copper and fiber mode by determining
the medium first and then branching to the required functions. For fiber
they all work fine, for copper they are not tested but return the result
of the genmii_* function anyway which is supposed to work.

The patch moves the genmii_* functions around to avoid foreward declarations.

Signed-off-by: Jens Osterkamp <jens@de.ibm.com>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Linas Vepstas <linas@austin.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

authored by

Jens Osterkamp and committed by
Jeff Garzik
eb5b5b2f 6fedae1f

+263 -136
+254 -135
drivers/net/sungem_phy.c
··· 310 310 return 0; 311 311 } 312 312 313 + static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) 314 + { 315 + u16 ctl, adv; 316 + 317 + phy->autoneg = 1; 318 + phy->speed = SPEED_10; 319 + phy->duplex = DUPLEX_HALF; 320 + phy->pause = 0; 321 + phy->advertising = advertise; 322 + 323 + /* Setup standard advertise */ 324 + adv = phy_read(phy, MII_ADVERTISE); 325 + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 326 + if (advertise & ADVERTISED_10baseT_Half) 327 + adv |= ADVERTISE_10HALF; 328 + if (advertise & ADVERTISED_10baseT_Full) 329 + adv |= ADVERTISE_10FULL; 330 + if (advertise & ADVERTISED_100baseT_Half) 331 + adv |= ADVERTISE_100HALF; 332 + if (advertise & ADVERTISED_100baseT_Full) 333 + adv |= ADVERTISE_100FULL; 334 + phy_write(phy, MII_ADVERTISE, adv); 335 + 336 + /* Start/Restart aneg */ 337 + ctl = phy_read(phy, MII_BMCR); 338 + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 339 + phy_write(phy, MII_BMCR, ctl); 340 + 341 + return 0; 342 + } 343 + 344 + static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) 345 + { 346 + u16 ctl; 347 + 348 + phy->autoneg = 0; 349 + phy->speed = speed; 350 + phy->duplex = fd; 351 + phy->pause = 0; 352 + 353 + ctl = phy_read(phy, MII_BMCR); 354 + ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); 355 + 356 + /* First reset the PHY */ 357 + phy_write(phy, MII_BMCR, ctl | BMCR_RESET); 358 + 359 + /* Select speed & duplex */ 360 + switch(speed) { 361 + case SPEED_10: 362 + break; 363 + case SPEED_100: 364 + ctl |= BMCR_SPEED100; 365 + break; 366 + case SPEED_1000: 367 + default: 368 + return -EINVAL; 369 + } 370 + if (fd == DUPLEX_FULL) 371 + ctl |= BMCR_FULLDPLX; 372 + phy_write(phy, MII_BMCR, ctl); 373 + 374 + return 0; 375 + } 376 + 377 + static int genmii_poll_link(struct mii_phy *phy) 378 + { 379 + u16 status; 380 + 381 + (void)phy_read(phy, MII_BMSR); 382 + status = phy_read(phy, MII_BMSR); 383 + if ((status & BMSR_LSTATUS) == 0) 384 + return 0; 385 + if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) 386 + return 0; 387 + return 1; 388 + } 389 + 390 + static int genmii_read_link(struct mii_phy *phy) 391 + { 392 + u16 lpa; 393 + 394 + if (phy->autoneg) { 395 + lpa = phy_read(phy, MII_LPA); 396 + 397 + if (lpa & (LPA_10FULL | LPA_100FULL)) 398 + phy->duplex = DUPLEX_FULL; 399 + else 400 + phy->duplex = DUPLEX_HALF; 401 + if (lpa & (LPA_100FULL | LPA_100HALF)) 402 + phy->speed = SPEED_100; 403 + else 404 + phy->speed = SPEED_10; 405 + phy->pause = 0; 406 + } 407 + /* On non-aneg, we assume what we put in BMCR is the speed, 408 + * though magic-aneg shouldn't prevent this case from occurring 409 + */ 410 + 411 + return 0; 412 + } 413 + 313 414 static int generic_suspend(struct mii_phy* phy) 314 415 { 315 416 phy_write(phy, MII_BMCR, BMCR_PDOWN); ··· 461 360 } 462 361 } 463 362 #endif /* CONFIG_PPC_PMAC */ 464 - 465 - return 0; 466 - } 467 - 468 - static int bcm5421_enable_fiber(struct mii_phy* phy) 469 - { 470 - /* enable fiber mode */ 471 - phy_write(phy, MII_NCONFIG, 0x9020); 472 - /* LEDs active in both modes, autosense prio = fiber */ 473 - phy_write(phy, MII_NCONFIG, 0x945f); 474 - 475 - /* switch off fibre autoneg */ 476 - phy_write(phy, MII_NCONFIG, 0xfc01); 477 - phy_write(phy, 0x0b, 0x0004); 478 - 479 - return 0; 480 - } 481 - 482 - static int bcm5461_enable_fiber(struct mii_phy* phy) 483 - { 484 - phy_write(phy, MII_NCONFIG, 0xfc0c); 485 - phy_write(phy, MII_BMCR, 0x4140); 486 - phy_write(phy, MII_NCONFIG, 0xfc0b); 487 - phy_write(phy, MII_BMCR, 0x0140); 488 363 489 364 return 0; 490 365 } ··· 589 512 phy_write(phy, 0x1d, 0x0004); 590 513 phy_write(phy, 0x1e, 0x4850); 591 514 } 515 + return 0; 516 + } 517 + 518 + #define BCM5421_MODE_MASK (1 << 5) 519 + 520 + static int bcm5421_poll_link(struct mii_phy* phy) 521 + { 522 + u32 phy_reg; 523 + int mode; 524 + 525 + /* find out in what mode we are */ 526 + phy_write(phy, MII_NCONFIG, 0x1000); 527 + phy_reg = phy_read(phy, MII_NCONFIG); 528 + 529 + mode = (phy_reg & BCM5421_MODE_MASK) >> 5; 530 + 531 + if ( mode == BCM54XX_COPPER) 532 + return genmii_poll_link(phy); 533 + 534 + /* try to find out wether we have a link */ 535 + phy_write(phy, MII_NCONFIG, 0x2000); 536 + phy_reg = phy_read(phy, MII_NCONFIG); 537 + 538 + if (phy_reg & 0x0020) 539 + return 0; 540 + else 541 + return 1; 542 + } 543 + 544 + static int bcm5421_read_link(struct mii_phy* phy) 545 + { 546 + u32 phy_reg; 547 + int mode; 548 + 549 + /* find out in what mode we are */ 550 + phy_write(phy, MII_NCONFIG, 0x1000); 551 + phy_reg = phy_read(phy, MII_NCONFIG); 552 + 553 + mode = (phy_reg & BCM5421_MODE_MASK ) >> 5; 554 + 555 + if ( mode == BCM54XX_COPPER) 556 + return bcm54xx_read_link(phy); 557 + 558 + phy->speed = SPEED_1000; 559 + 560 + /* find out wether we are running half- or full duplex */ 561 + phy_write(phy, MII_NCONFIG, 0x2000); 562 + phy_reg = phy_read(phy, MII_NCONFIG); 563 + 564 + if ( (phy_reg & 0x0080) >> 7) 565 + phy->duplex |= DUPLEX_HALF; 566 + else 567 + phy->duplex |= DUPLEX_FULL; 568 + 569 + return 0; 570 + } 571 + 572 + static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg) 573 + { 574 + /* enable fiber mode */ 575 + phy_write(phy, MII_NCONFIG, 0x9020); 576 + /* LEDs active in both modes, autosense prio = fiber */ 577 + phy_write(phy, MII_NCONFIG, 0x945f); 578 + 579 + if (!autoneg) { 580 + /* switch off fibre autoneg */ 581 + phy_write(phy, MII_NCONFIG, 0xfc01); 582 + phy_write(phy, 0x0b, 0x0004); 583 + } 584 + 585 + phy->autoneg = autoneg; 586 + 587 + return 0; 588 + } 589 + 590 + #define BCM5461_FIBER_LINK (1 << 2) 591 + #define BCM5461_MODE_MASK (3 << 1) 592 + 593 + static int bcm5461_poll_link(struct mii_phy* phy) 594 + { 595 + u32 phy_reg; 596 + int mode; 597 + 598 + /* find out in what mode we are */ 599 + phy_write(phy, MII_NCONFIG, 0x7c00); 600 + phy_reg = phy_read(phy, MII_NCONFIG); 601 + 602 + mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; 603 + 604 + if ( mode == BCM54XX_COPPER) 605 + return genmii_poll_link(phy); 606 + 607 + /* find out wether we have a link */ 608 + phy_write(phy, MII_NCONFIG, 0x7000); 609 + phy_reg = phy_read(phy, MII_NCONFIG); 610 + 611 + if (phy_reg & BCM5461_FIBER_LINK) 612 + return 1; 613 + else 614 + return 0; 615 + } 616 + 617 + #define BCM5461_FIBER_DUPLEX (1 << 3) 618 + 619 + static int bcm5461_read_link(struct mii_phy* phy) 620 + { 621 + u32 phy_reg; 622 + int mode; 623 + 624 + /* find out in what mode we are */ 625 + phy_write(phy, MII_NCONFIG, 0x7c00); 626 + phy_reg = phy_read(phy, MII_NCONFIG); 627 + 628 + mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; 629 + 630 + if ( mode == BCM54XX_COPPER) { 631 + return bcm54xx_read_link(phy); 632 + } 633 + 634 + phy->speed = SPEED_1000; 635 + 636 + /* find out wether we are running half- or full duplex */ 637 + phy_write(phy, MII_NCONFIG, 0x7000); 638 + phy_reg = phy_read(phy, MII_NCONFIG); 639 + 640 + if (phy_reg & BCM5461_FIBER_DUPLEX) 641 + phy->duplex |= DUPLEX_FULL; 642 + else 643 + phy->duplex |= DUPLEX_HALF; 644 + 645 + return 0; 646 + } 647 + 648 + static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg) 649 + { 650 + /* select fiber mode, enable 1000 base-X registers */ 651 + phy_write(phy, MII_NCONFIG, 0xfc0b); 652 + 653 + if (autoneg) { 654 + /* enable fiber with no autonegotiation */ 655 + phy_write(phy, MII_ADVERTISE, 0x01e0); 656 + phy_write(phy, MII_BMCR, 0x1140); 657 + } else { 658 + /* enable fiber with autonegotiation */ 659 + phy_write(phy, MII_BMCR, 0x0140); 660 + } 661 + 662 + phy->autoneg = autoneg; 663 + 592 664 return 0; 593 665 } 594 666 ··· 870 644 871 645 return 0; 872 646 } 873 - 874 - static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) 875 - { 876 - u16 ctl, adv; 877 - 878 - phy->autoneg = 1; 879 - phy->speed = SPEED_10; 880 - phy->duplex = DUPLEX_HALF; 881 - phy->pause = 0; 882 - phy->advertising = advertise; 883 - 884 - /* Setup standard advertise */ 885 - adv = phy_read(phy, MII_ADVERTISE); 886 - adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); 887 - if (advertise & ADVERTISED_10baseT_Half) 888 - adv |= ADVERTISE_10HALF; 889 - if (advertise & ADVERTISED_10baseT_Full) 890 - adv |= ADVERTISE_10FULL; 891 - if (advertise & ADVERTISED_100baseT_Half) 892 - adv |= ADVERTISE_100HALF; 893 - if (advertise & ADVERTISED_100baseT_Full) 894 - adv |= ADVERTISE_100FULL; 895 - if (advertise & ADVERTISED_Pause) 896 - adv |= ADVERTISE_PAUSE_CAP; 897 - if (advertise & ADVERTISED_Asym_Pause) 898 - adv |= ADVERTISE_PAUSE_ASYM; 899 - phy_write(phy, MII_ADVERTISE, adv); 900 - 901 - /* Start/Restart aneg */ 902 - ctl = phy_read(phy, MII_BMCR); 903 - ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); 904 - phy_write(phy, MII_BMCR, ctl); 905 - 906 - return 0; 907 - } 908 - 909 - static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) 910 - { 911 - u16 ctl; 912 - 913 - phy->autoneg = 0; 914 - phy->speed = speed; 915 - phy->duplex = fd; 916 - phy->pause = 0; 917 - 918 - ctl = phy_read(phy, MII_BMCR); 919 - ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); 920 - 921 - /* First reset the PHY */ 922 - phy_write(phy, MII_BMCR, ctl | BMCR_RESET); 923 - 924 - /* Select speed & duplex */ 925 - switch(speed) { 926 - case SPEED_10: 927 - break; 928 - case SPEED_100: 929 - ctl |= BMCR_SPEED100; 930 - break; 931 - case SPEED_1000: 932 - default: 933 - return -EINVAL; 934 - } 935 - if (fd == DUPLEX_FULL) 936 - ctl |= BMCR_FULLDPLX; 937 - phy_write(phy, MII_BMCR, ctl); 938 - 939 - return 0; 940 - } 941 - 942 - static int genmii_poll_link(struct mii_phy *phy) 943 - { 944 - u16 status; 945 - 946 - (void)phy_read(phy, MII_BMSR); 947 - status = phy_read(phy, MII_BMSR); 948 - if ((status & BMSR_LSTATUS) == 0) 949 - return 0; 950 - if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) 951 - return 0; 952 - return 1; 953 - } 954 - 955 - static int genmii_read_link(struct mii_phy *phy) 956 - { 957 - u16 lpa; 958 - 959 - if (phy->autoneg) { 960 - lpa = phy_read(phy, MII_LPA); 961 - 962 - if (lpa & (LPA_10FULL | LPA_100FULL)) 963 - phy->duplex = DUPLEX_FULL; 964 - else 965 - phy->duplex = DUPLEX_HALF; 966 - if (lpa & (LPA_100FULL | LPA_100HALF)) 967 - phy->speed = SPEED_100; 968 - else 969 - phy->speed = SPEED_10; 970 - phy->pause = (phy->duplex == DUPLEX_FULL) && 971 - ((lpa & LPA_PAUSE) != 0); 972 - } 973 - /* On non-aneg, we assume what we put in BMCR is the speed, 974 - * though magic-aneg shouldn't prevent this case from occurring 975 - */ 976 - 977 - return 0; 978 - } 979 - 980 647 981 648 #define MII_BASIC_FEATURES \ 982 649 (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ ··· 1004 885 .suspend = generic_suspend, 1005 886 .setup_aneg = bcm54xx_setup_aneg, 1006 887 .setup_forced = bcm54xx_setup_forced, 1007 - .poll_link = genmii_poll_link, 1008 - .read_link = bcm54xx_read_link, 888 + .poll_link = bcm5421_poll_link, 889 + .read_link = bcm5421_read_link, 1009 890 .enable_fiber = bcm5421_enable_fiber, 1010 891 }; 1011 892 ··· 1042 923 .suspend = generic_suspend, 1043 924 .setup_aneg = bcm54xx_setup_aneg, 1044 925 .setup_forced = bcm54xx_setup_forced, 1045 - .poll_link = genmii_poll_link, 1046 - .read_link = bcm54xx_read_link, 926 + .poll_link = bcm5461_poll_link, 927 + .read_link = bcm5461_read_link, 1047 928 .enable_fiber = bcm5461_enable_fiber, 1048 929 }; 1049 930
+9 -1
drivers/net/sungem_phy.h
··· 12 12 int (*setup_forced)(struct mii_phy *phy, int speed, int fd); 13 13 int (*poll_link)(struct mii_phy *phy); 14 14 int (*read_link)(struct mii_phy *phy); 15 - int (*enable_fiber)(struct mii_phy *phy); 15 + int (*enable_fiber)(struct mii_phy *phy, int autoneg); 16 16 }; 17 17 18 18 /* Structure used to statically define an mii/gii based PHY */ ··· 24 24 int magic_aneg; /* Autoneg does all speed test for us */ 25 25 const char* name; 26 26 const struct mii_phy_ops* ops; 27 + }; 28 + 29 + enum { 30 + BCM54XX_COPPER, 31 + BCM54XX_FIBER, 32 + BCM54XX_GBIC, 33 + BCM54XX_SGMII, 34 + BCM54XX_UNKNOWN, 27 35 }; 28 36 29 37 /* An instance of a PHY, partially borrowed from mii_if_info */