X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Fsky2.c;h=d77358649af7eec7b316ba018cf17680cfdc567b;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=4103e304fba95ccb230bff186362aec636376af4;hpb=f7ed79d23a47594e7834d66a8f14449796d4f3e6;p=linux-2.6.git diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 4103e304f..d77358649 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -51,7 +51,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.6.1" +#define DRV_VERSION "0.15" #define PFX DRV_NAME " " /* @@ -61,6 +61,10 @@ * a receive requires one (or two if using 64 bit dma). */ +#define is_ec_a1(hw) \ + unlikely((hw)->chip_id == CHIP_ID_YUKON_EC && \ + (hw)->chip_rev == CHIP_REV_YU_EC_A1) + #define RX_LE_SIZE 512 #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) #define RX_MAX_PENDING (RX_LE_SIZE/2 - 2) @@ -79,8 +83,6 @@ #define NAPI_WEIGHT 64 #define PHY_RETRIES 1000 -#define RING_NEXT(x,s) (((x)+1) & ((s)-1)) - static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR @@ -94,18 +96,11 @@ static int copybreak __read_mostly = 256; module_param(copybreak, int, 0); MODULE_PARM_DESC(copybreak, "Receive copy threshold"); -static int disable_msi = 0; -module_param(disable_msi, int, 0); -MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); - -static int idle_timeout = 100; -module_param(idle_timeout, int, 0); -MODULE_PARM_DESC(idle_timeout, "Idle timeout workaround for lost interrupts (ms)"); - static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, - { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */ + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4340) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4341) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4342) }, @@ -129,7 +124,6 @@ MODULE_DEVICE_TABLE(pci, sky2_id_table); /* Avoid conditionals by using array */ static const unsigned txqaddr[] = { Q_XA1, Q_XA2 }; static const unsigned rxqaddr[] = { Q_R1, Q_R2 }; -static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 }; /* This driver supports yukon2 chipset only */ static const char *yukon2_name[] = { @@ -187,11 +181,12 @@ static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) return v; } -static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) +static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) { u16 power_control; u32 reg1; int vaux; + int ret = 0; pr_debug("sky2_set_power_state %d\n", state); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); @@ -233,11 +228,8 @@ static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) if (hw->ports > 1) reg1 |= PCI_Y2_PHY2_COMA; } - sky2_pci_write32(hw, PCI_DEV_REG1, reg1); - udelay(100); if (hw->chip_id == CHIP_ID_YUKON_EC_U) { - sky2_write16(hw, B0_CTST, Y2_HW_WOL_ON); sky2_pci_write32(hw, PCI_DEV_REG3, 0); reg1 = sky2_pci_read32(hw, PCI_DEV_REG4); reg1 &= P_ASPM_CONTROL_MSK; @@ -245,6 +237,8 @@ static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) sky2_pci_write32(hw, PCI_DEV_REG5, 0); } + sky2_pci_write32(hw, PCI_DEV_REG1, reg1); + break; case PCI_D3hot: @@ -274,10 +268,12 @@ static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) break; default: printk(KERN_ERR PFX "Unknown power state %d\n", state); + ret = -1; } sky2_pci_write16(hw, hw->pm_cap + PCI_PM_CTRL, power_control); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + return ret; } static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) @@ -304,8 +300,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) struct sky2_port *sky2 = netdev_priv(hw->dev[port]); u16 ctrl, ct1000, adv, pg, ledctrl, ledover; - if (sky2->autoneg == AUTONEG_ENABLE && - !(hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { + if (sky2->autoneg == AUTONEG_ENABLE && hw->chip_id != CHIP_ID_YUKON_XL) { u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK | @@ -333,7 +328,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); if (sky2->autoneg == AUTONEG_ENABLE && - (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { + hw->chip_id == CHIP_ID_YUKON_XL) { ctrl &= ~PHY_M_PC_DSC_MSK; ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA; } @@ -465,11 +460,10 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); /* set LED Function Control register */ - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, - (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ - PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ - PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ + PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ + PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ /* set Polarity Control register */ gm_phy_write(hw, port, PHY_MARV_PHY_STAT, @@ -483,25 +477,6 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* restore page register */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); break; - case CHIP_ID_YUKON_EC_U: - pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); - - /* select page 3 to access LED control register */ - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); - - /* set LED Function Control register */ - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, - (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(8) | /* 10 Mbps */ - PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ - PHY_M_LEDC_STA0_CTRL(7)));/* 1000 Mbps */ - - /* set Blink Rate in LED Timer Control Register */ - gm_phy_write(hw, port, PHY_MARV_INT_MASK, - ledctrl | PHY_M_LED_BLINK_RT(BLINK_84MS)); - /* restore page register */ - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); - break; default: /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */ @@ -510,21 +485,19 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); } - if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == CHIP_REV_YU_EC_A1) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev >= 2) { /* apply fixes in PHY AFE */ - pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255); - + gm_phy_write(hw, port, 22, 255); /* increase differential signal amplitude in 10BASE-T */ - gm_phy_write(hw, port, 0x18, 0xaa99); - gm_phy_write(hw, port, 0x17, 0x2011); + gm_phy_write(hw, port, 24, 0xaa99); + gm_phy_write(hw, port, 23, 0x2011); /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ - gm_phy_write(hw, port, 0x18, 0xa204); - gm_phy_write(hw, port, 0x17, 0x2002); + gm_phy_write(hw, port, 24, 0xa204); + gm_phy_write(hw, port, 23, 0x2002); /* set page register to 0 */ - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + gm_phy_write(hw, port, 22, 0); } else { gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); @@ -547,9 +520,9 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* Force a renegotiation */ static void sky2_phy_reinit(struct sky2_port *sky2) { - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); sky2_phy_init(sky2->hw, sky2->port); - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); } static void sky2_mac_init(struct sky2_hw *hw, unsigned port) @@ -598,11 +571,6 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) if (sky2->duplex == DUPLEX_FULL) reg |= GM_GPCR_DUP_FULL; - - /* turn off pause in 10/100mbps half duplex */ - else if (sky2->speed != SPEED_1000 && - hw->chip_id != CHIP_ID_YUKON_EC_U) - sky2->tx_pause = sky2->rx_pause = 0; } else reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; @@ -619,9 +587,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC)); - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); sky2_phy_init(hw, port); - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); /* MIB clear */ reg = gma_read16(hw, port, GM_PHY_ADDR); @@ -769,15 +737,41 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2) { struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod; - sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE); + sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE; return le; } -/* Update chip's next pointer */ -static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx) +/* + * This is a workaround code taken from SysKonnect sk98lin driver + * to deal with chip bug on Yukon EC rev 0 in the wraparound case. + */ +static void sky2_put_idx(struct sky2_hw *hw, unsigned q, + u16 idx, u16 *last, u16 size) { wmb(); - sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx); + if (is_ec_a1(hw) && idx < *last) { + u16 hwget = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX)); + + if (hwget == 0) { + /* Start prefetching again */ + sky2_write8(hw, Y2_QADDR(q, PREF_UNIT_FIFO_WM), 0xe0); + goto setnew; + } + + if (hwget == size - 1) { + /* set watermark to one list element */ + sky2_write8(hw, Y2_QADDR(q, PREF_UNIT_FIFO_WM), 8); + + /* set put index to first list element */ + sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), 0); + } else /* have hardware go to end of list */ + sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), + size - 1); + } else { +setnew: + sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx); + } + *last = idx; mmiowb(); } @@ -785,7 +779,7 @@ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx) static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2) { struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put; - sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE); + sky2->rx_put = (sky2->rx_put + 1) % RX_LE_SIZE; return le; } @@ -900,7 +894,7 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!netif_running(dev)) return -ENODEV; /* Phy still in reset */ - switch (cmd) { + switch(cmd) { case SIOCGMIIPHY: data->phy_id = PHY_ADDR_MARV; @@ -908,9 +902,9 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCGMIIREG: { u16 val = 0; - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val); - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); data->val_out = val; break; @@ -920,10 +914,10 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f, data->val_in); - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); break; } return err; @@ -975,7 +969,8 @@ static inline struct sk_buff *sky2_alloc_skb(unsigned int size, gfp_t gfp_mask) skb = __dev_alloc_skb(size + RX_SKB_ALIGN, gfp_mask); if (likely(skb)) { unsigned long p = (unsigned long) skb->data; - skb_reserve(skb, ALIGN(p, RX_SKB_ALIGN) - p); + skb_reserve(skb, + ((p + RX_SKB_ALIGN - 1) & ~(RX_SKB_ALIGN - 1)) - p); } return skb; @@ -992,7 +987,6 @@ static int sky2_rx_start(struct sky2_port *sky2) struct sky2_hw *hw = sky2->hw; unsigned rxq = rxqaddr[sky2->port]; int i; - unsigned thresh; sky2->rx_put = sky2->rx_next = 0; sky2_qset(hw, rxq); @@ -1017,24 +1011,13 @@ static int sky2_rx_start(struct sky2_port *sky2) sky2_rx_add(sky2, re->mapaddr); } - - /* - * The receiver hangs if it receives frames larger than the - * packet buffer. As a workaround, truncate oversize frames, but - * the register is limited to 9 bits, so if you do frames > 2052 - * you better get the MTU right! - */ - thresh = (sky2->rx_bufsize - 8) / sizeof(u32); - if (thresh > 0x1ff) - sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF); - else { - sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), thresh); - sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON); - } - + /* Truncate oversize frames */ + sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), sky2->rx_bufsize - 8); + sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON); /* Tell chip about available buffers */ sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put); + sky2->rx_last_put = sky2_read16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX)); return 0; nomem: sky2_rx_clean(sky2); @@ -1047,26 +1030,8 @@ static int sky2_up(struct net_device *dev) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; - u32 ramsize, rxspace, imask; - int cap, err = -ENOMEM; - struct net_device *otherdev = hw->dev[sky2->port^1]; - - /* - * On dual port PCI-X card, there is an problem where status - * can be received out of order due to split transactions - */ - if (otherdev && netif_running(otherdev) && - (cap = pci_find_capability(hw->pdev, PCI_CAP_ID_PCIX))) { - struct sky2_port *osky2 = netdev_priv(otherdev); - u16 cmd; - - cmd = sky2_pci_read16(hw, cap + PCI_X_CMD); - cmd &= ~PCI_X_CMD_MAX_SPLIT; - sky2_pci_write16(hw, cap + PCI_X_CMD, cmd); - - sky2->rx_csum = 0; - osky2->rx_csum = 0; - } + u32 ramsize, rxspace; + int err = -ENOMEM; if (netif_msg_ifup(sky2)) printk(KERN_INFO PFX "%s: enabling interface\n", dev->name); @@ -1130,10 +1095,10 @@ static int sky2_up(struct net_device *dev) goto err_out; /* Enable interrupts from phy/mac for port */ - imask = sky2_read32(hw, B0_IMSK); - imask |= portirq_msk[port]; - sky2_write32(hw, B0_IMSK, imask); - + spin_lock_irq(&hw->hw_lock); + hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); return 0; err_out: @@ -1159,7 +1124,7 @@ err_out: /* Modular subtraction in ring */ static inline int tx_dist(unsigned tail, unsigned head) { - return (head - tail) & (TX_RING_SIZE - 1); + return (head - tail) % TX_RING_SIZE; } /* Number of list elements available for next tx */ @@ -1176,7 +1141,7 @@ static unsigned tx_le_req(const struct sk_buff *skb) count = sizeof(dma_addr_t) / sizeof(u32); count += skb_shinfo(skb)->nr_frags * count; - if (skb_is_gso(skb)) + if (skb_shinfo(skb)->tso_size) ++count; if (skb->ip_summed == CHECKSUM_HW) @@ -1248,12 +1213,12 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) } /* Check for TCP Segmentation Offload */ - mss = skb_shinfo(skb)->gso_size; + mss = skb_shinfo(skb)->tso_size; if (mss != 0) { /* just drop the packet if non-linear expansion fails */ if (skb_header_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); goto out_unlock; } @@ -1336,7 +1301,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) le->opcode = OP_BUFFER | HW_OWNER; fre = sky2->tx_ring - + RING_NEXT((re - sky2->tx_ring) + i, TX_RING_SIZE); + + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; pci_unmap_addr_set(fre, mapaddr, mapping); } @@ -1350,7 +1315,8 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); } - sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod); + sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod, + &sky2->tx_last_put, TX_RING_SIZE); out_unlock: spin_unlock(&sky2->tx_lock); @@ -1382,7 +1348,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) struct tx_ring_info *re = sky2->tx_ring + put; struct sk_buff *skb = re->skb; - nxt = re->idx; + nxt = re->idx; BUG_ON(nxt >= TX_RING_SIZE); prefetch(sky2->tx_ring + nxt); @@ -1396,17 +1362,17 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct tx_ring_info *fre; - fre = sky2->tx_ring + RING_NEXT(put + i, TX_RING_SIZE); + fre = sky2->tx_ring + (put + i + 1) % TX_RING_SIZE; pci_unmap_page(pdev, pci_unmap_addr(fre, mapaddr), - skb_shinfo(skb)->frags[i].size, + skb_shinfo(skb)->frags[i].size, PCI_DMA_TODEVICE); } - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); } sky2->tx_cons = put; - if (tx_avail(sky2) > MAX_SKB_TX_LE) + if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE) netif_wake_queue(dev); } @@ -1425,7 +1391,6 @@ static int sky2_down(struct net_device *dev) struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; u16 ctrl; - u32 imask; /* Never really got started! */ if (!sky2->tx_le) @@ -1437,6 +1402,14 @@ static int sky2_down(struct net_device *dev) /* Stop more packets from being queued */ netif_stop_queue(dev); + /* Disable port IRQ */ + spin_lock_irq(&hw->hw_lock); + hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); + sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); + + flush_scheduled_work(); + sky2_phy_reset(hw, port); /* Stop transmitter */ @@ -1480,11 +1453,6 @@ static int sky2_down(struct net_device *dev) sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); - /* Disable port IRQ */ - imask = sky2_read32(hw, B0_IMSK); - imask &= ~portirq_msk[port]; - sky2_write32(hw, B0_IMSK, imask); - /* turn off LED's */ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); @@ -1579,26 +1547,17 @@ static void sky2_link_up(struct sky2_port *sky2) sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF); - if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) { + if (hw->chip_id == CHIP_ID_YUKON_XL) { u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); - u16 led = PHY_M_LEDC_LOS_CTRL(1); /* link active */ - - switch(sky2->speed) { - case SPEED_10: - led |= PHY_M_LEDC_INIT_CTRL(7); - break; - - case SPEED_100: - led |= PHY_M_LEDC_STA1_CTRL(7); - break; - - case SPEED_1000: - led |= PHY_M_LEDC_STA0_CTRL(7); - break; - } gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, led); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(sky2->speed == + SPEED_10 ? 7 : 0) | + PHY_M_LEDC_STA1_CTRL(sky2->speed == + SPEED_100 ? 7 : 0) | + PHY_M_LEDC_STA0_CTRL(sky2->speed == + SPEED_1000 ? 7 : 0)); gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } @@ -1673,7 +1632,7 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->speed = sky2_phy_speed(hw, aux); /* Pause bits are offset (9..8) */ - if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) + if (hw->chip_id == CHIP_ID_YUKON_XL) aux >>= 6; sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0; @@ -1688,19 +1647,20 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) return 0; } -/* Interrupt from PHY */ -static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) +/* + * Interrupt from PHY are handled outside of interrupt context + * because accessing phy registers requires spin wait which might + * cause excess interrupt latency. + */ +static void sky2_phy_task(void *arg) { - struct net_device *dev = hw->dev[port]; - struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_port *sky2 = arg; + struct sky2_hw *hw = sky2->hw; u16 istatus, phystat; - spin_lock(&sky2->phy_lock); - istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT); - phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT); - - if (!netif_running(dev)) - goto out; + down(&sky2->phy_sema); + istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT); + phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT); if (netif_msg_intr(sky2)) printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n", @@ -1726,7 +1686,12 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) sky2_link_down(sky2); } out: - spin_unlock(&sky2->phy_lock); + up(&sky2->phy_sema); + + spin_lock_irq(&hw->hw_lock); + hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); } @@ -1738,49 +1703,41 @@ static void sky2_tx_timeout(struct net_device *dev) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; unsigned txq = txqaddr[sky2->port]; - u16 report, done; - - if (netif_msg_timer(sky2)) - printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); + u16 ridx; - report = sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX); - done = sky2_read16(hw, Q_ADDR(txq, Q_DONE)); - - printk(KERN_DEBUG PFX "%s: transmit ring %u .. %u report=%u done=%u\n", - dev->name, - sky2->tx_cons, sky2->tx_prod, report, done); - - if (report != done) { - printk(KERN_INFO PFX "status burst pending (irq moderation?)\n"); + /* Maybe we just missed an status interrupt */ + spin_lock(&sky2->tx_lock); + ridx = sky2_read16(hw, + sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX); + sky2_tx_complete(sky2, ridx); + spin_unlock(&sky2->tx_lock); - sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); - sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); - } else if (report != sky2->tx_cons) { - printk(KERN_INFO PFX "status report lost?\n"); + if (!netif_queue_stopped(dev)) { + if (net_ratelimit()) + pr_info(PFX "transmit interrupt missed? recovered\n"); + return; + } - spin_lock_bh(&sky2->tx_lock); - sky2_tx_complete(sky2, report); - spin_unlock_bh(&sky2->tx_lock); - } else { - printk(KERN_INFO PFX "hardware hung? flushing\n"); + if (netif_msg_timer(sky2)) + printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); - sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP); - sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); + sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP); + sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); - sky2_tx_clean(sky2); + sky2_tx_clean(sky2); - sky2_qset(hw, txq); - sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1); - } + sky2_qset(hw, txq); + sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1); } +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* Want receive buffer size to be multiple of 64 bits * and incl room for vlan and truncation */ static inline unsigned sky2_buf_size(int mtu) { - return ALIGN(mtu + ETH_HLEN + VLAN_HLEN, 8) + 8; + return roundup(mtu + ETH_HLEN + VLAN_HLEN, 8) + 8; } static int sky2_change_mtu(struct net_device *dev, int new_mtu) @@ -1789,7 +1746,6 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) struct sky2_hw *hw = sky2->hw; int err; u16 ctl, mode; - u32 imask; if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) return -EINVAL; @@ -1802,15 +1758,12 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) return 0; } - imask = sky2_read32(hw, B0_IMSK); sky2_write32(hw, B0_IMSK, 0); dev->trans_start = jiffies; /* prevent tx timeout */ netif_stop_queue(dev); netif_poll_disable(hw->dev[0]); - synchronize_irq(hw->pdev->irq); - ctl = gma_read16(hw, sky2->port, GM_GP_CTRL); gma_write16(hw, sky2->port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA); sky2_rx_stop(sky2); @@ -1829,7 +1782,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD); err = sky2_rx_start(sky2); - sky2_write32(hw, B0_IMSK, imask); + sky2_write32(hw, B0_IMSK, hw->intr_mask); if (err) dev_close(dev); @@ -1906,7 +1859,8 @@ resubmit: sky2_rx_add(sky2, re->mapaddr); /* Tell receiver about new buffers. */ - sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put); + sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put, + &sky2->rx_last_put, RX_LE_SIZE); return skb; @@ -1933,33 +1887,55 @@ error: goto resubmit; } -/* Transmit complete */ -static inline void sky2_tx_done(struct net_device *dev, u16 last) +/* + * Check for transmit complete + */ +#define TX_NO_STATUS 0xffff + +static void sky2_tx_check(struct sky2_hw *hw, int port, u16 last) { - struct sky2_port *sky2 = netdev_priv(dev); + if (last != TX_NO_STATUS) { + struct net_device *dev = hw->dev[port]; + if (dev && netif_running(dev)) { + struct sky2_port *sky2 = netdev_priv(dev); - if (netif_running(dev)) { - spin_lock(&sky2->tx_lock); - sky2_tx_complete(sky2, last); - spin_unlock(&sky2->tx_lock); + spin_lock(&sky2->tx_lock); + sky2_tx_complete(sky2, last); + spin_unlock(&sky2->tx_lock); + } } } -/* Is status ring empty or is there more to do? */ -static inline int sky2_more_work(const struct sky2_hw *hw) +/* + * Both ports share the same status interrupt, therefore there is only + * one poll routine. + */ +static int sky2_poll(struct net_device *dev0, int *budget) { - return (hw->st_idx != sky2_read16(hw, STAT_PUT_IDX)); -} + struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; + unsigned int to_do = min(dev0->quota, *budget); + unsigned int work_done = 0; + u16 hwidx; + u16 tx_done[2] = { TX_NO_STATUS, TX_NO_STATUS }; -/* Process status response ring */ -static int sky2_status_intr(struct sky2_hw *hw, int to_do) -{ - int work_done = 0; - u16 hwidx = sky2_read16(hw, STAT_PUT_IDX); + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + /* + * Kick the STAT_LEV_TIMER_CTRL timer. + * This fixes my hangs on Yukon-EC (0xb6) rev 1. + * The if clause is there to start the timer only if it has been + * configured correctly and not been disabled via ethtool. + */ + if (sky2_read8(hw, STAT_LEV_TIMER_CTRL) == TIM_START) { + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_STOP); + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START); + } + + hwidx = sky2_read16(hw, STAT_PUT_IDX); + BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); - while (hw->st_idx != hwidx) { + while (hwidx != hw->st_idx) { struct sky2_status_le *le = hw->st_le + hw->st_idx; struct net_device *dev; struct sky2_port *sky2; @@ -1967,14 +1943,18 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) u32 status; u16 length; - hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE); + le = hw->st_le + hw->st_idx; + hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; + prefetch(hw->st_le + hw->st_idx); BUG_ON(le->link >= 2); dev = hw->dev[le->link]; + if (dev == NULL || !netif_running(dev)) + continue; sky2 = netdev_priv(dev); - length = le->length; - status = le->status; + status = le32_to_cpu(le->status); + length = le16_to_cpu(le->length); switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: @@ -2016,27 +1996,42 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) case OP_TXINDEXLE: /* TX index reports status for both ports */ - BUILD_BUG_ON(TX_RING_SIZE > 0x1000); - sky2_tx_done(hw->dev[0], status & 0xfff); - if (hw->dev[1]) - sky2_tx_done(hw->dev[1], - ((status >> 24) & 0xff) - | (u16)(length & 0xf) << 8); + tx_done[0] = status & 0xffff; + tx_done[1] = ((status >> 24) & 0xff) + | (u16)(length & 0xf) << 8; break; default: if (net_ratelimit()) printk(KERN_WARNING PFX "unknown status opcode 0x%x\n", le->opcode); - goto exit_loop; + break; } } - /* Fully processed status ring so clear irq */ - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - exit_loop: - return work_done; + sky2_tx_check(hw, 0, tx_done[0]); + sky2_tx_check(hw, 1, tx_done[1]); + + if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_START) { + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); + } + + if (likely(work_done < to_do)) { + spin_lock_irq(&hw->hw_lock); + __netif_rx_complete(dev0); + + hw->intr_mask |= Y2_IS_STAT_BMU; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); + + return 0; + } else { + *budget -= work_done; + dev0->quota -= work_done; + return 1; + } } static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status) @@ -2155,60 +2150,42 @@ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port) } } -/* This should never happen it is a fatal situation */ -static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, - const char *rxtx, u32 mask) +static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) { struct net_device *dev = hw->dev[port]; struct sky2_port *sky2 = netdev_priv(dev); - u32 imask; - - printk(KERN_ERR PFX "%s: %s descriptor error (hardware problem)\n", - dev ? dev->name : "", rxtx); - imask = sky2_read32(hw, B0_IMSK); - imask &= ~mask; - sky2_write32(hw, B0_IMSK, imask); - - if (dev) { - spin_lock(&sky2->phy_lock); - sky2_link_down(sky2); - spin_unlock(&sky2->phy_lock); - } -} + hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); + sky2_write32(hw, B0_IMSK, hw->intr_mask); -/* If idle then force a fake soft NAPI poll once a second - * to work around cases where sharing an edge triggered interrupt. - */ -static inline void sky2_idle_start(struct sky2_hw *hw) -{ - if (idle_timeout > 0) - mod_timer(&hw->idle_timer, - jiffies + msecs_to_jiffies(idle_timeout)); + schedule_work(&sky2->phy_task); } -static void sky2_idle(unsigned long arg) +static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) { - struct sky2_hw *hw = (struct sky2_hw *) arg; - struct net_device *dev = hw->dev[0]; - - if (__netif_rx_schedule_prep(dev)) - __netif_rx_schedule(dev); - - mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout)); -} - + struct sky2_hw *hw = dev_id; + struct net_device *dev0 = hw->dev[0]; + u32 status; -static int sky2_poll(struct net_device *dev0, int *budget) -{ - struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; - int work_limit = min(dev0->quota, *budget); - int work_done = 0; - u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + status = sky2_read32(hw, B0_Y2_SP_ISRC2); + if (status == 0 || status == ~0) + return IRQ_NONE; + spin_lock(&hw->hw_lock); if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); + /* Do NAPI for Rx and Tx status */ + if (status & Y2_IS_STAT_BMU) { + hw->intr_mask &= ~Y2_IS_STAT_BMU; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + + if (likely(__netif_rx_schedule_prep(dev0))) { + prefetch(&hw->st_le[hw->st_idx]); + __netif_rx_schedule(dev0); + } + } + if (status & Y2_IS_IRQ_PHY1) sky2_phy_intr(hw, 0); @@ -2221,45 +2198,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) if (status & Y2_IS_IRQ_MAC2) sky2_mac_intr(hw, 1); - if (status & Y2_IS_CHK_RX1) - sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); - - if (status & Y2_IS_CHK_RX2) - sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); - - if (status & Y2_IS_CHK_TXA1) - sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); - - if (status & Y2_IS_CHK_TXA2) - sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); - - work_done = sky2_status_intr(hw, work_limit); - *budget -= work_done; - dev0->quota -= work_done; - - if (sky2_more_work(hw)) - return 1; - - netif_rx_complete(dev0); - - sky2_read32(hw, B0_Y2_SP_LISR); - return 0; -} - -static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct sky2_hw *hw = dev_id; - struct net_device *dev0 = hw->dev[0]; - u32 status; - - /* Reading this mask interrupts as side effect */ - status = sky2_read32(hw, B0_Y2_SP_ISRC2); - if (status == 0 || status == ~0) - return IRQ_NONE; + sky2_write32(hw, B0_Y2_SP_ICR, 2); - prefetch(&hw->st_le[hw->st_idx]); - if (likely(__netif_rx_schedule_prep(dev0))) - __netif_rx_schedule(dev0); + spin_unlock(&hw->hw_lock); return IRQ_HANDLED; } @@ -2268,10 +2209,8 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) static void sky2_netpoll(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); - struct net_device *dev0 = sky2->hw->dev[0]; - if (netif_running(dev) && __netif_rx_schedule_prep(dev0)) - __netif_rx_schedule(dev0); + sky2_intr(sky2->hw->pdev->irq, sky2->hw, NULL); } #endif @@ -2300,7 +2239,7 @@ static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk) } -static int __devinit sky2_reset(struct sky2_hw *hw) +static int sky2_reset(struct sky2_hw *hw) { u16 status; u8 t8; @@ -2315,16 +2254,6 @@ static int __devinit sky2_reset(struct sky2_hw *hw) return -EOPNOTSUPP; } - hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4; - - /* This rev is really old, and requires untested workarounds */ - if (hw->chip_id == CHIP_ID_YUKON_EC && hw->chip_rev == CHIP_REV_YU_EC_A1) { - printk(KERN_ERR PFX "%s: unsupported revision Yukon-%s (0x%x) rev %d\n", - pci_name(hw->pdev), yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL], - hw->chip_id, hw->chip_rev); - return -EOPNOTSUPP; - } - /* disable ASF */ if (hw->chip_id <= CHIP_ID_YUKON_EC) { sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); @@ -2345,7 +2274,7 @@ static int __devinit sky2_reset(struct sky2_hw *hw) sky2_write8(hw, B0_CTST, CS_MRST_CLR); /* clear any PEX errors */ - if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) + if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) sky2_pci_write32(hw, PEX_UNC_ERR_STAT, 0xffffffffUL); @@ -2356,6 +2285,7 @@ static int __devinit sky2_reset(struct sky2_hw *hw) if (!(sky2_read8(hw, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC)) ++hw->ports; } + hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4; sky2_set_power_state(hw, PCI_D0); @@ -2421,18 +2351,30 @@ static int __devinit sky2_reset(struct sky2_hw *hw) /* Set the list last index */ sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1); - sky2_write16(hw, STAT_TX_IDX_TH, 10); - sky2_write8(hw, STAT_FIFO_WM, 16); + /* These status setup values are copied from SysKonnect's driver */ + if (is_ec_a1(hw)) { + /* WA for dev. #4.3 */ + sky2_write16(hw, STAT_TX_IDX_TH, 0xfff); /* Tx Threshold */ - /* set Status-FIFO ISR watermark */ - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0) - sky2_write8(hw, STAT_FIFO_ISR_WM, 4); - else - sky2_write8(hw, STAT_FIFO_ISR_WM, 16); + /* set Status-FIFO watermark */ + sky2_write8(hw, STAT_FIFO_WM, 0x21); /* WA for dev. #4.18 */ - sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); - sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 20)); - sky2_write32(hw, STAT_LEV_TIMER_INI, sky2_us2clk(hw, 100)); + /* set Status-FIFO ISR watermark */ + sky2_write8(hw, STAT_FIFO_ISR_WM, 0x07); /* WA for dev. #4.18 */ + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 10000)); + } else { + sky2_write16(hw, STAT_TX_IDX_TH, 10); + sky2_write8(hw, STAT_FIFO_WM, 16); + + /* set Status-FIFO ISR watermark */ + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0) + sky2_write8(hw, STAT_FIFO_ISR_WM, 4); + else + sky2_write8(hw, STAT_FIFO_ISR_WM, 16); + + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); + sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 7)); + } /* enable status unit */ sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON); @@ -2577,34 +2519,17 @@ static const struct sky2_stat { { "rx_unicast", GM_RXF_UC_OK }, { "tx_mac_pause", GM_TXF_MPAUSE }, { "rx_mac_pause", GM_RXF_MPAUSE }, - { "collisions", GM_TXF_COL }, + { "collisions", GM_TXF_SNG_COL }, { "late_collision",GM_TXF_LAT_COL }, { "aborted", GM_TXF_ABO_COL }, - { "single_collisions", GM_TXF_SNG_COL }, { "multi_collisions", GM_TXF_MUL_COL }, - - { "rx_short", GM_RXF_SHT }, + { "fifo_underrun", GM_TXE_FIFO_UR }, + { "fifo_overflow", GM_RXE_FIFO_OV }, + { "rx_toolong", GM_RXF_LNG_ERR }, + { "rx_jabber", GM_RXF_JAB_PKT }, { "rx_runt", GM_RXE_FRAG }, - { "rx_64_byte_packets", GM_RXF_64B }, - { "rx_65_to_127_byte_packets", GM_RXF_127B }, - { "rx_128_to_255_byte_packets", GM_RXF_255B }, - { "rx_256_to_511_byte_packets", GM_RXF_511B }, - { "rx_512_to_1023_byte_packets", GM_RXF_1023B }, - { "rx_1024_to_1518_byte_packets", GM_RXF_1518B }, - { "rx_1518_to_max_byte_packets", GM_RXF_MAX_SZ }, { "rx_too_long", GM_RXF_LNG_ERR }, - { "rx_fifo_overflow", GM_RXE_FIFO_OV }, - { "rx_jabber", GM_RXF_JAB_PKT }, { "rx_fcs_error", GM_RXF_FCS_ERR }, - - { "tx_64_byte_packets", GM_TXF_64B }, - { "tx_65_to_127_byte_packets", GM_TXF_127B }, - { "tx_128_to_255_byte_packets", GM_TXF_255B }, - { "tx_256_to_511_byte_packets", GM_TXF_511B }, - { "tx_512_to_1023_byte_packets", GM_TXF_1023B }, - { "tx_1024_to_1518_byte_packets", GM_TXF_1518B }, - { "tx_1519_to_max_byte_packets", GM_TXF_MAX_SZ }, - { "tx_fifo_underrun", GM_TXE_FIFO_UR }, }; static u32 sky2_get_rx_csum(struct net_device *dev) @@ -2706,7 +2631,7 @@ static struct net_device_stats *sky2_get_stats(struct net_device *dev) sky2->net_stats.rx_bytes = data[1]; sky2->net_stats.tx_packets = data[2] + data[4] + data[6]; sky2->net_stats.rx_packets = data[3] + data[5] + data[7]; - sky2->net_stats.multicast = data[3] + data[5]; + sky2->net_stats.multicast = data[5] + data[7]; sky2->net_stats.collisions = data[10]; sky2->net_stats.tx_aborted_errors = data[12]; @@ -2835,7 +2760,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) ms = data * 1000; /* save initial values */ - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); if (hw->chip_id == CHIP_ID_YUKON_XL) { u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); @@ -2851,9 +2776,9 @@ static int sky2_phys_id(struct net_device *dev, u32 data) sky2_led(hw, port, onoff); onoff = !onoff; - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); interrupted = msleep_interruptible(250); - spin_lock_bh(&sky2->phy_lock); + down(&sky2->phy_sema); ms -= 250; } @@ -2868,7 +2793,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); } - spin_unlock_bh(&sky2->phy_lock); + up(&sky2->phy_sema); return 0; } @@ -2898,6 +2823,38 @@ static int sky2_set_pauseparam(struct net_device *dev, return err; } +#ifdef CONFIG_PM +static void sky2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + wol->supported = WAKE_MAGIC; + wol->wolopts = sky2->wol ? WAKE_MAGIC : 0; +} + +static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + + if (wol->wolopts != WAKE_MAGIC && wol->wolopts != 0) + return -EOPNOTSUPP; + + sky2->wol = wol->wolopts == WAKE_MAGIC; + + if (sky2->wol) { + memcpy_toio(hw->regs + WOL_MAC_ADDR, dev->dev_addr, ETH_ALEN); + + sky2_write16(hw, WOL_CTRL_STAT, + WOL_CTL_ENA_PME_ON_MAGIC_PKT | + WOL_CTL_ENA_MAGIC_PKT_UNIT); + } else + sky2_write16(hw, WOL_CTRL_STAT, WOL_CTL_DEFAULT); + + return 0; +} +#endif + static int sky2_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ecmd) { @@ -2938,11 +2895,19 @@ static int sky2_set_coalesce(struct net_device *dev, { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - const u32 tmax = sky2_clk2us(hw, 0x0ffffff); + const u32 tmin = sky2_clk2us(hw, 1); + const u32 tmax = 5000; + + if (ecmd->tx_coalesce_usecs != 0 && + (ecmd->tx_coalesce_usecs < tmin || ecmd->tx_coalesce_usecs > tmax)) + return -EINVAL; - if (ecmd->tx_coalesce_usecs > tmax || - ecmd->rx_coalesce_usecs > tmax || - ecmd->rx_coalesce_usecs_irq > tmax) + if (ecmd->rx_coalesce_usecs != 0 && + (ecmd->rx_coalesce_usecs < tmin || ecmd->rx_coalesce_usecs > tmax)) + return -EINVAL; + + if (ecmd->rx_coalesce_usecs_irq != 0 && + (ecmd->rx_coalesce_usecs_irq < tmin || ecmd->rx_coalesce_usecs_irq > tmax)) return -EINVAL; if (ecmd->tx_max_coalesced_frames >= TX_RING_SIZE-1) @@ -3077,6 +3042,10 @@ static struct ethtool_ops sky2_ethtool_ops = { .set_ringparam = sky2_set_ringparam, .get_pauseparam = sky2_get_pauseparam, .set_pauseparam = sky2_set_pauseparam, +#ifdef CONFIG_PM + .get_wol = sky2_get_wol, + .set_wol = sky2_set_wol, +#endif .phys_id = sky2_phys_id, .get_stats_count = sky2_get_stats_count, .get_ethtool_stats = sky2_get_ethtool_stats, @@ -3129,11 +3098,17 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, sky2->duplex = -1; sky2->speed = -1; sky2->advertising = sky2_supported_modes(hw); - sky2->rx_csum = 1; - spin_lock_init(&sky2->phy_lock); + /* Receive checksum disabled for Yukon XL + * because of observed problems with incorrect + * values when multiple packets are received in one interrupt + */ + sky2->rx_csum = (hw->chip_id != CHIP_ID_YUKON_XL); + + INIT_WORK(&sky2->phy_task, sky2_phy_task, sky2); + init_MUTEX(&sky2->phy_sema); sky2->tx_pending = TX_DEF_PENDING; - sky2->rx_pending = RX_DEF_PENDING; + sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING; sky2->rx_bufsize = sky2_buf_size(ETH_DATA_LEN); hw->dev[port] = dev; @@ -3175,66 +3150,6 @@ static void __devinit sky2_show_addr(struct net_device *dev) dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); } -/* Handle software interrupt used during MSI test */ -static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id, - struct pt_regs *regs) -{ - struct sky2_hw *hw = dev_id; - u32 status = sky2_read32(hw, B0_Y2_SP_ISRC2); - - if (status == 0) - return IRQ_NONE; - - if (status & Y2_IS_IRQ_SW) { - hw->msi_detected = 1; - wake_up(&hw->msi_wait); - sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ); - } - sky2_write32(hw, B0_Y2_SP_ICR, 2); - - return IRQ_HANDLED; -} - -/* Test interrupt path by forcing a a software IRQ */ -static int __devinit sky2_test_msi(struct sky2_hw *hw) -{ - struct pci_dev *pdev = hw->pdev; - int err; - - init_waitqueue_head (&hw->msi_wait); - - sky2_write32(hw, B0_IMSK, Y2_IS_IRQ_SW); - - err = request_irq(pdev->irq, sky2_test_intr, SA_SHIRQ, DRV_NAME, hw); - if (err) { - printk(KERN_ERR PFX "%s: cannot assign irq %d\n", - pci_name(pdev), pdev->irq); - return err; - } - - sky2_write8(hw, B0_CTST, CS_ST_SW_IRQ); - sky2_read8(hw, B0_CTST); - - wait_event_timeout(hw->msi_wait, hw->msi_detected, HZ/10); - - if (!hw->msi_detected) { - /* MSI test failed, go back to INTx mode */ - printk(KERN_WARNING PFX "%s: No interrupt was generated using MSI, " - "switching to INTx mode. Please report this failure to " - "the PCI maintainer and include system chipset information.\n", - pci_name(pdev)); - - err = -EOPNOTSUPP; - sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ); - } - - sky2_write32(hw, B0_IMSK, 0); - - free_irq(pdev->irq, hw); - - return err; -} - static int __devinit sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -3303,6 +3218,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out_free_hw; } hw->pm_cap = pm_cap; + spin_lock_init(&hw->hw_lock); #ifdef __BIG_ENDIAN /* byte swap descriptors in hardware */ @@ -3355,32 +3271,21 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } } - if (!disable_msi && pci_enable_msi(pdev) == 0) { - err = sky2_test_msi(hw); - if (err == -EOPNOTSUPP) - pci_disable_msi(pdev); - else if (err) - goto err_out_unregister; - } - - err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw); + err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw); if (err) { printk(KERN_ERR PFX "%s: cannot assign irq %d\n", pci_name(pdev), pdev->irq); goto err_out_unregister; } - sky2_write32(hw, B0_IMSK, Y2_IS_BASE); - - setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); - sky2_idle_start(hw); + hw->intr_mask = Y2_IS_BASE; + sky2_write32(hw, B0_IMSK, hw->intr_mask); pci_set_drvdata(pdev, hw); return 0; err_out_unregister: - pci_disable_msi(pdev); if (dev1) { unregister_netdev(dev1); free_netdev(dev1); @@ -3410,24 +3315,19 @@ static void __devexit sky2_remove(struct pci_dev *pdev) if (!hw) return; - del_timer_sync(&hw->idle_timer); - - sky2_write32(hw, B0_IMSK, 0); - synchronize_irq(hw->pdev->irq); - dev0 = hw->dev[0]; dev1 = hw->dev[1]; if (dev1) unregister_netdev(dev1); unregister_netdev(dev0); + sky2_write32(hw, B0_IMSK, 0); sky2_set_power_state(hw, PCI_D3hot); sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); sky2_write8(hw, B0_CTST, CS_RST_SET); sky2_read8(hw, B0_CTST); free_irq(pdev->irq, hw); - pci_disable_msi(pdev); pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); pci_release_regions(pdev); pci_disable_device(pdev); @@ -3446,14 +3346,8 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) { struct sky2_hw *hw = pci_get_drvdata(pdev); int i; - pci_power_t pstate = pci_choose_state(pdev, state); - - if (!(pstate == PCI_D3hot || pstate == PCI_D3cold)) - return -EINVAL; - del_timer_sync(&hw->idle_timer); - - for (i = 0; i < hw->ports; i++) { + for (i = 0; i < 2; i++) { struct net_device *dev = hw->dev[i]; if (dev) { @@ -3462,14 +3356,10 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) sky2_down(dev); netif_device_detach(dev); - netif_poll_disable(dev); } } - sky2_write32(hw, B0_IMSK, 0); - pci_save_state(pdev); - sky2_set_power_state(hw, pstate); - return 0; + return sky2_set_power_state(hw, pci_choose_state(pdev, state)); } static int sky2_resume(struct pci_dev *pdev) @@ -3479,31 +3369,27 @@ static int sky2_resume(struct pci_dev *pdev) pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D0, 0); - sky2_set_power_state(hw, PCI_D0); + err = sky2_set_power_state(hw, PCI_D0); + if (err) + goto out; err = sky2_reset(hw); if (err) goto out; - sky2_write32(hw, B0_IMSK, Y2_IS_BASE); - - for (i = 0; i < hw->ports; i++) { + for (i = 0; i < 2; i++) { struct net_device *dev = hw->dev[i]; if (dev && netif_running(dev)) { netif_device_attach(dev); - netif_poll_enable(dev); - err = sky2_up(dev); if (err) { printk(KERN_ERR PFX "%s: could not up: %d\n", dev->name, err); dev_close(dev); - goto out; + break; } } } - - sky2_idle_start(hw); out: return err; }