X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fnet%2Fsky2.c;h=53839979cfb8fb1249dc27161b4cab2649fb994c;hb=e12651539808437a97f3e33c659c3d7b4000e41e;hp=e5679965501cb66daa1774de31d9764c8b53d7f5;hpb=7a7b5181517752005fd0dc822176a6975218ec0f;p=linux-2.6-omap-h63xx.git diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index e5679965501..53839979cfb 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -49,7 +49,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.12" +#define DRV_VERSION "1.13" #define PFX DRV_NAME " " /* @@ -1053,8 +1053,7 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF); sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF); - if (sky2->vlgrp) - sky2->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(sky2->vlgrp, vid, NULL); netif_tx_unlock_bh(dev); } @@ -1766,10 +1765,10 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) { struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; - u16 lpa; + u16 advert, lpa; + advert = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV); lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP); - if (lpa & PHY_M_AN_RF) { printk(KERN_ERR PFX "%s: remote fault", sky2->netdev->name); return -1; @@ -1784,20 +1783,40 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->speed = sky2_phy_speed(hw, aux); sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; - /* Pause bits are offset (9..8) */ - if (hw->chip_id == CHIP_ID_YUKON_XL - || hw->chip_id == CHIP_ID_YUKON_EC_U - || hw->chip_id == CHIP_ID_YUKON_EX) - aux >>= 6; - - sky2->flow_status = sky2_flow(aux & PHY_M_PS_RX_P_EN, - aux & PHY_M_PS_TX_P_EN); + /* Since the pause result bits seem to in different positions on + * different chips. look at registers. + */ + if (!sky2_is_copper(hw)) { + /* Shift for bits in fiber PHY */ + advert &= ~(ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM); + lpa &= ~(LPA_PAUSE_CAP|LPA_PAUSE_ASYM); + + if (advert & ADVERTISE_1000XPAUSE) + advert |= ADVERTISE_PAUSE_CAP; + if (advert & ADVERTISE_1000XPSE_ASYM) + advert |= ADVERTISE_PAUSE_ASYM; + if (lpa & LPA_1000XPAUSE) + lpa |= LPA_PAUSE_CAP; + if (lpa & LPA_1000XPAUSE_ASYM) + lpa |= LPA_PAUSE_ASYM; + } + + sky2->flow_status = FC_NONE; + if (advert & ADVERTISE_PAUSE_CAP) { + if (lpa & LPA_PAUSE_CAP) + sky2->flow_status = FC_BOTH; + else if (advert & ADVERTISE_PAUSE_ASYM) + sky2->flow_status = FC_RX; + } else if (advert & ADVERTISE_PAUSE_ASYM) { + if ((lpa & LPA_PAUSE_CAP) && (lpa & LPA_PAUSE_ASYM)) + sky2->flow_status = FC_TX; + } if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX)) sky2->flow_status = FC_NONE; - if (aux & PHY_M_PS_RX_P_EN) + if (sky2->flow_status & FC_TX) sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); else sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); @@ -1846,16 +1865,13 @@ out: spin_unlock(&sky2->phy_lock); } - /* Transmit timeout is only called if we are running, carrier is up * and tx queue is full (stopped). - * Called with netif_tx_lock held. */ static void sky2_tx_timeout(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - u32 imask; if (netif_msg_timer(sky2)) printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); @@ -1865,19 +1881,8 @@ static void sky2_tx_timeout(struct net_device *dev) sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX), sky2_read16(hw, Q_ADDR(txqaddr[sky2->port], Q_DONE))); - imask = sky2_read32(hw, B0_IMSK); /* block IRQ in hw */ - sky2_write32(hw, B0_IMSK, 0); - sky2_read32(hw, B0_IMSK); - - netif_poll_disable(hw->dev[0]); /* stop NAPI poll */ - synchronize_irq(hw->pdev->irq); - - netif_start_queue(dev); /* don't wakeup during flush */ - sky2_tx_complete(sky2, sky2->tx_prod); /* Flush transmit queue */ - - sky2_write32(hw, B0_IMSK, imask); - - sky2_phy_reinit(sky2); /* this clears flow control etc */ + /* can't restart safely under softirq */ + schedule_work(&hw->restart_work); } static int sky2_change_mtu(struct net_device *dev, int new_mtu) @@ -2050,9 +2055,6 @@ static struct sk_buff *sky2_receive(struct net_device *dev, if (!(status & GMR_FS_RX_OK)) goto resubmit; - if (length > dev->mtu + ETH_HLEN) - goto oversize; - if (length < copybreak) skb = receive_copy(sky2, re, length); else @@ -2062,14 +2064,10 @@ resubmit: return skb; -oversize: - ++sky2->net_stats.rx_over_errors; - goto resubmit; - error: ++sky2->net_stats.rx_errors; if (status & GMR_FS_RX_FF_OV) { - sky2->net_stats.rx_fifo_errors++; + sky2->net_stats.rx_over_errors++; goto resubmit; } @@ -2631,6 +2629,49 @@ static void sky2_reset(struct sky2_hw *hw) sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START); } +static void sky2_restart(struct work_struct *work) +{ + struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work); + struct net_device *dev; + int i, err; + + dev_dbg(&hw->pdev->dev, "restarting\n"); + + del_timer_sync(&hw->idle_timer); + + rtnl_lock(); + sky2_write32(hw, B0_IMSK, 0); + sky2_read32(hw, B0_IMSK); + + netif_poll_disable(hw->dev[0]); + + for (i = 0; i < hw->ports; i++) { + dev = hw->dev[i]; + if (netif_running(dev)) + sky2_down(dev); + } + + sky2_reset(hw); + sky2_write32(hw, B0_IMSK, Y2_IS_BASE); + netif_poll_enable(hw->dev[0]); + + for (i = 0; i < hw->ports; i++) { + dev = hw->dev[i]; + if (netif_running(dev)) { + err = sky2_up(dev); + if (err) { + printk(KERN_INFO PFX "%s: could not restart %d\n", + dev->name, err); + dev_close(dev); + } + } + } + + sky2_idle_start(hw); + + rtnl_unlock(); +} + static inline u8 sky2_wol_supported(const struct sky2_hw *hw) { return sky2_is_copper(hw) ? (WAKE_PHY | WAKE_MAGIC) : 0; @@ -3593,6 +3634,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); + INIT_WORK(&hw->restart_work, sky2_restart); + sky2_idle_start(hw); pci_set_drvdata(pdev, hw); @@ -3629,6 +3672,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev) del_timer_sync(&hw->idle_timer); + flush_scheduled_work(); + sky2_write32(hw, B0_IMSK, 0); synchronize_irq(hw->pdev->irq);