Commit abdb66b5 authored by Kou Ishizaki's avatar Kou Ishizaki Committed by Jeff Garzik

spidernet: autoneg support for Celleb

Add auto negotiation support for Celleb.
Signed-off-by: default avatarKou Ishizaki <kou.ishizaki@toshiba.co.jp>
Signed-off-by: default avatarLinas Vepstas <linas@austin.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent d406eafe
...@@ -165,6 +165,41 @@ spider_net_read_phy(struct net_device *netdev, int mii_id, int reg) ...@@ -165,6 +165,41 @@ spider_net_read_phy(struct net_device *netdev, int mii_id, int reg)
return readvalue; return readvalue;
} }
/**
* spider_net_setup_aneg - initial auto-negotiation setup
* @card: device structure
**/
static void
spider_net_setup_aneg(struct spider_net_card *card)
{
struct mii_phy *phy = &card->phy;
u32 advertise = 0;
u16 bmcr, bmsr, stat1000, estat;
bmcr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMCR);
bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR);
stat1000 = spider_net_read_phy(card->netdev, phy->mii_id, MII_STAT1000);
estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS);
if (bmsr & BMSR_10HALF)
advertise |= ADVERTISED_10baseT_Half;
if (bmsr & BMSR_10FULL)
advertise |= ADVERTISED_10baseT_Full;
if (bmsr & BMSR_100HALF)
advertise |= ADVERTISED_100baseT_Half;
if (bmsr & BMSR_100FULL)
advertise |= ADVERTISED_100baseT_Full;
if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL))
advertise |= SUPPORTED_1000baseT_Full;
if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF))
advertise |= SUPPORTED_1000baseT_Half;
mii_phy_probe(phy, phy->mii_id);
phy->def->ops->setup_aneg(phy, advertise);
}
/** /**
* spider_net_rx_irq_off - switch off rx irq on this spider card * spider_net_rx_irq_off - switch off rx irq on this spider card
* @card: device structure * @card: device structure
...@@ -1247,6 +1282,33 @@ spider_net_set_mac(struct net_device *netdev, void *p) ...@@ -1247,6 +1282,33 @@ spider_net_set_mac(struct net_device *netdev, void *p)
return 0; return 0;
} }
/**
* 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 */
spider_net_setup_aneg(card);
mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER);
}
/** /**
* spider_net_handle_error_irq - handles errors raised by an interrupt * spider_net_handle_error_irq - handles errors raised by an interrupt
* @card: card structure * @card: card structure
...@@ -1500,6 +1562,9 @@ spider_net_interrupt(int irq, void *ptr) ...@@ -1500,6 +1562,9 @@ spider_net_interrupt(int irq, void *ptr)
if (status_reg & SPIDER_NET_TXINT) if (status_reg & SPIDER_NET_TXINT)
netif_rx_schedule(netdev); netif_rx_schedule(netdev);
if (status_reg & SPIDER_NET_LINKINT)
spider_net_link_reset(netdev);
if (status_reg & SPIDER_NET_ERRINT ) if (status_reg & SPIDER_NET_ERRINT )
spider_net_handle_error_irq(card, status_reg); spider_net_handle_error_irq(card, status_reg);
...@@ -1624,8 +1689,6 @@ spider_net_enable_card(struct spider_net_card *card) ...@@ -1624,8 +1689,6 @@ spider_net_enable_card(struct spider_net_card *card)
spider_net_write_reg(card, SPIDER_NET_GMACLENLMT, spider_net_write_reg(card, SPIDER_NET_GMACLENLMT,
SPIDER_NET_LENLMT_VALUE); SPIDER_NET_LENLMT_VALUE);
spider_net_write_reg(card, SPIDER_NET_GMACMODE,
SPIDER_NET_MACMODE_VALUE);
spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, spider_net_write_reg(card, SPIDER_NET_GMACOPEMD,
SPIDER_NET_OPMODE_VALUE); SPIDER_NET_OPMODE_VALUE);
...@@ -1656,6 +1719,11 @@ spider_net_open(struct net_device *netdev) ...@@ -1656,6 +1719,11 @@ spider_net_open(struct net_device *netdev)
struct spider_net_card *card = netdev_priv(netdev); struct spider_net_card *card = netdev_priv(netdev);
int result; int result;
/* start probing with copper */
spider_net_setup_aneg(card);
if (card->phy.def->phy_id)
mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER);
result = spider_net_init_chain(card, &card->tx_chain); result = spider_net_init_chain(card, &card->tx_chain);
if (result) if (result)
goto alloc_tx_failed; goto alloc_tx_failed;
...@@ -1693,17 +1761,88 @@ spider_net_open(struct net_device *netdev) ...@@ -1693,17 +1761,88 @@ spider_net_open(struct net_device *netdev)
alloc_rx_failed: alloc_rx_failed:
spider_net_free_chain(card, &card->tx_chain); spider_net_free_chain(card, &card->tx_chain);
alloc_tx_failed: alloc_tx_failed:
del_timer_sync(&card->aneg_timer);
return result; return result;
} }
/**
* spider_net_link_phy
* @data: used for pointer to card structure
*
*/
static void spider_net_link_phy(unsigned long data)
{
struct spider_net_card *card = (struct spider_net_card *)data;
struct mii_phy *phy = &card->phy;
/* 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 failed,
* retry from beginning */
spider_net_setup_aneg(card);
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);
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);
if (phy->speed == 1000)
spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0x00000001);
else
spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0);
card->aneg_count = 0;
pr_debug("Found %s with %i Mbps, %s-duplex %sautoneg.\n",
phy->def->name, phy->speed, phy->duplex==1 ? "Full" : "Half",
phy->autoneg==1 ? "" : "no ");
return;
}
/** /**
* spider_net_setup_phy - setup PHY * spider_net_setup_phy - setup PHY
* @card: card structure * @card: card structure
* *
* returns 0 on success, <0 on failure * returns 0 on success, <0 on failure
* *
* spider_net_setup_phy is used as part of spider_net_probe. Sets * spider_net_setup_phy is used as part of spider_net_probe.
* the PHY to 1000 Mbps
**/ **/
static int static int
spider_net_setup_phy(struct spider_net_card *card) spider_net_setup_phy(struct spider_net_card *card)
...@@ -1714,21 +1853,21 @@ spider_net_setup_phy(struct spider_net_card *card) ...@@ -1714,21 +1853,21 @@ spider_net_setup_phy(struct spider_net_card *card)
SPIDER_NET_DMASEL_VALUE); SPIDER_NET_DMASEL_VALUE);
spider_net_write_reg(card, SPIDER_NET_GPCCTRL, spider_net_write_reg(card, SPIDER_NET_GPCCTRL,
SPIDER_NET_PHY_CTRL_VALUE); SPIDER_NET_PHY_CTRL_VALUE);
phy->mii_id = 1;
phy->dev = card->netdev; phy->dev = card->netdev;
phy->mdio_read = spider_net_read_phy; phy->mdio_read = spider_net_read_phy;
phy->mdio_write = spider_net_write_phy; phy->mdio_write = spider_net_write_phy;
mii_phy_probe(phy, phy->mii_id); for (phy->mii_id = 1; phy->mii_id <= 31; phy->mii_id++) {
unsigned short id;
if (phy->def->ops->setup_forced) id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR);
phy->def->ops->setup_forced(phy, SPEED_1000, DUPLEX_FULL); if (id != 0x0000 && id != 0xffff) {
if (!mii_phy_probe(phy, phy->mii_id)) {
phy->def->ops->enable_fiber(phy); pr_info("Found %s.\n", phy->def->name);
break;
phy->def->ops->read_link(phy); }
pr_info("Found %s with %i Mbps, %s-duplex.\n", phy->def->name, }
phy->speed, phy->duplex==1 ? "Full" : "Half"); }
return 0; return 0;
} }
...@@ -1900,11 +2039,13 @@ spider_net_stop(struct net_device *netdev) ...@@ -1900,11 +2039,13 @@ spider_net_stop(struct net_device *netdev)
netif_carrier_off(netdev); netif_carrier_off(netdev);
netif_stop_queue(netdev); netif_stop_queue(netdev);
del_timer_sync(&card->tx_timer); del_timer_sync(&card->tx_timer);
del_timer_sync(&card->aneg_timer);
/* disable/mask all interrupts */ /* disable/mask all interrupts */
spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0);
spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0);
spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0);
spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0);
free_irq(netdev->irq, netdev); free_irq(netdev->irq, netdev);
...@@ -2043,6 +2184,11 @@ spider_net_setup_netdev(struct spider_net_card *card) ...@@ -2043,6 +2184,11 @@ spider_net_setup_netdev(struct spider_net_card *card)
card->tx_timer.data = (unsigned long) card; card->tx_timer.data = (unsigned long) card;
netdev->irq = card->pdev->irq; netdev->irq = card->pdev->irq;
card->aneg_count = 0;
init_timer(&card->aneg_timer);
card->aneg_timer.function = spider_net_link_phy;
card->aneg_timer.data = (unsigned long) card;
card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT;
card->tx_chain.num_desc = tx_descriptors; card->tx_chain.num_desc = tx_descriptors;
......
...@@ -50,6 +50,8 @@ extern char spider_net_driver_name[]; ...@@ -50,6 +50,8 @@ extern char spider_net_driver_name[];
#define SPIDER_NET_TX_DESCRIPTORS_MAX 512 #define SPIDER_NET_TX_DESCRIPTORS_MAX 512
#define SPIDER_NET_TX_TIMER (HZ/5) #define SPIDER_NET_TX_TIMER (HZ/5)
#define SPIDER_NET_ANEG_TIMER (HZ)
#define SPIDER_NET_ANEG_TIMEOUT 2
#define SPIDER_NET_RX_CSUM_DEFAULT 1 #define SPIDER_NET_RX_CSUM_DEFAULT 1
...@@ -104,6 +106,7 @@ extern char spider_net_driver_name[]; ...@@ -104,6 +106,7 @@ extern char spider_net_driver_name[];
#define SPIDER_NET_GMACOPEMD 0x00000100 #define SPIDER_NET_GMACOPEMD 0x00000100
#define SPIDER_NET_GMACLENLMT 0x00000108 #define SPIDER_NET_GMACLENLMT 0x00000108
#define SPIDER_NET_GMACST 0x00000110
#define SPIDER_NET_GMACINTEN 0x00000118 #define SPIDER_NET_GMACINTEN 0x00000118
#define SPIDER_NET_GMACPHYCTRL 0x00000120 #define SPIDER_NET_GMACPHYCTRL 0x00000120
...@@ -333,9 +336,12 @@ enum spider_net_int2_status { ...@@ -333,9 +336,12 @@ enum spider_net_int2_status {
/* We rely on flagged descriptor interrupts */ /* We rely on flagged descriptor interrupts */
#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) ) #define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) )
#define SPIDER_NET_LINKINT ( 1 << SPIDER_NET_GMAC2INT )
#define SPIDER_NET_ERRINT ( 0xffffffff & \ #define SPIDER_NET_ERRINT ( 0xffffffff & \
(~SPIDER_NET_TXINT) & \ (~SPIDER_NET_TXINT) & \
(~SPIDER_NET_RXINT) ) (~SPIDER_NET_RXINT) & \
(~SPIDER_NET_LINKINT) )
#define SPIDER_NET_GPREXEC 0x80000000 #define SPIDER_NET_GPREXEC 0x80000000
#define SPIDER_NET_GPRDAT_MASK 0x0000ffff #define SPIDER_NET_GPRDAT_MASK 0x0000ffff
...@@ -442,6 +448,8 @@ struct spider_net_card { ...@@ -442,6 +448,8 @@ struct spider_net_card {
struct spider_net_descr_chain rx_chain; struct spider_net_descr_chain rx_chain;
struct spider_net_descr *low_watermark; struct spider_net_descr *low_watermark;
int aneg_count;
struct timer_list aneg_timer;
struct timer_list tx_timer; struct timer_list tx_timer;
struct work_struct tx_timeout_task; struct work_struct tx_timeout_task;
atomic_t tx_timeout_task_counter; atomic_t tx_timeout_task_counter;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment