Subject: spidernet: dynamic phy setup code This patch modifies the patch submitted by Kou Ishizaki to make it work on the blade (http://marc.theaimsgroup.com/?l=linux-netdev&m=116593424505539&w=2). Unfortunately I dont have access to a Celleb so I cannot test it there. The basic logic behind this is simple : when the interface first comes up it tries to detect the phy. When the phy is detected, it is initially set up to use copper. A timer is set to check the link status in spidernet_link_phy using poll_link. If link check fails more than SPIDER_NET_ANEG_TIMEOUT times, it switches to fiber link with autonegotiation. If that fails more than SPIDER_NET_ANEG_TIMEOUT times, it switches to fiber link with no autonegotiation. If that also fails, it goes back to copper, and so forth. If the link comes up, it prints the link abilites and we are done. The name of the phy found attached to the spider chip is reported to the user. Hardcoded values for the timeout are moved to #defines. I put in a few comments to make the code more readable. Signed-off-by: Jens Osterkamp Signed-off-by: Arnd Bergmann Index: linux-2.6/drivers/net/spider_net.c =================================================================== --- linux-2.6.orig/drivers/net/spider_net.c +++ linux-2.6/drivers/net/spider_net.c @@ -1280,19 +1280,23 @@ spider_net_set_mac(struct net_device *ne * spider_net_link_reset * @netdev: net device structure * + * This is called when the PHY_LINK signal is asserted. For the blade this is + * not connected so we should never get here. + * */ static void spider_net_link_reset(struct net_device *netdev) { - struct spider_net_card *card=netdev_priv(netdev); del_timer_sync(&card->aneg_timer); + /* clear interrupt, block further interrupts */ spider_net_write_reg(card, SPIDER_NET_GMACST, spider_net_read_reg(card, SPIDER_NET_GMACST)); spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); + /* reset phy and setup aneg */ mii_phy_probe(&card->phy, card->phy.mii_id); spider_net_setup_aneg(card, is1000); if (card->phy.def->phy_id) @@ -1616,6 +1620,7 @@ spider_net_init_card(struct spider_net_c spider_net_write_reg(card, SPIDER_NET_CKRCTRL, SPIDER_NET_CKRCTRL_RUN_VALUE); + /* trigger ETOMOD signal */ spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, spider_net_read_reg(card, SPIDER_NET_GMACOPEMD) | 0x4); @@ -1739,6 +1744,8 @@ spider_net_open(struct net_device *netde if (spider_net_init_firmware(card)) goto init_firmware_failed; + /* start probing with copper */ + card->phy.medium = GMII_COPPER; spider_net_setup_aneg(card, is1000); if (card->phy.def->phy_id) mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); @@ -1805,43 +1812,54 @@ static void spider_net_link_phy(unsigned struct spider_net_card *card = (struct spider_net_card *)data; struct mii_phy *phy = &card->phy; - if (card->aneg_count > 10) { - /* timeout */ - card->aneg_count = 0; - is1000 = !is1000; - goto re_setup; + /* if link didn't come up after SPIDER_NET_ANEG_TIMEOUT tries, setup phy again */ + if (card->aneg_count > SPIDER_NET_ANEG_TIMEOUT) { + + pr_info("%s: link is down, trying to bring it up\n", card->netdev->name); + + switch(phy->medium) { + case GMII_COPPER: + /* enable fiber with autonegotiation first */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 1); + phy->medium = GMII_FIBER; + break; + + case GMII_FIBER: + /* fiber didn't come up, try to disable fiber autoneg */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 0); + phy->medium = GMII_UNKNOWN; + break; + + case GMII_UNKNOWN: + /* copper, fiber with and without autoneg failed, + * retry from beginning */ + spider_net_setup_aneg(card, is1000); + phy->medium = GMII_COPPER; + break; + } + + card->aneg_count = 0; + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + return; } + /* link still not up, try again later */ if (!(phy->def->ops->poll_link(phy))) { card->aneg_count++; mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); return; } + /* link came up, get abilities */ phy->def->ops->read_link(phy); - if (phy->speed == 1000 && !is1000) { - is1000 = 1; - goto re_setup; - } else if(phy->speed != 1000 && is1000) { - is1000 = 0; - goto re_setup; - } - - spider_net_write_reg(card, SPIDER_NET_GMACST, - spider_net_read_reg(card, SPIDER_NET_GMACST)); - spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0x4); - - pr_info("Found %s with %i Mbps, %s-duplex.\n", - phy->def->name, phy->speed, phy->duplex==1 ? "Full" : "Half"); + pr_info("%s: link is up with %i Mbps, %s-duplex, %sautoneg.\n", + card->netdev->name, phy->speed, phy->duplex==1 ? "Full" : "Half", + phy->autoneg==1 ? "" : "no "); return; - -re_setup: - mii_phy_probe(phy, phy->mii_id); - spider_net_setup_aneg(card, is1000); - card->aneg_count = 0; - mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); } /** @@ -1871,8 +1889,10 @@ spider_net_setup_phy(struct spider_net_c unsigned short id; id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); if (id != 0x0000 && id != 0xffff) { - mii_phy_probe(phy, phy->mii_id); + if (!mii_phy_probe(phy, phy->mii_id)) { + pr_info("Found %s.\n", phy->def->name); break; + } } } Index: linux-2.6/drivers/net/spider_net.h =================================================================== --- linux-2.6.orig/drivers/net/spider_net.h +++ linux-2.6/drivers/net/spider_net.h @@ -51,7 +51,8 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_TX_DESCRIPTORS_MAX 512 #define SPIDER_NET_TX_TIMER (HZ/5) -#define SPIDER_NET_ANEG_TIMER (HZ*2) +#define SPIDER_NET_ANEG_TIMER (HZ) +#define SPIDER_NET_ANEG_TIMEOUT 2 #define SPIDER_NET_RX_CSUM_DEFAULT 1