From cd28ab6a4e50a7601d22752aa7ce0c8197b10bdf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 16 Aug 2005 16:36:49 -0700 Subject: [PATCH] sky2: new experimental Marvell Yukon2 driver New driver for the Marvell Yukon2 Gigabit Ethernet chipset. This driver is based on the skge driver, but using the logic from the SysKonnect version of the sk98lin driver. It should support all the Yukon2 chipsets that are available in many current Intel and AMD motherboards. The driver does support ethtool, tx and rx checksum, and tcp segmentation offload. But it has only been tested for a short while and is known to stop receiving under heavy load. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2686 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2686 insertions(+) create mode 100644 drivers/net/sky2.c (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c new file mode 100644 index 00000000000..cfb2b41bc6b --- /dev/null +++ b/drivers/net/sky2.c @@ -0,0 +1,2686 @@ +/* + * New driver for Marvell Yukon 2 chipset. + * Based on earlier sk98lin, and skge driver. + * + * This driver intentionally does not support all the features + * of the original driver such as link fail-over and link management because + * those should be done at higher levels. + * + * Copyright (C) 2005 Stephen Hemminger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * TODO + * - coalescing setting? + * - variable ring size? + * + * TOTEST + * - speed setting + * - power management + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sky2.h" + +#define DRV_NAME "sky2" +#define DRV_VERSION "0.2" +#define PFX DRV_NAME " " + +/* + * The Yukon II chipset takes 64 bit command blocks (called list elements) + * that are organized into three (receive, transmit, status) different rings + * similar to Tigon3. A transmit can require several elements; + * a receive requires one (or two if using 64 bit dma). + */ + +#ifdef CONFIG_SKY2_EC_A1 +#define is_ec_a1(hw) \ + ((hw)->chip_id == CHIP_ID_YUKON_EC && \ + (hw)->chip_rev == CHIP_REV_YU_EC_A1) +#else +#define is_ec_a1(hw) 0 +#endif + +#define RX_LE_SIZE 256 +#define MIN_RX_BUFFERS 8 +#define MAX_RX_BUFFERS 124 +#define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) + +#define TX_RING_SIZE 256 // min 64 max 4096 +#define STATUS_RING_SIZE 1024 // pow2 > (2*Rx + Tx) +#define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le)) +#define ETH_JUMBO_MTU 9000 +#define TX_WATCHDOG (5 * HZ) +#define NAPI_WEIGHT 64 +#define PHY_RETRIES 1000 + +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 + | NETIF_MSG_IFUP| NETIF_MSG_IFDOWN; + +static int debug = -1; /* defaults above */ +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +static const struct pci_device_id sky2_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, + { 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) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4343) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4344) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4345) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4346) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4347) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, + { 0 } +}; +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 inline const char *chip_name(u8 chip_id) +{ + switch (chip_id) { + case CHIP_ID_GENESIS: + return "Genesis"; + case CHIP_ID_YUKON: + return "Yukon"; + case CHIP_ID_YUKON_LITE: + return "Yukon-Lite"; + case CHIP_ID_YUKON_LP: + return "Yukon-LP"; + case CHIP_ID_YUKON_XL: + return "Yukon-XL"; + case CHIP_ID_YUKON_EC: + return "Yukon-EC"; + case CHIP_ID_YUKON_FE: + return "Yukon-FE"; + default: + return "???"; + } +} + +static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) +{ + int i; + + gma_write16(hw, port, GM_SMI_DATA, val); + gma_write16(hw, port, GM_SMI_CTRL, + GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg)); + + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + + if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY)) + break; + } +} + +static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) +{ + int i; + + gma_write16(hw, port, GM_SMI_CTRL, + GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) + | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD); + + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL) + goto ready; + } + + printk(KERN_WARNING PFX "%s: phy read timeout\n", + hw->dev[port]->name); + ready: + return gma_read16(hw, port, GM_SMI_DATA); +} + +static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) +{ + u16 reg; + + /* disable all GMAC IRQ's */ + sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0); + /* disable PHY IRQs */ + gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0); + gma_write16(hw, port, GM_MC_ADDR_H1, 0); /* clear MC hash */ + gma_write16(hw, port, GM_MC_ADDR_H2, 0); + gma_write16(hw, port, GM_MC_ADDR_H3, 0); + gma_write16(hw, port, GM_MC_ADDR_H4, 0); + + reg = gma_read16(hw, port, GM_RX_CTRL); + reg |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA; + gma_write16(hw, port, GM_RX_CTRL, reg); +} + +static void sky2_phy_init(struct sky2_hw *hw, unsigned port) +{ + struct sky2_port *sky2 = netdev_priv(hw->dev[port]); + u16 ctrl, ct1000, adv; + u16 ledctrl, ledover; + + pr_debug("phy reset autoneg=%s advertising=0x%x pause rx=%s tx=%s\n", + sky2->autoneg == AUTONEG_ENABLE ? "enable" : "disable", + sky2->advertising, + sky2->rx_pause ? "on" : "off", + sky2->tx_pause ? "on" : "off"); + + 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 | + PHY_M_EC_MAC_S_MSK); + ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ); + + if (hw->chip_id == CHIP_ID_YUKON_EC) + ectrl |= PHY_M_EC_DSC_2(2) | PHY_M_EC_DOWN_S_ENA; + else + ectrl |= PHY_M_EC_M_DSC(2) | PHY_M_EC_S_DSC(3); + + gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl); + } + + ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); + if (hw->copper) { + if (hw->chip_id == CHIP_ID_YUKON_FE) { + /* enable automatic crossover */ + ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1; + } else { + /* disable energy detect */ + ctrl &= ~PHY_M_PC_EN_DET_MSK; + + /* enable automatic crossover */ + ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); + + if (sky2->autoneg == AUTONEG_ENABLE && + 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; + } + } + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); + } else { + /* workaround for deviation #4.88 (CRC errors) */ + /* disable Automatic Crossover */ + + ctrl &= ~PHY_M_PC_MDIX_MSK; + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); + + if (hw->chip_id == CHIP_ID_YUKON_XL) { + /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2); + ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); + ctrl &= ~PHY_M_MAC_MD_MSK; + ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl); + + /* select page 1 to access Fiber registers */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1); + } + + ctrl &= ~(PHY_M_PC_MDIX_MSK | PHY_M_MAC_MD_MSK); + ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX); + } + + ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL); + if (sky2->autoneg == AUTONEG_DISABLE) + ctrl &= ~PHY_CT_ANE; + else + ctrl |= PHY_CT_ANE; + + ctrl |= PHY_CT_RESET; + gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl); + + ctrl = 0; + ct1000 = 0; + adv = PHY_AN_CSMA; + + if (sky2->autoneg == AUTONEG_ENABLE) { + if (hw->copper) { + if (sky2->advertising & ADVERTISED_1000baseT_Full) + ct1000 |= PHY_M_1000C_AFD; + if (sky2->advertising & ADVERTISED_1000baseT_Half) + ct1000 |= PHY_M_1000C_AHD; + if (sky2->advertising & ADVERTISED_100baseT_Full) + adv |= PHY_M_AN_100_FD; + if (sky2->advertising & ADVERTISED_100baseT_Half) + adv |= PHY_M_AN_100_HD; + if (sky2->advertising & ADVERTISED_10baseT_Full) + adv |= PHY_M_AN_10_FD; + if (sky2->advertising & ADVERTISED_10baseT_Half) + adv |= PHY_M_AN_10_HD; + } else /* special defines for FIBER (88E1011S only) */ + adv |= PHY_M_AN_1000X_AHD | PHY_M_AN_1000X_AFD; + + /* Set Flow-control capabilities */ + if (sky2->tx_pause && sky2->rx_pause) + adv |= PHY_AN_PAUSE_CAP; /* symmetric */ + else if (sky2->rx_pause && !sky2->tx_pause) + adv |= PHY_AN_PAUSE_ASYM|PHY_AN_PAUSE_CAP; + else if (!sky2->rx_pause && sky2->tx_pause) + adv |= PHY_AN_PAUSE_ASYM; /* local */ + + /* Restart Auto-negotiation */ + ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG; + } else { + /* forced speed/duplex settings */ + ct1000 = PHY_M_1000C_MSE; + + if (sky2->duplex == DUPLEX_FULL) + ctrl |= PHY_CT_DUP_MD; + + switch (sky2->speed) { + case SPEED_1000: + ctrl |= PHY_CT_SP1000; + break; + case SPEED_100: + ctrl |= PHY_CT_SP100; + break; + } + + ctrl |= PHY_CT_RESET; + } + + if (hw->chip_id != CHIP_ID_YUKON_FE) + gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000); + + gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv); + gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl); + + /* Setup Phy LED's */ + ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS); + ledover = 0; + + switch (hw->chip_id) { + case CHIP_ID_YUKON_FE: + /* on 88E3082 these bits are at 11..9 (shifted left) */ + ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) << 1; + + ctrl = gm_phy_read(hw, port, PHY_MARV_FE_LED_PAR); + + /* delete ACT LED control bits */ + ctrl &= ~PHY_M_FELP_LED1_MSK; + /* change ACT LED control to blink mode */ + ctrl |= PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_ACT_BL); + gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl); + break; + + case CHIP_ID_YUKON_XL: + ctrl = 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(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, + (PHY_M_POLC_LS1_P_MIX(4) | PHY_M_POLC_IS0_P_MIX(4) | + PHY_M_POLC_LOS_CTRL(2) | PHY_M_POLC_INIT_CTRL(2) | + PHY_M_POLC_STA1_CTRL(2) | PHY_M_POLC_STA0_CTRL(2))); + + /* restore page register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, ctrl); + break; + + default: + /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */ + ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL; + /* turn off the Rx LED (LED_RX) */ + ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); + } + + gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); + + if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) { + /* turn on 100 Mbps LED (LED_LINK100) */ + ledover |= PHY_M_LED_MO_100(MO_LED_ON); + } + + if (ledover) + gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + + /* Enable phy interrupt on autonegotiation complete (or link up) */ + if (sky2->autoneg == AUTONEG_ENABLE) + gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL); + else + gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); +} + +static void sky2_mac_init(struct sky2_hw *hw, unsigned port) +{ + struct sky2_port *sky2 = netdev_priv(hw->dev[port]); + u16 reg; + int i; + const u8 *addr = hw->dev[port]->dev_addr; + + sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); + sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); + + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); + + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 + && port == 1) { + /* WA DEV_472 -- looks like crossed wires on port 2 */ + /* clear GMAC 1 Control reset */ + sky2_write8(hw, SK_REG(0, GMAC_CTRL), GMC_RST_CLR); + do { + sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_SET); + sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_CLR); + } while (gm_phy_read(hw, 1, PHY_MARV_ID0) != PHY_MARV_ID0_VAL || + gm_phy_read(hw, 1, PHY_MARV_ID1) != PHY_MARV_ID1_Y2 || + gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0); + } + + + if (sky2->autoneg == AUTONEG_DISABLE) { + reg = gma_read16(hw, port, GM_GP_CTRL); + reg |= GM_GPCR_AU_ALL_DIS; + gma_write16(hw, port, GM_GP_CTRL, reg); + gma_read16(hw, port, GM_GP_CTRL); + + + switch (sky2->speed) { + case SPEED_1000: + reg |= GM_GPCR_SPEED_1000; + /* fallthru */ + case SPEED_100: + reg |= GM_GPCR_SPEED_100; + } + + if (sky2->duplex == DUPLEX_FULL) + reg |= GM_GPCR_DUP_FULL; + } else + reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; + + if (!sky2->tx_pause && !sky2->rx_pause) { + sky2_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); + reg |= GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; + } else if (sky2->tx_pause &&!sky2->rx_pause) { + /* disable Rx flow-control */ + reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; + } + + gma_write16(hw, port, GM_GP_CTRL, reg); + + sky2_read16(hw, GMAC_IRQ_SRC); + + spin_lock_bh(&hw->phy_lock); + sky2_phy_init(hw, port); + spin_unlock_bh(&hw->phy_lock); + + /* MIB clear */ + reg = gma_read16(hw, port, GM_PHY_ADDR); + gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR); + + for (i = 0; i < GM_MIB_CNT_SIZE; i++) + gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i); + gma_write16(hw, port, GM_PHY_ADDR, reg); + + /* transmit control */ + gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF)); + + /* receive control reg: unicast + multicast + no FCS */ + gma_write16(hw, port, GM_RX_CTRL, + GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA); + + /* transmit flow control */ + gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff); + + /* transmit parameter */ + gma_write16(hw, port, GM_TX_PARAM, + TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) | + TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) | + TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) | + TX_BACK_OFF_LIM(TX_BOF_LIM_DEF)); + + /* serial mode register */ + reg = DATA_BLIND_VAL(DATA_BLIND_DEF) | + GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); + + if (hw->dev[port]->mtu > 1500) + reg |= GM_SMOD_JUMBO_ENA; + + gma_write16(hw, port, GM_SERIAL_MODE, reg); + + /* physical address: used for pause frames */ + gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr); + /* virtual address for data */ + gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr); + + /* enable interrupt mask for counter overflows */ + gma_write16(hw, port, GM_TX_IRQ_MSK, 0); + gma_write16(hw, port, GM_RX_IRQ_MSK, 0); + gma_write16(hw, port, GM_TR_IRQ_MSK, 0); + + /* Configure Rx MAC FIFO */ + sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); + sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), + GMF_OPER_ON | GMF_RX_F_FL_ON); + + reg = RX_FF_FL_DEF_MSK; + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev <= 1) + reg = 0; /* WA Dev #4115 */ + + sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), reg); + /* Set threshold to 0xa (64 bytes) + * ASF disabled so no need to do WA dev #4.30 + */ + sky2_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF); + + /* Configure Tx MAC FIFO */ + sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR); + sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON); + + /* Turn off Rx fifo flush (per sk98lin) */ + sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_F_FL_OFF); +} + +static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) +{ + u32 end; + + start /= 8; + len /= 8; + end = start + len - 1; + pr_debug("ramset q=%d start=0x%x end=0x%x\n", q, start, end); + + sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR); + sky2_write32(hw, RB_ADDR(q, RB_START), start); + sky2_write32(hw, RB_ADDR(q, RB_END), end); + sky2_write32(hw, RB_ADDR(q, RB_WP), start); + sky2_write32(hw, RB_ADDR(q, RB_RP), start); + + if (q == Q_R1 || q == Q_R2) { + /* Set thresholds on receive queue's */ + sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), + start + (2*len)/3); + sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), + start + (len/3)); + } else { + /* Enable store & forward on Tx queue's because + * Tx FIFO is only 1K on Yukon + */ + sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD); + } + + sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD); +} + + +/* Setup Bus Memory Interface */ +static void sky2_qset(struct sky2_hw *hw, u16 q, u32 wm) +{ + sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_RESET); + sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_OPER_INIT); + sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_FIFO_OP_ON); + sky2_write32(hw, Q_ADDR(q, Q_WM), wm); +} + + +/* Setup prefetch unit registers. This is the interface between + * hardware and driver list elements + */ +static inline void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr, + u64 addr, u32 last) +{ + pr_debug("sky2 prefetch init q=%x addr=%llx last=%x\n", + Y2_QADDR(qaddr, 0), addr, last); + + sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); + sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR); + sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), addr >> 32); + sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), (u32) addr); + sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last); + sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON); +} + + +/* + * This is a workaround code taken from syskonnect sk98lin driver + * to deal with chip bug in the wraparound case. + */ +static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, + u16 idx, u16 *last, u16 size) + +{ + BUG_ON(idx >= size); + + wmb(); + 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; + } +} + +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 = (sky2->rx_put + 1) % RX_LE_SIZE; + return le; +} + +static inline void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map, u16 len) +{ + struct sky2_rx_le *le; + + if (sizeof(map) > sizeof(u32)) { + le = sky2_next_rx(sky2); + le->rx.addr = cpu_to_le32((u64) map >> 32); + le->ctrl = 0; + le->opcode = OP_ADDR64 | HW_OWNER; + } + + le = sky2_next_rx(sky2); + le->rx.addr = cpu_to_le32((u32) map); + le->length = cpu_to_le16(len); + le->ctrl = 0; + le->opcode = OP_PACKET | HW_OWNER; +} + +/* Tell chip where to start receive checksum. + * Actually has two checksums, but set both same to avoid possible byte + * order problems. + */ +static void sky2_rx_set_offset(struct sky2_port *sky2) +{ + struct sky2_rx_le *le; + + sky2_write32(sky2->hw, + Q_ADDR(rxqaddr[sky2->port], Q_CSR), + sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); + + le = sky2_next_rx(sky2); + le->rx.csum.start1 = ETH_HLEN; + le->rx.csum.start2 = ETH_HLEN; + le->ctrl = 0; + le->opcode = OP_TCPSTART | HW_OWNER; + wmb(); + sky2_write16(sky2->hw, + Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX), + sky2->rx_put); + +} + +/* Cleanout receive buffer area, assumes receiver hardware stopped */ +static void sky2_rx_clean(struct sky2_port *sky2) +{ + unsigned i; + + memset(sky2->rx_le, 0, RX_LE_BYTES); + for (i = 0; i < sky2->rx_ring_size; i++) { + struct ring_info *re = sky2->rx_ring + i; + + if (re->skb) { + pci_unmap_single(sky2->hw->pdev, + pci_unmap_addr(re, mapaddr), + pci_unmap_len(re, maplen), + PCI_DMA_FROMDEVICE); + kfree_skb(re->skb); + re->skb = NULL; + } + } +} + +static inline struct sk_buff *sky2_rx_alloc_skb(struct sky2_port *sky2, + unsigned int size, int gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(size, gfp_mask); + if (likely(skb)) { + skb->dev = sky2->netdev; + skb_reserve(skb, NET_IP_ALIGN); + } + return skb; +} + +/* + * Allocate and setup receiver buffer pool. + * In case of 64 bit dma, there are 2X as many list elements + * available as ring entries + * and need to reserve one list element so we don't wrap around. + */ +static int sky2_rx_fill(struct sky2_port *sky2) +{ + unsigned i; + unsigned int rx_buf_size = sky2->netdev->mtu + ETH_HLEN + 8; + + pr_debug("sky2_rx_fill %d\n", sky2->rx_ring_size); + for (i = 0; i < sky2->rx_ring_size; i++) { + struct ring_info *re = sky2->rx_ring + i; + dma_addr_t paddr; + + re->skb = sky2_rx_alloc_skb(sky2, rx_buf_size, GFP_KERNEL); + if (!re->skb) + goto nomem; + + paddr = pci_map_single(sky2->hw->pdev, re->skb->data, + rx_buf_size, PCI_DMA_FROMDEVICE); + + pci_unmap_len_set(re, maplen, rx_buf_size); + pci_unmap_addr_set(re, mapaddr, paddr); + sky2_rx_add(sky2, paddr, rx_buf_size); + } + + sky2_write16(sky2->hw, + Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX), + sky2->rx_put); + + return 0; +nomem: + sky2_rx_clean(sky2); + return -ENOMEM; +} + +/* Bring up network interface. */ +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; + int err = -ENOMEM; + + if (netif_msg_ifup(sky2)) + printk(KERN_INFO PFX "%s: enabling interface\n", dev->name); + + /* must be power of 2 */ + sky2->tx_le = pci_alloc_consistent(hw->pdev, + TX_RING_SIZE * sizeof(struct sky2_tx_le), + &sky2->tx_le_map); + if (!sky2->tx_le) + goto err_out; + + sky2->tx_ring = kmalloc(TX_RING_SIZE * sizeof(struct ring_info), + GFP_KERNEL); + if (!sky2->tx_ring) + goto err_out; + sky2->tx_prod = sky2->tx_cons = 0; + memset(sky2->tx_ring, 0, TX_RING_SIZE * sizeof(struct ring_info)); + + sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES, + &sky2->rx_le_map); + if (!sky2->rx_le) + goto err_out; + memset(sky2->rx_le, 0, RX_LE_BYTES); + + sky2->rx_ring = kmalloc(sky2->rx_ring_size * sizeof(struct ring_info), + GFP_KERNEL); + if (!sky2->rx_ring) + goto err_out; + + sky2_mac_init(hw, port); + + /* Configure RAM buffers */ + if (hw->chip_id == CHIP_ID_YUKON_FE || + (hw->chip_id == CHIP_ID_YUKON_EC && hw->chip_rev == 2)) + ramsize = 4096; + else { + u8 e0 = sky2_read8(hw, B2_E_0); + ramsize = (e0 == 0) ? (128*1024) : (e0 * 4096); + } + + /* 2/3 for Rx */ + rxspace = (2 * ramsize) / 3; + sky2_ramset(hw, rxqaddr[port], 0, rxspace); + sky2_ramset(hw, txqaddr[port], rxspace, ramsize - rxspace); + + sky2_qset(hw, rxqaddr[port], is_pciex(hw) ? 0x80 : 0x600); + sky2_qset(hw, txqaddr[port], 0x600); + + sky2->rx_put = sky2->rx_next = 0; + sky2_prefetch_init(hw, rxqaddr[port], sky2->rx_le_map, RX_LE_SIZE-1); + + sky2_rx_set_offset(sky2); + + err = sky2_rx_fill(sky2); + if (err) + goto err_out; + + sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, + TX_RING_SIZE - 1); + + /* Enable interrupts from phy/mac for port */ + hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + return 0; + +err_out: + if (sky2->rx_le) + pci_free_consistent(hw->pdev, RX_LE_BYTES, + sky2->rx_le, sky2->rx_le_map); + if (sky2->tx_le) + pci_free_consistent(hw->pdev, + TX_RING_SIZE * sizeof(struct sky2_tx_le), + sky2->tx_le, sky2->tx_le_map); + if (sky2->tx_ring) + kfree(sky2->tx_ring); + if (sky2->rx_ring) + kfree(sky2->rx_ring); + + return err; +} + +/* + * Worst case number of list elements is 36 + * TSO + CHKSUM + ADDR64 + BUFFER + (ADDR+BUFFER)*MAXFRAGS + */ +#define MAX_SKB_TX_LE (4 + 2*MAX_SKB_FRAGS) + +static inline int sky2_xmit_avail(const struct sky2_port *sky2) +{ + return (sky2->tx_cons > sky2->tx_prod ? 0 : TX_RING_SIZE) + + sky2->tx_cons - sky2->tx_prod - 1; +} + +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 = (sky2->tx_prod + 1) % TX_RING_SIZE; + return le; +} + +/* Put one frame in ring for transmit. */ +static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + struct sky2_tx_le *le; + struct ring_info *re; + unsigned i, len; + dma_addr_t mapping; + u32 addr64; + u16 mss; + u8 ctrl; + + skb = skb_padto(skb, ETH_ZLEN); + if (!skb) + return NETDEV_TX_OK; + + if (!spin_trylock(&sky2->tx_lock)) + return NETDEV_TX_LOCKED; + + if (unlikely(sky2_xmit_avail(sky2) < MAX_SKB_TX_LE)) { + netif_stop_queue(dev); + spin_unlock(&sky2->tx_lock); + + printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (netif_msg_tx_queued(sky2)) + printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n", + dev->name, sky2->tx_prod, skb->len); + + + len = skb_headlen(skb); + mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + + /* Check for TCP Segmentation Offload */ + mss = skb_shinfo(skb)->tso_size; + if (mss) { + /* 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); + return NETDEV_TX_OK; + } + + mss += ((skb->h.th->doff - 5) * 4); /* TCP options */ + mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + mss += ETH_HLEN; + + le = get_tx_le(sky2); + le->tx.tso.size = cpu_to_le16(mss); + le->ctrl = 0; + le->opcode = OP_LRGLEN | HW_OWNER; + } + + /* Handle Hi DMA */ + if (sizeof(mapping) > sizeof(u32)) { + addr64 = (u64)mapping >> 32; + + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32(addr64); + le->ctrl = 0; + le->opcode = OP_ADDR64 | HW_OWNER; + } + + /* Handle TCP checksum offload */ + ctrl = 0; + if (skb->ip_summed == CHECKSUM_HW) { + ptrdiff_t hdr = skb->h.raw - skb->data; + + ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM; + if (skb->nh.iph->protocol == IPPROTO_UDP) + ctrl |= UDPTCP; + + le = get_tx_le(sky2); + le->tx.csum.start = cpu_to_le16(hdr); + le->tx.csum.offset = cpu_to_le16(hdr + skb->csum); + le->length = 0; + le->ctrl = 1; /* one packet */ + le->opcode = OP_TCPLISW|HW_OWNER; + } + + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32((u32) mapping); + le->length = cpu_to_le16(len); + le->ctrl = ctrl; + le->opcode = (mss ? OP_LARGESEND : OP_PACKET) |HW_OWNER; + + re = &sky2->tx_ring[le - sky2->tx_le]; + re->skb = skb; + pci_unmap_addr_set(re, mapaddr, mapping); + pci_unmap_len_set(re, maplen, len); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + + if (sizeof(mapping) > sizeof(u32)) { + u32 hi = (u64) mapping >> 32; + if (hi != addr64) { + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32(hi); + le->ctrl = 0; + le->opcode = OP_ADDR64|HW_OWNER; + addr64 = hi; + } + } + + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32((u32) mapping); + le->length = cpu_to_le16(frag->size); + le->ctrl = ctrl; + le->opcode = OP_BUFFER|HW_OWNER; + + re = &sky2->tx_ring[le - sky2->tx_le]; + pci_unmap_addr_set(re, mapaddr, mapping); + pci_unmap_len_set(re, maplen, frag->size); + } + + le->ctrl |= EOP; + + sky2_put_idx(sky2->hw, txqaddr[sky2->port], sky2->tx_prod, + &sky2->tx_last_put, TX_RING_SIZE); + + if (sky2_xmit_avail(sky2) < MAX_SKB_TX_LE) { + pr_debug("%s: transmit queue full\n", dev->name); + netif_stop_queue(dev); + } + spin_unlock(&sky2->tx_lock); + + dev->trans_start = jiffies; + return NETDEV_TX_OK; +} + + +/* + * Free ring elements from starting at tx_cons until done + * This unwinds the elements based on the usage assigned + * xmit routine. + */ +static void sky2_tx_complete(struct net_device *dev, u16 done) +{ + struct sky2_port *sky2 = netdev_priv(dev); + unsigned idx = sky2->tx_cons; + struct sk_buff *skb = NULL; + + BUG_ON(done >= TX_RING_SIZE); + + spin_lock(&sky2->tx_lock); + while (idx != done) { + struct ring_info *re = sky2->tx_ring + idx; + struct sky2_tx_le *le = sky2->tx_le + idx; + + BUG_ON(le->opcode == 0); + + switch(le->opcode & ~HW_OWNER) { + case OP_LARGESEND: + case OP_PACKET: + if (skb) + dev_kfree_skb_any(skb); + skb = re->skb; + BUG_ON(!skb); + re->skb = NULL; + + pci_unmap_single(sky2->hw->pdev, + pci_unmap_addr(re, mapaddr), + pci_unmap_len(re, maplen), + PCI_DMA_TODEVICE); + break; + + case OP_BUFFER: + pci_unmap_page(sky2->hw->pdev, + pci_unmap_addr(re, mapaddr), + pci_unmap_len(re, maplen), + PCI_DMA_TODEVICE); + break; + } + + le->opcode = 0; + idx = (idx + 1) % TX_RING_SIZE; + } + + if (skb) + dev_kfree_skb_any(skb); + sky2->tx_cons = idx; + + if (sky2_xmit_avail(sky2) > MAX_SKB_TX_LE) + netif_wake_queue(dev); + spin_unlock(&sky2->tx_lock); +} + +/* Cleanup all untransmitted buffers, assume transmitter not running */ +static inline void sky2_tx_clean(struct sky2_port *sky2) +{ + sky2_tx_complete(sky2->netdev, sky2->tx_prod); +} + +/* Network shutdown */ +static int sky2_down(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 ctrl; + int i; + + if (netif_msg_ifdown(sky2)) + printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); + + netif_stop_queue(dev); + + /* Stop transmitter */ + sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP); + sky2_read32(hw, Q_ADDR(txqaddr[port], Q_CSR)); + + sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), + RB_RST_SET|RB_DIS_OP_MD); + + ctrl = gma_read16(hw, port, GM_GP_CTRL); + ctrl &= ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA); + gma_write16(hw, port, GM_GP_CTRL, ctrl); + + sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); + + /* Workaround shared GMAC reset */ + if (! (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 + && port == 0 && hw->dev[1] && netif_running(hw->dev[1]))) + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET); + + /* Disable Force Sync bit and Enable Alloc bit */ + sky2_write8(hw, SK_REG(port, TXA_CTRL), + TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC); + + /* Stop Interval Timer and Limit Counter of Tx Arbiter */ + sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L); + sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L); + + /* Reset the PCI FIFO of the async Tx queue */ + sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_RST_SET | BMU_FIFO_RST); + + /* Reset the Tx prefetch units */ + sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL), + PREF_UNIT_RST_SET); + + sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET); + + /* + * The RX Stop command will not work for Yukon-2 if the BMU does not + * reach the end of packet and since we can't make sure that we have + * incoming data, we must reset the BMU while it is not doing a DMA + * transfer. Since it is possible that the RX path is still active, + * the RX RAM buffer will be stopped first, so any possible incoming + * data will not trigger a DMA. After the RAM buffer is stopped, the + * BMU is polled until any DMA in progress is ended and only then it + * will be reset. + */ + + /* disable the RAM Buffer receive queue */ + sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_DIS_OP_MD); + + for (i = 0; i < 0xffff; i++) + if (sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RSL)) + == sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RL))) + break; + + sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), + BMU_RST_SET | BMU_FIFO_RST); + /* reset the Rx prefetch unit */ + sky2_write32(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_CTRL), + PREF_UNIT_RST_SET); + + 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); + + /* turn off led's */ + sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); + + sky2_tx_clean(sky2); + sky2_rx_clean(sky2); + + pci_free_consistent(hw->pdev, RX_LE_BYTES, + sky2->rx_le, sky2->rx_le_map); + kfree(sky2->rx_ring); + + pci_free_consistent(hw->pdev, + TX_RING_SIZE * sizeof(struct sky2_tx_le), + sky2->tx_le, sky2->tx_le_map); + kfree(sky2->tx_ring); + + return 0; +} + +static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux) +{ + if (hw->chip_id == CHIP_ID_YUKON_FE) + return (aux & PHY_M_PS_SPEED_100) ? SPEED_100 : SPEED_10; + + switch (aux & PHY_M_PS_SPEED_MSK) { + case PHY_M_PS_SPEED_1000: + return SPEED_1000; + case PHY_M_PS_SPEED_100: + return SPEED_100; + default: + return SPEED_10; + } +} + +static void sky2_link_up(struct sky2_port *sky2) +{ + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 reg; + + /* Enable Transmit FIFO Underrun */ + sky2_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK); + + reg = gma_read16(hw, port, GM_GP_CTRL); + if (sky2->duplex == DUPLEX_FULL || sky2->autoneg == AUTONEG_ENABLE) + reg |= GM_GPCR_DUP_FULL; + + + /* enable Rx/Tx */ + reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA; + gma_write16(hw, port, GM_GP_CTRL, reg); + gma_read16(hw, port, GM_GP_CTRL); + + gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); + + netif_carrier_on(sky2->netdev); + netif_wake_queue(sky2->netdev); + + /* Turn on link LED */ + sky2_write8(hw, SK_REG(port, LNK_LED_REG), + LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF); + + if (netif_msg_link(sky2)) + printk(KERN_INFO PFX + "%s: Link is up at %d Mbps, %s duplex, flowcontrol %s\n", + sky2->netdev->name, sky2->speed, + sky2->duplex == DUPLEX_FULL ? "full" : "half", + (sky2->tx_pause && sky2->rx_pause) ? "both" : + sky2->tx_pause ? "tx" : + sky2->rx_pause ? "rx" : "none"); +} + +static void sky2_link_down(struct sky2_port *sky2) +{ + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 reg; + + gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0); + + reg = gma_read16(hw, port, GM_GP_CTRL); + reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); + gma_write16(hw, port, GM_GP_CTRL, reg); + gma_read16(hw, port, GM_GP_CTRL); /* PCI post */ + + if (sky2->rx_pause && !sky2->tx_pause) { + /* restore Asymmetric Pause bit */ + gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, + gm_phy_read(hw, port, + PHY_MARV_AUNE_ADV) + | PHY_M_AN_ASP); + } + + sky2_phy_reset(hw, port); + + netif_carrier_off(sky2->netdev); + netif_stop_queue(sky2->netdev); + + /* Turn on link LED */ + sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF); + + if (netif_msg_link(sky2)) + printk(KERN_INFO PFX "%s: Link is down.\n", sky2->netdev->name); + sky2_phy_init(hw, port); +} + + +/* + * Interrrupt from PHY are handled in tasklet (soft irq) + * because accessing phy registers requires spin wait which might + * cause excess interrupt latency. + */ +static void sky2_phy_task(unsigned long data) +{ + struct sky2_port *sky2 = (struct sky2_port *) data; + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 istatus, phystat; + + istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT); + + phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT); + + if (netif_msg_intr(sky2)) + printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n", + sky2->netdev->name, istatus, phystat); + + if (istatus & PHY_M_IS_AN_COMPL) { + u16 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); + } + else if (hw->chip_id != CHIP_ID_YUKON_FE + && gm_phy_read(hw, port, PHY_MARV_1000T_STAT) + & PHY_B_1000S_MSF) { + printk(KERN_ERR PFX "%s: master/slave fault", + sky2->netdev->name); + } + else if (!(phystat & PHY_M_PS_SPDUP_RES)) { + printk(KERN_ERR PFX "%s: speed/duplex mismatch", + sky2->netdev->name); + } + else { + sky2->duplex = (phystat & PHY_M_PS_FULL_DUP) + ? DUPLEX_FULL : DUPLEX_HALF; + + sky2->speed = sky2_phy_speed(hw, phystat); + + sky2->tx_pause = (phystat & PHY_M_PS_TX_P_EN) != 0; + sky2->rx_pause = (phystat & PHY_M_PS_RX_P_EN) != 0; + + if ((!sky2->tx_pause && !sky2->rx_pause) || + (sky2->speed < SPEED_1000 && sky2->duplex == DUPLEX_HALF)) + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); + else + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); + sky2_link_up(sky2); + } + } else { + + if (istatus & PHY_M_IS_LSP_CHANGE) + sky2->speed = sky2_phy_speed(hw, phystat); + + if (istatus & PHY_M_IS_DUP_CHANGE) + sky2->duplex = (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; + if (istatus & PHY_M_IS_LST_CHANGE) { + if (phystat & PHY_M_PS_LINK_UP) + sky2_link_up(sky2); + else + sky2_link_down(sky2); + } + } + + local_irq_disable(); + hw->intr_mask |= (port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + local_irq_enable(); +} + +static void sky2_tx_timeout(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + if (netif_msg_timer(sky2)) + printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); + + sky2_write32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR), BMU_STOP); + sky2_read32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR)); + + sky2_tx_clean(sky2); +} + +static int sky2_change_mtu(struct net_device *dev, int new_mtu) +{ + int err = 0; + + if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) + return -EINVAL; + + if (netif_running(dev)) + sky2_down(dev); + + dev->mtu = new_mtu; + + if (netif_running(dev)) + err = sky2_up(dev); + + return err; +} + +/* + * Receive one packet. + * For small packets or errors, just reuse existing skb. + * For larger pakects, get new buffer. + */ +static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, + u16 length, u32 status) +{ + struct net_device *dev = hw->dev[port]; + struct sky2_port *sky2 = netdev_priv(dev); + struct ring_info *re = sky2->rx_ring + sky2->rx_next; + struct sk_buff *skb = re->skb; + dma_addr_t mapping; + const unsigned int rx_buf_size = dev->mtu + ETH_HLEN + 8; + + if (unlikely(netif_msg_rx_status(sky2))) + printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", + dev->name, sky2->rx_next, status, length); + + sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_ring_size; + + pci_unmap_single(sky2->hw->pdev, + pci_unmap_addr(re, mapaddr), + pci_unmap_len(re, maplen), + PCI_DMA_FROMDEVICE); + prefetch(skb->data); + + if (!(status & GMR_FS_RX_OK) + || (status & GMR_FS_ANY_ERR) + || (length << 16) != (status & GMR_FS_LEN) + || length > rx_buf_size) + goto error; + + re->skb = sky2_rx_alloc_skb(sky2, rx_buf_size, GFP_ATOMIC); + if (!re->skb) + goto reuse; + +submit: + mapping = pci_map_single(sky2->hw->pdev, re->skb->data, + rx_buf_size, PCI_DMA_FROMDEVICE); + + pci_unmap_len_set(re, maplen, rx_buf_size); + pci_unmap_addr_set(re, mapaddr, mapping); + + sky2_rx_add(sky2, mapping, rx_buf_size); + sky2_put_idx(sky2->hw, rxqaddr[sky2->port], + sky2->rx_put, &sky2->rx_last_put, RX_LE_SIZE); + + return skb; + +error: + if (netif_msg_rx_err(sky2)) + printk(KERN_INFO PFX "%s: rx error, status 0x%x length %d\n", + sky2->netdev->name, status, length); + + if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE)) + sky2->net_stats.rx_length_errors++; + if (status & GMR_FS_FRAGMENT) + sky2->net_stats.rx_frame_errors++; + if (status & GMR_FS_CRC_ERR) + sky2->net_stats.rx_crc_errors++; +reuse: + re->skb = skb; + skb = NULL; + goto submit; +} + +static u16 get_tx_index(u8 port, u32 status, u16 len) +{ + if (port == 0) + return status & 0xfff; + else + return ((status >> 24) & 0xff) | (len & 0xf) << 8; +} + +/* + * NAPI poll routine. + * Both ports share the same status interrupt, therefore there is only + * one poll routine. + * + */ +static int sky2_poll(struct net_device *dev, int *budget) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + unsigned int to_do = min(dev->quota, *budget); + unsigned int work_done = 0; + unsigned char summed[2] = { CHECKSUM_NONE, CHECKSUM_NONE }; + unsigned int csum[2] = { 0 }; + unsigned int rx_handled[2] = { 0, 0}; + u16 last; + + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + last = sky2_read16(hw, STAT_PUT_IDX); + + while (hw->st_idx != last && work_done < to_do) { + struct sky2_status_le *le = hw->st_le + hw->st_idx; + struct sk_buff *skb; + u8 port; + u32 status; + u16 length; + + rmb(); + status = le32_to_cpu(le->status); + length = le16_to_cpu(le->length); + port = le->link; + + BUG_ON(port >= hw->ports); + + switch(le->opcode & ~HW_OWNER) { + case OP_RXSTAT: + ++rx_handled[port]; + skb = sky2_receive(hw, port, length, status); + if (likely(skb)) { + __skb_put(skb, length); + skb->protocol = eth_type_trans(skb, dev); + + /* Add hw checksum if available */ + skb->ip_summed = summed[port]; + skb->csum = csum[port]; + + /* Clear for next packet */ + csum[port] = 0; + summed[port] = CHECKSUM_NONE; + + netif_receive_skb(skb); + + dev->last_rx = jiffies; + ++work_done; + } + break; + + case OP_RXCHKS: + /* Save computed checksum for next rx */ + csum[port] = le16_to_cpu(status & 0xffff); + summed[port] = CHECKSUM_HW; + break; + + case OP_TXINDEXLE: + sky2_tx_complete(hw->dev[port], + get_tx_index(port, status, length)); + break; + + case OP_RXTIMESTAMP: + break; + + default: + if (net_ratelimit()) + printk(KERN_WARNING PFX "unknown status opcode 0x%x\n", + le->opcode); + break; + } + + hw->st_idx = (hw->st_idx + 1) & (STATUS_RING_SIZE -1); + } + + *budget -= work_done; + dev->quota -= work_done; + if (work_done < to_do) { + /* + * Another chip workaround, need to restart TX timer if status + * LE was handled. WA_DEV_43_418 + */ + if (is_ec_a1(hw)) { + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); + } + + hw->intr_mask |= Y2_IS_STAT_BMU; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + netif_rx_complete(dev); + } + + return work_done >= to_do; + +} + +static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status) +{ + struct net_device *dev = hw->dev[port]; + + printk(KERN_INFO PFX "%s: hw error interrupt status 0x%x\n", + dev->name, status); + + if (status & Y2_IS_PAR_RD1) { + printk(KERN_ERR PFX "%s: ram data read parity error\n", + dev->name); + /* Clear IRQ */ + sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_RD_PERR); + } + + if (status & Y2_IS_PAR_WR1) { + printk(KERN_ERR PFX "%s: ram data write parity error\n", + dev->name); + + sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_WR_PERR); + } + + if (status & Y2_IS_PAR_MAC1) { + printk(KERN_ERR PFX "%s: MAC parity error\n", dev->name); + sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_PE); + } + + if (status & Y2_IS_PAR_RX1) { + printk(KERN_ERR PFX "%s: RX parity error\n", dev->name); + sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), BMU_CLR_IRQ_PAR); + } + + if (status & Y2_IS_TCP_TXA1) { + printk(KERN_ERR PFX "%s: TCP segmentation error\n", dev->name); + sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_CLR_IRQ_TCP); + } +} + +static void sky2_hw_intr(struct sky2_hw *hw) +{ + u32 status = sky2_read32(hw, B0_HWE_ISRC); + + if (status & Y2_IS_TIST_OV) { + pr_debug (PFX "%s: unused timer overflow??\n", + pci_name(hw->pdev)); + sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); + } + + if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) { + u16 pci_err = sky2_read16(hw, PCI_C(PCI_STATUS)); + printk(KERN_ERR PFX "%s: pci hw error (0x%x)\n", + pci_name(hw->pdev), pci_err); + + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + sky2_write16(hw, PCI_C(PCI_STATUS), + pci_err | PCI_STATUS_ERROR_BITS); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + } + + if (status & Y2_IS_PCI_EXP) { + /* PCI-Express uncorrectable Error occured */ + u32 pex_err = sky2_read32(hw, PCI_C(PEX_UNC_ERR_STAT)); + + /* + * On PCI-Express bus bridges are called root complexes. + * PCI-Express errors are recognized by the root complex too, + * which requests the system to handle the problem. After error + * occurence it may be that no access to the adapter may be performed + * any longer. + */ + printk(KERN_ERR PFX "%s: pci express error (0x%x)\n", + pci_name(hw->pdev), pex_err); + + /* clear the interrupt */ + sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + sky2_write32(hw, PCI_C(PEX_UNC_ERR_STAT), 0xffffffffUL); + sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + + if (pex_err & PEX_FATAL_ERRORS) { + u32 hwmsk = sky2_read32(hw, B0_HWE_IMSK); + hwmsk &= ~Y2_IS_PCI_EXP; + sky2_write32(hw, B0_HWE_IMSK, hwmsk); + } + } + + if (status & Y2_HWE_L1_MASK) + sky2_hw_error(hw, 0, status); + status >>= 8; + if (status & Y2_HWE_L1_MASK) + sky2_hw_error(hw, 1, status); +} + +static void sky2_mac_intr(struct sky2_hw *hw, unsigned port) +{ + struct net_device *dev = hw->dev[port]; + struct sky2_port *sky2 = netdev_priv(dev); + u8 status = sky2_read8(hw, SK_REG(port, GMAC_IRQ_SRC)); + + if (netif_msg_intr(sky2)) + printk(KERN_INFO PFX "%s: mac interrupt status 0x%x\n", + dev->name, status); + + if (status & GM_IS_RX_FF_OR) { + ++sky2->net_stats.rx_fifo_errors; + sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO); + } + + if (status & GM_IS_TX_FF_UR) { + ++sky2->net_stats.tx_fifo_errors; + sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU); + } + +} + +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); + + hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); + sky2_write32(hw, B0_IMSK, hw->intr_mask); + tasklet_schedule(&sky2->phy_task); +} + +static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sky2_hw *hw = dev_id; + u32 status; + + status = sky2_read32(hw, B0_Y2_SP_ISRC2); + if (status == 0 || status == ~0) /* hotplug or shared irq */ + return IRQ_NONE; + + if (status & Y2_IS_HW_ERR) + sky2_hw_intr(hw); + + if ((status & Y2_IS_STAT_BMU) && netif_rx_schedule_prep(hw->dev[0])) { + hw->intr_mask &= ~Y2_IS_STAT_BMU; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + __netif_rx_schedule(hw->dev[0]); + } + + if (status & Y2_IS_IRQ_PHY1) + sky2_phy_intr(hw, 0); + + if (status & Y2_IS_IRQ_PHY2) + sky2_phy_intr(hw, 1); + + if (status & Y2_IS_IRQ_MAC1) + sky2_mac_intr(hw, 0); + + if (status & Y2_IS_IRQ_MAC2) + sky2_mac_intr(hw, 1); + + + sky2_write32(hw, B0_Y2_SP_ICR, 2); + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sky2_netpoll(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + disable_irq(dev->irq); + sky2_intr(dev->irq, sky2->hw, NULL); + enable_irq(dev->irq); +} +#endif + +/* Chip internal frequency for clock calculations */ +static inline u32 sky2_khz(const struct sky2_hw *hw) +{ + switch(hw->chip_id) { + case CHIP_ID_YUKON_EC: + return 125000; /* 125 Mhz */ + case CHIP_ID_YUKON_FE: + return 100000; /* 100 Mhz */ + default: /* YUKON_XL */ + return 156000; /* 156 Mhz */ + } +} + +static inline u32 sky2_ms2clk(const struct sky2_hw *hw, u32 ms) +{ + return sky2_khz(hw) * ms; +} + +static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) +{ + return (sky2_khz(hw) * 75) / 1000; +} + +static int sky2_reset(struct sky2_hw *hw) +{ + u32 ctst, power; + u16 status; + u8 t8, pmd_type; + int i; + + ctst = sky2_read32(hw, B0_CTST); + + sky2_write8(hw, B0_CTST, CS_RST_CLR); + hw->chip_id = sky2_read8(hw, B2_CHIP_ID); + if (hw->chip_id < CHIP_ID_YUKON_XL || hw->chip_id > CHIP_ID_YUKON_FE) { + printk(KERN_ERR PFX "%s: unsupported chip type 0x%x\n", + pci_name(hw->pdev), hw->chip_id); + return -EOPNOTSUPP; + } + + /* disable ASF */ + if (hw->chip_id <= CHIP_ID_YUKON_EC) { + sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); + sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE); + } + + /* do a SW reset */ + sky2_write8(hw, B0_CTST, CS_RST_SET); + sky2_write8(hw, B0_CTST, CS_RST_CLR); + + /* clear PCI errors, if any */ + status = sky2_read16(hw, PCI_C(PCI_STATUS)); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + sky2_write16(hw, PCI_C(PCI_STATUS), + status | PCI_STATUS_ERROR_BITS); + + sky2_write8(hw, B0_CTST, CS_MRST_CLR); + + /* clear any PEX errors */ + if (is_pciex(hw)) { + sky2_write32(hw, PCI_C(PEX_UNC_ERR_STAT), 0xffffffffUL); + sky2_read16(hw, PCI_C(PEX_LNK_STAT)); + } + + pmd_type = sky2_read8(hw, B2_PMD_TYP); + hw->copper = !(pmd_type == 'L' || pmd_type == 'S'); + + hw->ports = 1; + t8 = sky2_read8(hw, B2_Y2_HW_RES); + if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) { + 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; + + /* switch power to VCC (WA for VAUX problem) */ + sky2_write8(hw, B0_POWER_CTRL, + PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); + + /* disable Core Clock Division, */ + sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); + + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + /* enable bits are inverted */ + sky2_write8(hw, B2_Y2_CLK_GATE, + Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | + Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | + Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); + else + sky2_write8(hw, B2_Y2_CLK_GATE, 0); + + /* Turn off phy power saving */ + power = sky2_read32(hw, PCI_C(PCI_DEV_REG1)); + power &= ~(PCI_Y2_PHY1_POWD|PCI_Y2_PHY2_POWD); + + /* back asswards .. */ + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { + power |= PCI_Y2_PHY1_COMA; + if (hw->ports > 1) + power |= PCI_Y2_PHY2_COMA; + } + sky2_write32(hw, PCI_C(PCI_DEV_REG1), power); + + for (i = 0; i < hw->ports; i++) { + sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); + sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR); + } + + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + + sky2_write32(hw, B2_I2C_IRQ, 1); /* Clear I2C IRQ noise */ + + /* turn off hardware timer (unused) */ + sky2_write8(hw, B2_TI_CTRL, TIM_STOP); + sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ); + + sky2_write8(hw, B0_Y2LED, LED_STAT_ON); + + /* Turn on descriptor polling -- is this necessary? */ + sky2_write32(hw, B28_DPT_INI, sky2_us2clk(hw, 75)); + sky2_write8(hw, B28_DPT_CTRL, DPT_START); + + /* Turn off receive timestamp */ + sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP); + + /* enable the Tx Arbiters */ + for (i = 0; i < hw->ports; i++) + sky2_write8(hw, SK_REG(i, TXA_CTRL), TXA_ENA_ARB); + + /* Initialize ram interface */ + for (i = 0; i < hw->ports; i++) { + sky2_write16(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR); + + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS1), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R2), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA2), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS2), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R2), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA2), SK_RI_TO_53); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53); + } + + /* Optimize PCI Express access */ + if (is_pciex(hw)) { + u16 ctrl = sky2_read32(hw, PCI_C(PEX_DEV_CTRL)); + ctrl &= ~PEX_DC_MAX_RRS_MSK; + ctrl |= PEX_DC_MAX_RD_RQ_SIZE(4); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + sky2_write16(hw, PCI_C(PEX_DEV_CTRL), ctrl); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); + } + + sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); + + hw->intr_mask = Y2_IS_BASE; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + + /* disable all GMAC IRQ's */ + sky2_write8(hw, GMAC_IRQ_MSK, 0); + + spin_lock_bh(&hw->phy_lock); + for (i = 0; i < hw->ports; i++) + sky2_phy_reset(hw, i); + spin_unlock_bh(&hw->phy_lock); + + /* Setup ring for status responses */ + hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES, + &hw->st_dma); + if (!hw->st_le) + return -ENOMEM; + + memset(hw->st_le, 0, STATUS_LE_BYTES); + hw->st_idx = 0; + + sky2_write32(hw, STAT_CTRL, SC_STAT_RST_SET); + sky2_write32(hw, STAT_CTRL, SC_STAT_RST_CLR); + + sky2_write32(hw, STAT_LIST_ADDR_LO, hw->st_dma); + sky2_write32(hw, STAT_LIST_ADDR_HI, (u64)hw->st_dma >> 32); + + /* Set the list last index */ + sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE-1); + + if (is_ec_a1(hw)) { + /* WA for dev. #4.3 */ + sky2_write16(hw, STAT_TX_IDX_TH, ST_TXTH_IDX_MASK); + + /* set Status-FIFO watermark */ + sky2_write8(hw, STAT_FIFO_WM, 0x21); /* WA for dev. #4.18 */ + + /* set Status-FIFO ISR watermark */ + sky2_write8(hw, STAT_FIFO_ISR_WM, 0x07);/* WA for dev. #4.18 */ + + /* WA for dev. #4.3 and #4.18 */ + /* set Status-FIFO Tx timer init value */ + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_ms2clk(hw, 10)); + } else { + /* + * Theses settings should avoid the + * temporary hanging of the status BMU. + * May be not all required... still under investigation... + */ + sky2_write16(hw, STAT_TX_IDX_TH, 0x000a); + + /* set Status-FIFO watermark */ + sky2_write8(hw, STAT_FIFO_WM, 0x10); + + /* set Status-FIFO ISR watermark */ + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0) + sky2_write8(hw, STAT_FIFO_ISR_WM, 0x10); + + else /* WA 4109 */ + sky2_write8(hw, STAT_FIFO_ISR_WM, 0x04); + + sky2_write32(hw, STAT_ISR_TIMER_INI, 0x0190); + } + + /* enable the prefetch unit */ + /* operational bit not functional for Yukon-EC, but fixed in Yukon-2? */ + sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON); + + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START); + sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START); + + return 0; +} + +static inline u32 sky2_supported_modes(const struct sky2_hw *hw) +{ + u32 modes; + if (hw->copper) { + modes = SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_Autoneg| SUPPORTED_TP; + + if (hw->chip_id != CHIP_ID_YUKON_FE) + modes |= SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full; + } else + modes = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE + | SUPPORTED_Autoneg; + return modes; +} + +static int sky2_get_settings(struct net_device *dev, + struct ethtool_cmd *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + + ecmd->transceiver = XCVR_INTERNAL; + ecmd->supported = sky2_supported_modes(hw); + ecmd->phy_address = PHY_ADDR_MARV; + if (hw->copper) { + ecmd->supported = SUPPORTED_10baseT_Half + + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg| SUPPORTED_TP; + ecmd->port = PORT_TP; + } else + ecmd->port = PORT_FIBRE; + + ecmd->advertising = sky2->advertising; + ecmd->autoneg = sky2->autoneg; + ecmd->speed = sky2->speed; + ecmd->duplex = sky2->duplex; + return 0; +} + +static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + const struct sky2_hw *hw = sky2->hw; + u32 supported = sky2_supported_modes(hw); + + if (ecmd->autoneg == AUTONEG_ENABLE) { + ecmd->advertising = supported; + sky2->duplex = -1; + sky2->speed = -1; + } else { + u32 setting; + + switch(ecmd->speed) { + case SPEED_1000: + if (ecmd->duplex == DUPLEX_FULL) + setting = SUPPORTED_1000baseT_Full; + else if (ecmd->duplex == DUPLEX_HALF) + setting = SUPPORTED_1000baseT_Half; + else + return -EINVAL; + break; + case SPEED_100: + if (ecmd->duplex == DUPLEX_FULL) + setting = SUPPORTED_100baseT_Full; + else if (ecmd->duplex == DUPLEX_HALF) + setting = SUPPORTED_100baseT_Half; + else + return -EINVAL; + break; + + case SPEED_10: + if (ecmd->duplex == DUPLEX_FULL) + setting = SUPPORTED_10baseT_Full; + else if (ecmd->duplex == DUPLEX_HALF) + setting = SUPPORTED_10baseT_Half; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + if ((setting & supported) == 0) + return -EINVAL; + + sky2->speed = ecmd->speed; + sky2->duplex = ecmd->duplex; + } + + sky2->autoneg = ecmd->autoneg; + sky2->advertising = ecmd->advertising; + + if (netif_running(dev)) { + sky2_down(dev); + sky2_up(dev); + } + + return 0; +} + +static void sky2_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, pci_name(sky2->hw->pdev)); +} + +static const struct sky2_stat { + char name[ETH_GSTRING_LEN]; + u16 offset; +} sky2_stats[] = { + { "tx_bytes", GM_TXO_OK_HI }, + { "rx_bytes", GM_RXO_OK_HI }, + { "tx_broadcast", GM_TXF_BC_OK }, + { "rx_broadcast", GM_RXF_BC_OK }, + { "tx_multicast", GM_TXF_MC_OK }, + { "rx_multicast", GM_RXF_MC_OK }, + { "tx_unicast", GM_TXF_UC_OK }, + { "rx_unicast", GM_RXF_UC_OK }, + { "tx_mac_pause", GM_TXF_MPAUSE }, + { "rx_mac_pause", GM_RXF_MPAUSE }, + { "collisions", GM_TXF_SNG_COL }, + { "late_collision",GM_TXF_LAT_COL }, + { "aborted", GM_TXF_ABO_COL }, + { "multi_collisions", GM_TXF_MUL_COL }, + { "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_too_long", GM_RXF_LNG_ERR }, + { "rx_fcs_error", GM_RXF_FCS_ERR }, +}; + + +static u32 sky2_get_rx_csum(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + return sky2->rx_csum; +} + +static int sky2_set_rx_csum(struct net_device *dev, u32 data) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + sky2->rx_csum = data; + sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR), + data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); + + return 0; +} + +static u32 sky2_get_msglevel(struct net_device *netdev) +{ + struct sky2_port *sky2 = netdev_priv(netdev); + return sky2->msg_enable; +} + +static void sky2_phy_stats(struct sky2_port *sky2, u64 *data) +{ + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + int i; + + data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32 + | (u64) gma_read32(hw, port, GM_TXO_OK_LO); + data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32 + | (u64) gma_read32(hw, port, GM_RXO_OK_LO); + + for (i = 2; i < ARRAY_SIZE(sky2_stats); i++) + data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset); +} + + +static void sky2_set_msglevel(struct net_device *netdev, u32 value) +{ + struct sky2_port *sky2 = netdev_priv(netdev); + sky2->msg_enable = value; +} + +static int sky2_get_stats_count(struct net_device *dev) +{ + return ARRAY_SIZE(sky2_stats); +} + +static void sky2_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + sky2_phy_stats(sky2, data); +} + +static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(sky2_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + sky2_stats[i].name, ETH_GSTRING_LEN); + break; + } +} + +/* Use hardware MIB variables for critical path statistics and + * transmit feedback not reported at interrupt. + * Other errors are accounted for in interrupt handler. + */ +static struct net_device_stats *sky2_get_stats(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + u64 data[ARRAY_SIZE(sky2_stats)]; + + sky2_phy_stats(sky2, data); + + sky2->net_stats.tx_bytes = data[0]; + 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[5] + data[7]; + sky2->net_stats.collisions = data[10]; + sky2->net_stats.tx_aborted_errors = data[12]; + + return &sky2->net_stats; +} + +static int sky2_set_mac_address(struct net_device *dev, void *p) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sockaddr *addr = p; + int err = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + sky2_down(dev); + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port*8, + dev->dev_addr, ETH_ALEN); + memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port*8, + dev->dev_addr, ETH_ALEN); + if (dev->flags & IFF_UP) + err = sky2_up(dev); + return err; +} + +static void sky2_set_multicast(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + struct dev_mc_list *list = dev->mc_list; + u16 reg; + u8 filter[8]; + + memset(filter, 0, sizeof(filter)); + + reg = gma_read16(hw, port, GM_RX_CTRL); + reg |= GM_RXCR_UCF_ENA; + + if (dev->flags & IFF_PROMISC) /* promiscious */ + reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); + else if (dev->flags & IFF_ALLMULTI) /* all multicast */ + memset(filter, 0xff, sizeof(filter)); + else if (dev->mc_count == 0) /* no multicast */ + reg &= ~GM_RXCR_MCF_ENA; + else { + int i; + reg |= GM_RXCR_MCF_ENA; + + for (i = 0; list && i < dev->mc_count; i++, list = list->next) { + u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f; + filter[bit/8] |= 1 << (bit%8); + } + } + + + gma_write16(hw, port, GM_MC_ADDR_H1, + (u16)filter[0] | ((u16)filter[1] << 8)); + gma_write16(hw, port, GM_MC_ADDR_H2, + (u16)filter[2] | ((u16)filter[3] << 8)); + gma_write16(hw, port, GM_MC_ADDR_H3, + (u16)filter[4] | ((u16)filter[5] << 8)); + gma_write16(hw, port, GM_MC_ADDR_H4, + (u16)filter[6] | ((u16)filter[7] << 8)); + + gma_write16(hw, port, GM_RX_CTRL, reg); +} + +/* Can have one global because blinking is controlled by + * ethtool and that is always under RTNL mutex + */ +static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) +{ + spin_lock_bh(&hw->phy_lock); + gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0); + if (on) + gm_phy_write(hw, port, PHY_MARV_LED_OVER, + PHY_M_LED_MO_DUP(MO_LED_ON) | + PHY_M_LED_MO_10(MO_LED_ON) | + PHY_M_LED_MO_100(MO_LED_ON) | + PHY_M_LED_MO_1000(MO_LED_ON) | + PHY_M_LED_MO_RX(MO_LED_ON)); + else + gm_phy_write(hw, port, PHY_MARV_LED_OVER, + + PHY_M_LED_MO_DUP(MO_LED_OFF) | + PHY_M_LED_MO_10(MO_LED_OFF) | + PHY_M_LED_MO_100(MO_LED_OFF) | + PHY_M_LED_MO_1000(MO_LED_OFF) | + PHY_M_LED_MO_RX(MO_LED_OFF)); + + spin_unlock_bh(&hw->phy_lock); +} + +/* blink LED's for finding board */ +static int sky2_phys_id(struct net_device *dev, u32 data) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 ledctrl, ledover; + long ms; + int onoff = 1; + + if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) + ms = jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT); + else + ms = data * 1000; + + /* save initial values */ + spin_lock_bh(&hw->phy_lock); + ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); + ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); + spin_unlock_bh(&hw->phy_lock); + + while (ms > 0) { + sky2_led(hw, port, onoff); + onoff = !onoff; + + if (msleep_interruptible(250)) + break; /* interrupted */ + ms -= 250; + } + + /* resume regularly scheduled programming */ + spin_lock_bh(&hw->phy_lock); + gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); + gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + spin_unlock_bh(&hw->phy_lock); + + return 0; +} + +static void sky2_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + ecmd->tx_pause = sky2->tx_pause; + ecmd->rx_pause = sky2->rx_pause; + ecmd->autoneg = sky2->autoneg; +} + +static int sky2_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + int err = 0; + + sky2->autoneg = ecmd->autoneg; + sky2->tx_pause = ecmd->tx_pause != 0; + sky2->rx_pause = ecmd->rx_pause != 0; + + if (netif_running(dev)) { + sky2_down(dev); + err = sky2_up(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 struct ethtool_ops sky2_ethtool_ops = { + .get_settings = sky2_get_settings, + .set_settings = sky2_set_settings, + .get_drvinfo = sky2_get_drvinfo, + .get_msglevel = sky2_get_msglevel, + .set_msglevel = sky2_set_msglevel, + .get_link = ethtool_op_get_link, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, + .get_rx_csum = sky2_get_rx_csum, + .set_rx_csum = sky2_set_rx_csum, + .get_strings = sky2_get_strings, + .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, +}; + +/* Initialize network device */ +static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, + unsigned port, int highmem) +{ + struct sky2_port *sky2; + struct net_device *dev = alloc_etherdev(sizeof(*sky2)); + + if (!dev) { + printk(KERN_ERR "sky2 etherdev alloc failed"); + return NULL; + } + + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &hw->pdev->dev); + dev->open = sky2_up; + dev->stop = sky2_down; + dev->hard_start_xmit = sky2_xmit_frame; + dev->get_stats = sky2_get_stats; + dev->set_multicast_list = sky2_set_multicast; + dev->set_mac_address = sky2_set_mac_address; + dev->change_mtu = sky2_change_mtu; + SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops); + dev->tx_timeout = sky2_tx_timeout; + dev->watchdog_timeo = TX_WATCHDOG; + if (port == 0) + dev->poll = sky2_poll; + dev->weight = NAPI_WEIGHT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = sky2_netpoll; +#endif + dev->irq = hw->pdev->irq; + + sky2 = netdev_priv(dev); + sky2->netdev = dev; + sky2->hw = hw; + sky2->msg_enable = netif_msg_init(debug, default_msg); + + spin_lock_init(&sky2->tx_lock); + /* Auto speed and flow control */ + sky2->autoneg = AUTONEG_ENABLE; + sky2->tx_pause = 0; + sky2->rx_pause = 1; + sky2->duplex = -1; + sky2->speed = -1; + sky2->advertising = sky2_supported_modes(hw); + sky2->rx_csum = 1; + sky2->rx_ring_size = is_ec_a1(hw) ? MIN_RX_BUFFERS : MAX_RX_BUFFERS; + tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long) sky2); + + hw->dev[port] = dev; + + sky2->port = port; + + dev->features |= NETIF_F_LLTX; + if (highmem) + dev->features |= NETIF_F_HIGHDMA; + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO; + + /* read the mac address */ + memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port*8, ETH_ALEN); + + /* device is off until link detection */ + netif_carrier_off(dev); + netif_stop_queue(dev); + + return dev; +} + +static inline void sky2_show_addr(struct net_device *dev) +{ + const struct sky2_port *sky2 = netdev_priv(dev); + + if (netif_msg_probe(sky2)) + printk(KERN_INFO PFX "%s: addr %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); +} + +static int __devinit sky2_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev, *dev1; + struct sky2_hw *hw; + int err, using_dac = 0; + + if ((err = pci_enable_device(pdev))) { + printk(KERN_ERR PFX "%s cannot enable PCI device\n", + pci_name(pdev)); + goto err_out; + } + + if ((err = pci_request_regions(pdev, DRV_NAME))) { + printk(KERN_ERR PFX "%s cannot obtain PCI resources\n", + pci_name(pdev)); + goto err_out_disable_pdev; + } + + pci_set_master(pdev); + + if (sizeof(dma_addr_t) > sizeof(u32)) { + err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + if (!err) + using_dac = 1; + } + + if (!using_dac) { + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + printk(KERN_ERR PFX "%s no usable DMA configuration\n", + pci_name(pdev)); + goto err_out_free_regions; + } + } + +#ifdef __BIG_ENDIAN + /* byte swap decriptors in hardware */ + { + u32 reg; + + pci_read_config_dword(pdev, PCI_DEV_REG2, ®); + reg |= PCI_REV_DESC; + pci_write_config_dword(pdev, PCI_DEV_REG2, reg); + } +#endif + + err = -ENOMEM; + hw = kmalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) { + printk(KERN_ERR PFX "%s: cannot allocate hardware struct\n", + pci_name(pdev)); + goto err_out_free_regions; + } + + memset(hw, 0, sizeof(*hw)); + hw->pdev = pdev; + spin_lock_init(&hw->phy_lock); + + hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); + if (!hw->regs) { + printk(KERN_ERR PFX "%s: cannot map device registers\n", + pci_name(pdev)); + goto err_out_free_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_iounmap; + } + pci_set_drvdata(pdev, hw); + + err = sky2_reset(hw); + if (err) + goto err_out_free_irq; + + printk(KERN_INFO PFX "addr 0x%lx irq %d chip 0x%x (%s) rev %d\n", + pci_resource_start(pdev, 0), pdev->irq, + hw->chip_id, chip_name(hw->chip_id), hw->chip_rev); + + if ((dev = sky2_init_netdev(hw, 0, using_dac)) == NULL) + goto err_out_free_pci; + + if ((err = register_netdev(dev))) { + printk(KERN_ERR PFX "%s: cannot register net device\n", + pci_name(pdev)); + goto err_out_free_netdev; + } + + sky2_show_addr(dev); + + if (hw->ports > 1 && (dev1 = sky2_init_netdev(hw, 1, using_dac))) { + if (register_netdev(dev1) == 0) + sky2_show_addr(dev1); + else { + /* Failure to register second port need not be fatal */ + printk(KERN_WARNING PFX "register of second port failed\n"); + hw->dev[1] = NULL; + free_netdev(dev1); + } + } + + return 0; + +err_out_free_netdev: + free_netdev(dev); + +err_out_free_irq: + free_irq(pdev->irq, hw); +err_out_free_pci: + pci_free_consistent(hw->pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); +err_out_iounmap: + iounmap(hw->regs); +err_out_free_hw: + kfree(hw); +err_out_free_regions: + pci_release_regions(pdev); +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +err_out: + return err; +} + +static void __devexit sky2_remove(struct pci_dev *pdev) +{ + struct sky2_hw *hw = pci_get_drvdata(pdev); + struct net_device *dev0, *dev1; + + if(!hw) + return; + + if ((dev1 = hw->dev[1])) + unregister_netdev(dev1); + dev0 = hw->dev[0]; + unregister_netdev(dev0); + + sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); + + free_irq(pdev->irq, hw); + pci_free_consistent(pdev, STATUS_LE_BYTES, + hw->st_le, hw->st_dma); + pci_release_regions(pdev); + pci_disable_device(pdev); + if (dev1) + free_netdev(dev1); + free_netdev(dev0); + iounmap(hw->regs); + kfree(hw); + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM +static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct sky2_hw *hw = pci_get_drvdata(pdev); + int i, wol = 0; + + for (i = 0; i < 2; i++) { + struct net_device *dev = hw->dev[i]; + + if (dev) { + struct sky2_port *sky2 = netdev_priv(dev); + if (netif_running(dev)) { + netif_carrier_off(dev); + sky2_down(dev); + } + netif_device_detach(dev); + wol |= sky2->wol; + } + } + + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sky2_resume(struct pci_dev *pdev) +{ + struct sky2_hw *hw = pci_get_drvdata(pdev); + int i; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + sky2_reset(hw); + + for (i = 0; i < 2; i++) { + struct net_device *dev = hw->dev[i]; + if (dev) { + netif_device_attach(dev); + if (netif_running(dev)) + sky2_up(dev); + } + } + return 0; +} +#endif + +static struct pci_driver sky2_driver = { + .name = DRV_NAME, + .id_table = sky2_id_table, + .probe = sky2_probe, + .remove = __devexit_p(sky2_remove), +#ifdef CONFIG_PM + .suspend = sky2_suspend, + .resume = sky2_resume, +#endif +}; + +static int __init sky2_init_module(void) +{ + + return pci_module_init(&sky2_driver); +} + +static void __exit sky2_cleanup_module(void) +{ + pci_unregister_driver(&sky2_driver); +} + +module_init(sky2_init_module); +module_exit(sky2_cleanup_module); + +MODULE_DESCRIPTION("Marvell Yukon 2 Gigabit Ethernet driver"); +MODULE_AUTHOR("Stephen Hemminger "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 793b883ed12a6ae6e2901ddb5e038b77d6f0c0ac Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 14 Sep 2005 16:06:14 -0700 Subject: [PATCH] sky2: driver update. Here is revised patch against netdev sky2 branch. It includes whitespace fixes, all the changes from the previous review as well as some optimizations and timing fixes to solve some of the hangs. The stall problem is better but not perfect. It appears that under stress the chip can't keep up with the bus and sends a pause frame, then hangs. This version is for testing, and hopefully other eyes might see the root cause of the problem. I don't want to reinvent the ugly watchdog code in the syskonnect version of sk98lin. If you read it you will see, the original driver writer and the hardware developer obviously didn't understand each other. Dual port support is included, but not tested yet. It did require small change to NAPI since both ports share same IRQ. Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 1311 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 749 insertions(+), 562 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index cfb2b41bc6b..bc95aacab20 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -15,7 +15,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License @@ -26,14 +26,17 @@ /* * TODO * - coalescing setting? - * - variable ring size? + * - vlan support * * TOTEST + * - variable ring size * - speed setting * - power management + * - netpoll */ #include +#include #include #include #include @@ -45,14 +48,13 @@ #include #include #include -#include #include #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.2" +#define DRV_VERSION "0.4" #define PFX DRV_NAME " " /* @@ -70,13 +72,18 @@ #define is_ec_a1(hw) 0 #endif -#define RX_LE_SIZE 256 -#define MIN_RX_BUFFERS 8 -#define MAX_RX_BUFFERS 124 +#define RX_LE_SIZE 256 #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) +#define RX_MAX_PENDING (RX_LE_SIZE/2 - 1) +#define RX_DEF_PENDING 128 +#define RX_COPY_THRESHOLD 128 + +#define TX_RING_SIZE 512 +#define TX_DEF_PENDING (TX_RING_SIZE - 1) +#define TX_MIN_PENDING 64 +#define MAX_SKB_TX_LE (4 + 2*MAX_SKB_FRAGS) -#define TX_RING_SIZE 256 // min 64 max 4096 -#define STATUS_RING_SIZE 1024 // pow2 > (2*Rx + Tx) +#define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */ #define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le)) #define ETH_JUMBO_MTU 9000 #define TX_WATCHDOG (5 * HZ) @@ -84,15 +91,16 @@ #define PHY_RETRIES 1000 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 - | NETIF_MSG_IFUP| NETIF_MSG_IFDOWN; + NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK + | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR + | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_INTR; -static int debug = -1; /* defaults above */ +static int debug = -1; /* defaults above */ module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); 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) }, { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, @@ -111,34 +119,24 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, { 0 } }; + 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 inline const char *chip_name(u8 chip_id) -{ - switch (chip_id) { - case CHIP_ID_GENESIS: - return "Genesis"; - case CHIP_ID_YUKON: - return "Yukon"; - case CHIP_ID_YUKON_LITE: - return "Yukon-Lite"; - case CHIP_ID_YUKON_LP: - return "Yukon-LP"; - case CHIP_ID_YUKON_XL: - return "Yukon-XL"; - case CHIP_ID_YUKON_EC: - return "Yukon-EC"; - case CHIP_ID_YUKON_FE: - return "Yukon-FE"; - default: - return "???"; - } -} +static const char *yukon_name[] = { + [CHIP_ID_YUKON_LITE - CHIP_ID_YUKON] = "Lite", /* 0xb0 */ + [CHIP_ID_YUKON_LP - CHIP_ID_YUKON] = "LP", /* 0xb2 */ + [CHIP_ID_YUKON_XL - CHIP_ID_YUKON] = "XL", /* 0xb3 */ + [CHIP_ID_YUKON_EC - CHIP_ID_YUKON] = "EC", /* 0xb6 */ + [CHIP_ID_YUKON_FE - CHIP_ID_YUKON] = "FE", /* 0xb7 */ +}; + + +/* Access to external PHY */ static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) { int i; @@ -148,30 +146,28 @@ static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg)); for (i = 0; i < PHY_RETRIES; i++) { - udelay(1); - if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY)) - break; + return; + udelay(1); } + printk(KERN_WARNING PFX "%s: phy write timeout\n", hw->dev[port]->name); } static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) { int i; - gma_write16(hw, port, GM_SMI_CTRL, - GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) + gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD); for (i = 0; i < PHY_RETRIES; i++) { - udelay(1); if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL) goto ready; + udelay(1); } - printk(KERN_WARNING PFX "%s: phy read timeout\n", - hw->dev[port]->name); - ready: + printk(KERN_WARNING PFX "%s: phy read timeout\n", hw->dev[port]->name); +ready: return gma_read16(hw, port, GM_SMI_DATA); } @@ -183,6 +179,7 @@ static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0); /* disable PHY IRQs */ gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0); + gma_write16(hw, port, GM_MC_ADDR_H1, 0); /* clear MC hash */ gma_write16(hw, port, GM_MC_ADDR_H2, 0); gma_write16(hw, port, GM_MC_ADDR_H3, 0); @@ -196,21 +193,13 @@ static void sky2_phy_reset(struct sky2_hw *hw, unsigned port) static void sky2_phy_init(struct sky2_hw *hw, unsigned port) { struct sky2_port *sky2 = netdev_priv(hw->dev[port]); - u16 ctrl, ct1000, adv; - u16 ledctrl, ledover; - - pr_debug("phy reset autoneg=%s advertising=0x%x pause rx=%s tx=%s\n", - sky2->autoneg == AUTONEG_ENABLE ? "enable" : "disable", - sky2->advertising, - sky2->rx_pause ? "on" : "off", - sky2->tx_pause ? "on" : "off"); + u16 ctrl, ct1000, adv, pg, ledctrl, ledover; - if (sky2->autoneg == AUTONEG_ENABLE && - hw->chip_id != CHIP_ID_YUKON_XL) { + 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 | - PHY_M_EC_MAC_S_MSK); + PHY_M_EC_MAC_S_MSK); ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ); if (hw->chip_id == CHIP_ID_YUKON_EC) @@ -258,9 +247,6 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* select page 1 to access Fiber registers */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1); } - - ctrl &= ~(PHY_M_PC_MDIX_MSK | PHY_M_MAC_MD_MSK); - ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX); } ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL); @@ -290,14 +276,14 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) adv |= PHY_M_AN_10_FD; if (sky2->advertising & ADVERTISED_10baseT_Half) adv |= PHY_M_AN_10_HD; - } else /* special defines for FIBER (88E1011S only) */ + } else /* special defines for FIBER (88E1011S only) */ adv |= PHY_M_AN_1000X_AHD | PHY_M_AN_1000X_AFD; /* Set Flow-control capabilities */ if (sky2->tx_pause && sky2->rx_pause) - adv |= PHY_AN_PAUSE_CAP; /* symmetric */ + adv |= PHY_AN_PAUSE_CAP; /* symmetric */ else if (sky2->rx_pause && !sky2->tx_pause) - adv |= PHY_AN_PAUSE_ASYM|PHY_AN_PAUSE_CAP; + adv |= PHY_AN_PAUSE_ASYM | PHY_AN_PAUSE_CAP; else if (!sky2->rx_pause && sky2->tx_pause) adv |= PHY_AN_PAUSE_ASYM; /* local */ @@ -347,26 +333,28 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) break; case CHIP_ID_YUKON_XL: - ctrl = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + 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(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, - (PHY_M_POLC_LS1_P_MIX(4) | PHY_M_POLC_IS0_P_MIX(4) | - PHY_M_POLC_LOS_CTRL(2) | PHY_M_POLC_INIT_CTRL(2) | - PHY_M_POLC_STA1_CTRL(2) | PHY_M_POLC_STA0_CTRL(2))); + (PHY_M_POLC_LS1_P_MIX(4) | + PHY_M_POLC_IS0_P_MIX(4) | + PHY_M_POLC_LOS_CTRL(2) | + PHY_M_POLC_INIT_CTRL(2) | + PHY_M_POLC_STA1_CTRL(2) | + PHY_M_POLC_STA0_CTRL(2))); /* restore page register */ - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, ctrl); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); break; default: @@ -405,8 +393,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 - && port == 1) { + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 && port == 1) { /* WA DEV_472 -- looks like crossed wires on port 2 */ /* clear GMAC 1 Control reset */ sky2_write8(hw, SK_REG(0, GMAC_CTRL), GMC_RST_CLR); @@ -418,14 +405,12 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0); } - if (sky2->autoneg == AUTONEG_DISABLE) { reg = gma_read16(hw, port, GM_GP_CTRL); reg |= GM_GPCR_AU_ALL_DIS; gma_write16(hw, port, GM_GP_CTRL, reg); gma_read16(hw, port, GM_GP_CTRL); - switch (sky2->speed) { case SPEED_1000: reg |= GM_GPCR_SPEED_1000; @@ -441,15 +426,16 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) if (!sky2->tx_pause && !sky2->rx_pause) { sky2_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); - reg |= GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; - } else if (sky2->tx_pause &&!sky2->rx_pause) { + reg |= + GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; + } else if (sky2->tx_pause && !sky2->rx_pause) { /* disable Rx flow-control */ reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; } gma_write16(hw, port, GM_GP_CTRL, reg); - sky2_read16(hw, GMAC_IRQ_SRC); + sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC)); spin_lock_bh(&hw->phy_lock); sky2_phy_init(hw, port); @@ -460,7 +446,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR); for (i = 0; i < GM_MIB_CNT_SIZE; i++) - gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i); + gma_read16(hw, port, GM_MIB_CNT_BASE + 8 * i); gma_write16(hw, port, GM_PHY_ADDR, reg); /* transmit control */ @@ -468,7 +454,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* receive control reg: unicast + multicast + no FCS */ gma_write16(hw, port, GM_RX_CTRL, - GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA); + GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA); /* transmit flow control */ gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff); @@ -482,44 +468,43 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* serial mode register */ reg = DATA_BLIND_VAL(DATA_BLIND_DEF) | - GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); + GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); if (hw->dev[port]->mtu > 1500) reg |= GM_SMOD_JUMBO_ENA; gma_write16(hw, port, GM_SERIAL_MODE, reg); - /* physical address: used for pause frames */ - gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr); /* virtual address for data */ gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr); - /* enable interrupt mask for counter overflows */ + /* physical address: used for pause frames */ + gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr); + + /* ignore counter overflows */ gma_write16(hw, port, GM_TX_IRQ_MSK, 0); gma_write16(hw, port, GM_RX_IRQ_MSK, 0); gma_write16(hw, port, GM_TR_IRQ_MSK, 0); /* Configure Rx MAC FIFO */ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); - sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), + sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_OPER_ON | GMF_RX_F_FL_ON); - reg = RX_FF_FL_DEF_MSK; + /* Flush Rx MAC FIFO on any flowcontrol or error */ + reg = GMR_FS_ANY_ERR; if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev <= 1) reg = 0; /* WA Dev #4115 */ sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), reg); - /* Set threshold to 0xa (64 bytes) - * ASF disabled so no need to do WA dev #4.30 + /* Set threshold to 0xa (64 bytes) + * ASF disabled so no need to do WA dev #4.30 */ sky2_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF); /* Configure Tx MAC FIFO */ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR); sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON); - - /* Turn off Rx fifo flush (per sk98lin) */ - sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_F_FL_OFF); } static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) @@ -529,7 +514,8 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) start /= 8; len /= 8; end = start + len - 1; - pr_debug("ramset q=%d start=0x%x end=0x%x\n", q, start, end); + + pr_debug("sky2_ramset start=%d end=%d\n", start, end); sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR); sky2_write32(hw, RB_ADDR(q, RB_START), start); @@ -538,11 +524,15 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) sky2_write32(hw, RB_ADDR(q, RB_RP), start); if (q == Q_R1 || q == Q_R2) { + u32 rxup, rxlo; + + rxlo = len/2; + rxup = rxlo + len/4; + pr_debug(" utpp=%d ltpp=%d\n", rxup, rxlo); + /* Set thresholds on receive queue's */ - sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), - start + (2*len)/3); - sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), - start + (len/3)); + sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), rxup); + sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), rxlo); } else { /* Enable store & forward on Tx queue's because * Tx FIFO is only 1K on Yukon @@ -551,9 +541,9 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) } sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD); + sky2_read8(hw, RB_ADDR(q, RB_CTRL)); } - /* Setup Bus Memory Interface */ static void sky2_qset(struct sky2_hw *hw, u16 q, u32 wm) { @@ -563,61 +553,63 @@ static void sky2_qset(struct sky2_hw *hw, u16 q, u32 wm) sky2_write32(hw, Q_ADDR(q, Q_WM), wm); } - /* Setup prefetch unit registers. This is the interface between * hardware and driver list elements */ static inline void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr, u64 addr, u32 last) { - pr_debug("sky2 prefetch init q=%x addr=%llx last=%x\n", - Y2_QADDR(qaddr, 0), addr, last); - sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR); sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), addr >> 32); sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), (u32) addr); sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last); sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON); + + sky2_read32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL)); } +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 = (sky2->tx_prod + 1) % TX_RING_SIZE; + return le; +} /* * This is a workaround code taken from syskonnect sk98lin driver - * to deal with chip bug in the wraparound case. + * to deal with chip bug on Yukon EC rev 0 in the wraparound case. */ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx, u16 *last, u16 size) - { - BUG_ON(idx >= size); - - wmb(); 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); + sky2_write8(hw, Y2_QADDR(q, PREF_UNIT_FIFO_WM), 0xe0); goto setnew; } - if (hwget == size-1) { + 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 /* have hardware go to end of list */ + sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), + size - 1); } else { - setnew: +setnew: sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx); - *last = idx; } + *last = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_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; @@ -625,61 +617,74 @@ static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2) return le; } -static inline void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map, u16 len) +/* Build description to hardware about buffer */ +static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re) { struct sky2_rx_le *le; + u32 hi = (re->mapaddr >> 16) >> 16; - if (sizeof(map) > sizeof(u32)) { + re->idx = sky2->rx_put; + if (sky2->rx_addr64 != hi) { le = sky2_next_rx(sky2); - le->rx.addr = cpu_to_le32((u64) map >> 32); + le->addr = cpu_to_le32(hi); le->ctrl = 0; le->opcode = OP_ADDR64 | HW_OWNER; + sky2->rx_addr64 = hi; } - + le = sky2_next_rx(sky2); - le->rx.addr = cpu_to_le32((u32) map); - le->length = cpu_to_le16(len); + le->addr = cpu_to_le32((u32) re->mapaddr); + le->length = cpu_to_le16(re->maplen); le->ctrl = 0; le->opcode = OP_PACKET | HW_OWNER; } +/* Tell receiver about new buffers. */ +static inline void rx_set_put(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + if (sky2->rx_last_put != sky2->rx_put) + sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put, + &sky2->rx_last_put, RX_LE_SIZE); +} + /* Tell chip where to start receive checksum. * Actually has two checksums, but set both same to avoid possible byte * order problems. */ -static void sky2_rx_set_offset(struct sky2_port *sky2) +static void rx_set_checksum(struct sky2_port *sky2) { struct sky2_rx_le *le; - sky2_write32(sky2->hw, - Q_ADDR(rxqaddr[sky2->port], Q_CSR), - sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); - le = sky2_next_rx(sky2); - le->rx.csum.start1 = ETH_HLEN; - le->rx.csum.start2 = ETH_HLEN; + le->addr = (ETH_HLEN << 16) | ETH_HLEN; le->ctrl = 0; le->opcode = OP_TCPSTART | HW_OWNER; - wmb(); - sky2_write16(sky2->hw, - Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX), - sky2->rx_put); + + sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port], + PREF_UNIT_PUT_IDX), sky2->rx_put); + sky2_read16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX)); + mdelay(1); + sky2_write32(sky2->hw, + Q_ADDR(rxqaddr[sky2->port], Q_CSR), + sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); } + /* Cleanout receive buffer area, assumes receiver hardware stopped */ static void sky2_rx_clean(struct sky2_port *sky2) { unsigned i; memset(sky2->rx_le, 0, RX_LE_BYTES); - for (i = 0; i < sky2->rx_ring_size; i++) { + for (i = 0; i < sky2->rx_pending; i++) { struct ring_info *re = sky2->rx_ring + i; if (re->skb) { - pci_unmap_single(sky2->hw->pdev, - pci_unmap_addr(re, mapaddr), - pci_unmap_len(re, maplen), + pci_unmap_single(sky2->hw->pdev, + re->mapaddr, re->maplen, PCI_DMA_FROMDEVICE); kfree_skb(re->skb); re->skb = NULL; @@ -687,12 +692,13 @@ static void sky2_rx_clean(struct sky2_port *sky2) } } -static inline struct sk_buff *sky2_rx_alloc_skb(struct sky2_port *sky2, - unsigned int size, int gfp_mask) +static inline struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, + unsigned int size, + unsigned int gfp_mask) { struct sk_buff *skb; - skb = alloc_skb(size, gfp_mask); + skb = alloc_skb(size + NET_IP_ALIGN, gfp_mask); if (likely(skb)) { skb->dev = sky2->netdev; skb_reserve(skb, NET_IP_ALIGN); @@ -709,29 +715,21 @@ static inline struct sk_buff *sky2_rx_alloc_skb(struct sky2_port *sky2, static int sky2_rx_fill(struct sky2_port *sky2) { unsigned i; - unsigned int rx_buf_size = sky2->netdev->mtu + ETH_HLEN + 8; + const unsigned rx_buf_size = sky2->netdev->mtu + ETH_HLEN + 8; - pr_debug("sky2_rx_fill %d\n", sky2->rx_ring_size); - for (i = 0; i < sky2->rx_ring_size; i++) { + for (i = 0; i < sky2->rx_pending; i++) { struct ring_info *re = sky2->rx_ring + i; - dma_addr_t paddr; - re->skb = sky2_rx_alloc_skb(sky2, rx_buf_size, GFP_KERNEL); + re->skb = sky2_rx_alloc(sky2, rx_buf_size, GFP_KERNEL); if (!re->skb) goto nomem; - paddr = pci_map_single(sky2->hw->pdev, re->skb->data, - rx_buf_size, PCI_DMA_FROMDEVICE); - - pci_unmap_len_set(re, maplen, rx_buf_size); - pci_unmap_addr_set(re, mapaddr, paddr); - sky2_rx_add(sky2, paddr, rx_buf_size); + re->mapaddr = pci_map_single(sky2->hw->pdev, re->skb->data, + rx_buf_size, PCI_DMA_FROMDEVICE); + re->maplen = rx_buf_size; + sky2_rx_add(sky2, re); } - sky2_write16(sky2->hw, - Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX), - sky2->rx_put); - return 0; nomem: sky2_rx_clean(sky2); @@ -752,7 +750,8 @@ static int sky2_up(struct net_device *dev) /* must be power of 2 */ sky2->tx_le = pci_alloc_consistent(hw->pdev, - TX_RING_SIZE * sizeof(struct sky2_tx_le), + TX_RING_SIZE * + sizeof(struct sky2_tx_le), &sky2->tx_le_map); if (!sky2->tx_le) goto err_out; @@ -770,7 +769,7 @@ static int sky2_up(struct net_device *dev) goto err_out; memset(sky2->rx_le, 0, RX_LE_BYTES); - sky2->rx_ring = kmalloc(sky2->rx_ring_size * sizeof(struct ring_info), + sky2->rx_ring = kmalloc(sky2->rx_pending * sizeof(struct ring_info), GFP_KERNEL); if (!sky2->rx_ring) goto err_out; @@ -782,8 +781,8 @@ static int sky2_up(struct net_device *dev) (hw->chip_id == CHIP_ID_YUKON_EC && hw->chip_rev == 2)) ramsize = 4096; else { - u8 e0 = sky2_read8(hw, B2_E_0); - ramsize = (e0 == 0) ? (128*1024) : (e0 * 4096); + u8 e0 = sky2_read8(hw, B2_E_0); + ramsize = (e0 == 0) ? (128 * 1024) : (e0 * 4096); } /* 2/3 for Rx */ @@ -791,18 +790,29 @@ static int sky2_up(struct net_device *dev) sky2_ramset(hw, rxqaddr[port], 0, rxspace); sky2_ramset(hw, txqaddr[port], rxspace, ramsize - rxspace); + /* Make sure SyncQ is disabled */ + sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL), + RB_RST_SET); + sky2_qset(hw, rxqaddr[port], is_pciex(hw) ? 0x80 : 0x600); sky2_qset(hw, txqaddr[port], 0x600); sky2->rx_put = sky2->rx_next = 0; - sky2_prefetch_init(hw, rxqaddr[port], sky2->rx_le_map, RX_LE_SIZE-1); + sky2_prefetch_init(hw, rxqaddr[port], sky2->rx_le_map, RX_LE_SIZE - 1); - sky2_rx_set_offset(sky2); + rx_set_checksum(sky2); err = sky2_rx_fill(sky2); if (err) goto err_out; + /* Give buffers to receiver */ + sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX), + sky2->rx_put); + sky2->rx_last_put = sky2_read16(sky2->hw, + Y2_QADDR(rxqaddr[port], + PREF_UNIT_PUT_IDX)); + sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, TX_RING_SIZE - 1); @@ -827,96 +837,117 @@ err_out: return err; } -/* - * Worst case number of list elements is 36 - * TSO + CHKSUM + ADDR64 + BUFFER + (ADDR+BUFFER)*MAXFRAGS - */ -#define MAX_SKB_TX_LE (4 + 2*MAX_SKB_FRAGS) +/* Modular subtraction in ring */ +static inline int tx_dist(unsigned tail, unsigned head) +{ + return (head >= tail ? head : head + TX_RING_SIZE) - tail; +} -static inline int sky2_xmit_avail(const struct sky2_port *sky2) +/* Number of list elements available for next tx */ +static inline int tx_avail(const struct sky2_port *sky2) { - return (sky2->tx_cons > sky2->tx_prod ? 0 : TX_RING_SIZE) - + sky2->tx_cons - sky2->tx_prod - 1; + return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod); } -static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2) +/* Estimate of number of transmit list elements required */ +static inline unsigned tx_le_req(const struct sk_buff *skb) { - struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod; - sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE; - return le; + unsigned count; + + count = sizeof(dma_addr_t) / sizeof(u32); + count += skb_shinfo(skb)->nr_frags * count; + + if (skb_shinfo(skb)->tso_size) + ++count; + + if (skb->ip_summed) + ++count; + + return count; } -/* Put one frame in ring for transmit. */ +/* + * Put one packet in ring for transmit. + * A single packet can generate multiple list elements, and + * the number of ring elements will probably be less than the number + * of list elements used. + */ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; struct sky2_tx_le *le; struct ring_info *re; + unsigned long flags; unsigned i, len; dma_addr_t mapping; u32 addr64; u16 mss; u8 ctrl; - skb = skb_padto(skb, ETH_ZLEN); - if (!skb) - return NETDEV_TX_OK; - - if (!spin_trylock(&sky2->tx_lock)) + local_irq_save(flags); + if (!spin_trylock(&sky2->tx_lock)) { + local_irq_restore(flags); return NETDEV_TX_LOCKED; + } - if (unlikely(sky2_xmit_avail(sky2) < MAX_SKB_TX_LE)) { + if (unlikely(tx_avail(sky2) < tx_le_req(skb))) { netif_stop_queue(dev); - spin_unlock(&sky2->tx_lock); + spin_unlock_irqrestore(&sky2->tx_lock, flags); printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", dev->name); return NETDEV_TX_BUSY; } - if (netif_msg_tx_queued(sky2)) + if (unlikely(netif_msg_tx_queued(sky2))) printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n", dev->name, sky2->tx_prod, skb->len); - len = skb_headlen(skb); mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + addr64 = (mapping >> 16) >> 16; + + re = sky2->tx_ring + sky2->tx_prod; + + /* Send high bits if changed */ + if (addr64 != sky2->tx_addr64) { + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32(addr64); + le->ctrl = 0; + le->opcode = OP_ADDR64 | HW_OWNER; + sky2->tx_addr64 = addr64; + } /* Check for TCP Segmentation Offload */ mss = skb_shinfo(skb)->tso_size; - if (mss) { + 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); - return NETDEV_TX_OK; + dev_kfree_skb_any(skb); + goto out_unlock; } mss += ((skb->h.th->doff - 5) * 4); /* TCP options */ mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); mss += ETH_HLEN; + } + if (mss != sky2->tx_last_mss) { le = get_tx_le(sky2); le->tx.tso.size = cpu_to_le16(mss); - le->ctrl = 0; + le->tx.tso.rsvd = 0; le->opcode = OP_LRGLEN | HW_OWNER; - } - - /* Handle Hi DMA */ - if (sizeof(mapping) > sizeof(u32)) { - addr64 = (u64)mapping >> 32; - - le = get_tx_le(sky2); - le->tx.addr = cpu_to_le32(addr64); le->ctrl = 0; - le->opcode = OP_ADDR64 | HW_OWNER; + sky2->tx_last_mss = mss; } /* Handle TCP checksum offload */ ctrl = 0; if (skb->ip_summed == CHECKSUM_HW) { - ptrdiff_t hdr = skb->h.raw - skb->data; + u16 hdr = skb->h.raw - skb->data; + u16 offset = hdr + skb->csum; ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM; if (skb->nh.iph->protocol == IPPROTO_UDP) @@ -924,119 +955,112 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) le = get_tx_le(sky2); le->tx.csum.start = cpu_to_le16(hdr); - le->tx.csum.offset = cpu_to_le16(hdr + skb->csum); - le->length = 0; + le->tx.csum.offset = cpu_to_le16(offset); + le->length = 0; /* initial checksum value */ le->ctrl = 1; /* one packet */ - le->opcode = OP_TCPLISW|HW_OWNER; + le->opcode = OP_TCPLISW | HW_OWNER; } le = get_tx_le(sky2); le->tx.addr = cpu_to_le32((u32) mapping); le->length = cpu_to_le16(len); le->ctrl = ctrl; - le->opcode = (mss ? OP_LARGESEND : OP_PACKET) |HW_OWNER; + le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER); - re = &sky2->tx_ring[le - sky2->tx_le]; + /* Record the transmit mapping info */ re->skb = skb; - pci_unmap_addr_set(re, mapaddr, mapping); - pci_unmap_len_set(re, maplen, len); + re->mapaddr = mapping; + re->maplen = len; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + struct ring_info *fre; mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); - - if (sizeof(mapping) > sizeof(u32)) { - u32 hi = (u64) mapping >> 32; - if (hi != addr64) { - le = get_tx_le(sky2); - le->tx.addr = cpu_to_le32(hi); - le->ctrl = 0; - le->opcode = OP_ADDR64|HW_OWNER; - addr64 = hi; - } + addr64 = (mapping >> 16) >> 16; + if (addr64 != sky2->tx_addr64) { + le = get_tx_le(sky2); + le->tx.addr = cpu_to_le32(addr64); + le->ctrl = 0; + le->opcode = OP_ADDR64 | HW_OWNER; + sky2->tx_addr64 = addr64; } le = get_tx_le(sky2); le->tx.addr = cpu_to_le32((u32) mapping); le->length = cpu_to_le16(frag->size); le->ctrl = ctrl; - le->opcode = OP_BUFFER|HW_OWNER; + le->opcode = OP_BUFFER | HW_OWNER; - re = &sky2->tx_ring[le - sky2->tx_le]; - pci_unmap_addr_set(re, mapaddr, mapping); - pci_unmap_len_set(re, maplen, frag->size); + fre = sky2->tx_ring + + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; + fre->skb = NULL; + fre->mapaddr = mapping; + fre->maplen = frag->size; } - + re->idx = sky2->tx_prod; le->ctrl |= EOP; sky2_put_idx(sky2->hw, txqaddr[sky2->port], sky2->tx_prod, &sky2->tx_last_put, TX_RING_SIZE); - if (sky2_xmit_avail(sky2) < MAX_SKB_TX_LE) { - pr_debug("%s: transmit queue full\n", dev->name); + if (tx_avail(sky2) < MAX_SKB_TX_LE + 1) netif_stop_queue(dev); - } - spin_unlock(&sky2->tx_lock); + +out_unlock: + mmiowb(); + spin_unlock_irqrestore(&sky2->tx_lock, flags); dev->trans_start = jiffies; return NETDEV_TX_OK; } - /* - * Free ring elements from starting at tx_cons until done - * This unwinds the elements based on the usage assigned - * xmit routine. + * Free ring elements from starting at tx_cons until "done" + * + * NB: the hardware will tell us about partial completion of multi-part + * buffers; these are defered until completion. */ static void sky2_tx_complete(struct net_device *dev, u16 done) { struct sky2_port *sky2 = netdev_priv(dev); - unsigned idx = sky2->tx_cons; - struct sk_buff *skb = NULL; + unsigned i; - BUG_ON(done >= TX_RING_SIZE); + if (netif_msg_tx_done(sky2)) + printk(KERN_DEBUG "%s: tx done, upto %u\n", dev->name, done); spin_lock(&sky2->tx_lock); - while (idx != done) { - struct ring_info *re = sky2->tx_ring + idx; - struct sky2_tx_le *le = sky2->tx_le + idx; - - BUG_ON(le->opcode == 0); - - switch(le->opcode & ~HW_OWNER) { - case OP_LARGESEND: - case OP_PACKET: - if (skb) - dev_kfree_skb_any(skb); - skb = re->skb; - BUG_ON(!skb); - re->skb = NULL; - pci_unmap_single(sky2->hw->pdev, - pci_unmap_addr(re, mapaddr), - pci_unmap_len(re, maplen), - PCI_DMA_TODEVICE); - break; + while (sky2->tx_cons != done) { + struct ring_info *re = sky2->tx_ring + sky2->tx_cons; + struct sk_buff *skb; - case OP_BUFFER: - pci_unmap_page(sky2->hw->pdev, - pci_unmap_addr(re, mapaddr), - pci_unmap_len(re, maplen), - PCI_DMA_TODEVICE); - break; + /* Check for partial status */ + if (tx_dist(sky2->tx_cons, done) + < tx_dist(sky2->tx_cons, re->idx)) + goto out; + + skb = re->skb; + pci_unmap_single(sky2->hw->pdev, + re->mapaddr, re->maplen, PCI_DMA_TODEVICE); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct ring_info *fre; + fre = + sky2->tx_ring + (sky2->tx_cons + i + + 1) % TX_RING_SIZE; + pci_unmap_page(sky2->hw->pdev, fre->mapaddr, + fre->maplen, PCI_DMA_TODEVICE); } - le->opcode = 0; - idx = (idx + 1) % TX_RING_SIZE; - } - - if (skb) dev_kfree_skb_any(skb); - sky2->tx_cons = idx; - if (sky2_xmit_avail(sky2) > MAX_SKB_TX_LE) + sky2->tx_cons = re->idx; + } +out: + + if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE) netif_wake_queue(dev); spin_unlock(&sky2->tx_lock); } @@ -1061,22 +1085,24 @@ static int sky2_down(struct net_device *dev) netif_stop_queue(dev); + sky2_phy_reset(hw, port); + /* Stop transmitter */ sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP); sky2_read32(hw, Q_ADDR(txqaddr[port], Q_CSR)); sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), - RB_RST_SET|RB_DIS_OP_MD); + RB_RST_SET | RB_DIS_OP_MD); ctrl = gma_read16(hw, port, GM_GP_CTRL); - ctrl &= ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA); + ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA); gma_write16(hw, port, GM_GP_CTRL, ctrl); sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); /* Workaround shared GMAC reset */ - if (! (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 - && port == 0 && hw->dev[1] && netif_running(hw->dev[1]))) + if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 + && port == 0 && hw->dev[1] && netif_running(hw->dev[1]))) sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET); /* Disable Force Sync bit and Enable Alloc bit */ @@ -1088,7 +1114,8 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L); /* Reset the PCI FIFO of the async Tx queue */ - sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_RST_SET | BMU_FIFO_RST); + sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), + BMU_RST_SET | BMU_FIFO_RST); /* Reset the Tx prefetch units */ sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL), @@ -1144,6 +1171,9 @@ static int sky2_down(struct net_device *dev) static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux) { + if (!hw->copper) + return SPEED_1000; + if (hw->chip_id == CHIP_ID_YUKON_FE) return (aux & PHY_M_PS_SPEED_100) ? SPEED_100 : SPEED_10; @@ -1163,14 +1193,16 @@ static void sky2_link_up(struct sky2_port *sky2) unsigned port = sky2->port; u16 reg; + /* disable Rx GMAC FIFO flush mode */ + sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_F_FL_OFF); + /* Enable Transmit FIFO Underrun */ - sky2_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK); + sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK); reg = gma_read16(hw, port, GM_GP_CTRL); if (sky2->duplex == DUPLEX_FULL || sky2->autoneg == AUTONEG_ENABLE) reg |= GM_GPCR_DUP_FULL; - /* enable Rx/Tx */ reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA; gma_write16(hw, port, GM_GP_CTRL, reg); @@ -1182,17 +1214,30 @@ static void sky2_link_up(struct sky2_port *sky2) netif_wake_queue(sky2->netdev); /* Turn on link LED */ - sky2_write8(hw, SK_REG(port, LNK_LED_REG), + 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) { + u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); + 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); + } + if (netif_msg_link(sky2)) printk(KERN_INFO PFX "%s: Link is up at %d Mbps, %s duplex, flowcontrol %s\n", sky2->netdev->name, sky2->speed, sky2->duplex == DUPLEX_FULL ? "full" : "half", (sky2->tx_pause && sky2->rx_pause) ? "both" : - sky2->tx_pause ? "tx" : - sky2->rx_pause ? "rx" : "none"); + sky2->tx_pause ? "tx" : sky2->rx_pause ? "rx" : "none"); } static void sky2_link_down(struct sky2_port *sky2) @@ -1211,9 +1256,8 @@ static void sky2_link_down(struct sky2_port *sky2) if (sky2->rx_pause && !sky2->tx_pause) { /* restore Asymmetric Pause bit */ gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, - gm_phy_read(hw, port, - PHY_MARV_AUNE_ADV) - | PHY_M_AN_ASP); + gm_phy_read(hw, port, PHY_MARV_AUNE_ADV) + | PHY_M_AN_ASP); } sky2_phy_reset(hw, port); @@ -1229,6 +1273,51 @@ static void sky2_link_down(struct sky2_port *sky2) sky2_phy_init(hw, port); } +static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) +{ + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + u16 lpa; + + 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; + } + + if (hw->chip_id != CHIP_ID_YUKON_FE && + gm_phy_read(hw, port, PHY_MARV_1000T_STAT) & PHY_B_1000S_MSF) { + printk(KERN_ERR PFX "%s: master/slave fault", + sky2->netdev->name); + return -1; + } + + if (!(aux & PHY_M_PS_SPDUP_RES)) { + printk(KERN_ERR PFX "%s: speed/duplex mismatch", + sky2->netdev->name); + return -1; + } + + sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; + + sky2->speed = sky2_phy_speed(hw, aux); + + /* Pause bits are offset (9..8) */ + if (hw->chip_id == CHIP_ID_YUKON_XL) + aux >>= 6; + + sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0; + sky2->tx_pause = (aux & PHY_M_PS_TX_P_EN) != 0; + + if ((sky2->tx_pause || sky2->rx_pause) + && !(sky2->speed < SPEED_1000 && sky2->duplex == DUPLEX_HALF)) + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); + else + sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); + + return 0; +} /* * Interrrupt from PHY are handled in tasklet (soft irq) @@ -1237,69 +1326,42 @@ static void sky2_link_down(struct sky2_port *sky2) */ static void sky2_phy_task(unsigned long data) { - struct sky2_port *sky2 = (struct sky2_port *) data; + struct sky2_port *sky2 = (struct sky2_port *)data; struct sky2_hw *hw = sky2->hw; - unsigned port = sky2->port; u16 istatus, phystat; - istatus = gm_phy_read(hw, port, PHY_MARV_INT_STAT); - - phystat = gm_phy_read(hw, port, PHY_MARV_PHY_STAT); + spin_lock(&hw->phy_lock); + 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", sky2->netdev->name, istatus, phystat); if (istatus & PHY_M_IS_AN_COMPL) { - u16 lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP); + if (sky2_autoneg_done(sky2, phystat) == 0) + sky2_link_up(sky2); + goto out; + } - if (lpa & PHY_M_AN_RF) { - printk(KERN_ERR PFX "%s: remote fault", - sky2->netdev->name); - } - else if (hw->chip_id != CHIP_ID_YUKON_FE - && gm_phy_read(hw, port, PHY_MARV_1000T_STAT) - & PHY_B_1000S_MSF) { - printk(KERN_ERR PFX "%s: master/slave fault", - sky2->netdev->name); - } - else if (!(phystat & PHY_M_PS_SPDUP_RES)) { - printk(KERN_ERR PFX "%s: speed/duplex mismatch", - sky2->netdev->name); - } - else { - sky2->duplex = (phystat & PHY_M_PS_FULL_DUP) - ? DUPLEX_FULL : DUPLEX_HALF; + if (istatus & PHY_M_IS_LSP_CHANGE) + sky2->speed = sky2_phy_speed(hw, phystat); - sky2->speed = sky2_phy_speed(hw, phystat); - - sky2->tx_pause = (phystat & PHY_M_PS_TX_P_EN) != 0; - sky2->rx_pause = (phystat & PHY_M_PS_RX_P_EN) != 0; + if (istatus & PHY_M_IS_DUP_CHANGE) + sky2->duplex = + (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; - if ((!sky2->tx_pause && !sky2->rx_pause) || - (sky2->speed < SPEED_1000 && sky2->duplex == DUPLEX_HALF)) - sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); - else - sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); + if (istatus & PHY_M_IS_LST_CHANGE) { + if (phystat & PHY_M_PS_LINK_UP) sky2_link_up(sky2); - } - } else { - - if (istatus & PHY_M_IS_LSP_CHANGE) - sky2->speed = sky2_phy_speed(hw, phystat); - - if (istatus & PHY_M_IS_DUP_CHANGE) - sky2->duplex = (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; - if (istatus & PHY_M_IS_LST_CHANGE) { - if (phystat & PHY_M_PS_LINK_UP) - sky2_link_up(sky2); - else - sky2_link_down(sky2); - } + else + sky2_link_down(sky2); } +out: + spin_unlock(&hw->phy_lock); local_irq_disable(); - hw->intr_mask |= (port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; + hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; sky2_write32(hw, B0_IMSK, hw->intr_mask); local_irq_enable(); } @@ -1346,63 +1408,78 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, struct net_device *dev = hw->dev[port]; struct sky2_port *sky2 = netdev_priv(dev); struct ring_info *re = sky2->rx_ring + sky2->rx_next; - struct sk_buff *skb = re->skb; - dma_addr_t mapping; + struct sk_buff *skb, *nskb; const unsigned int rx_buf_size = dev->mtu + ETH_HLEN + 8; if (unlikely(netif_msg_rx_status(sky2))) printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", dev->name, sky2->rx_next, status, length); - sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_ring_size; - - pci_unmap_single(sky2->hw->pdev, - pci_unmap_addr(re, mapaddr), - pci_unmap_len(re, maplen), - PCI_DMA_FROMDEVICE); - prefetch(skb->data); + sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; - if (!(status & GMR_FS_RX_OK) - || (status & GMR_FS_ANY_ERR) - || (length << 16) != (status & GMR_FS_LEN) - || length > rx_buf_size) + skb = NULL; + if (!(status & GMR_FS_RX_OK) + || (status & GMR_FS_ANY_ERR) + || (length << 16) != (status & GMR_FS_LEN) + || length > rx_buf_size) goto error; - re->skb = sky2_rx_alloc_skb(sky2, rx_buf_size, GFP_ATOMIC); - if (!re->skb) - goto reuse; - -submit: - mapping = pci_map_single(sky2->hw->pdev, re->skb->data, - rx_buf_size, PCI_DMA_FROMDEVICE); + if (length < RX_COPY_THRESHOLD) { + nskb = sky2_rx_alloc(sky2, length, GFP_ATOMIC); + if (!nskb) + goto resubmit; + + pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->mapaddr, + length, PCI_DMA_FROMDEVICE); + memcpy(nskb->data, re->skb->data, length); + pci_dma_sync_single_for_device(sky2->hw->pdev, re->mapaddr, + length, PCI_DMA_FROMDEVICE); + skb = nskb; + } else { + nskb = sky2_rx_alloc(sky2, rx_buf_size, GFP_ATOMIC); + if (!nskb) + goto resubmit; - pci_unmap_len_set(re, maplen, rx_buf_size); - pci_unmap_addr_set(re, mapaddr, mapping); + skb = re->skb; + pci_unmap_single(sky2->hw->pdev, re->mapaddr, + re->maplen, PCI_DMA_FROMDEVICE); + prefetch(skb->data); - sky2_rx_add(sky2, mapping, rx_buf_size); - sky2_put_idx(sky2->hw, rxqaddr[sky2->port], - sky2->rx_put, &sky2->rx_last_put, RX_LE_SIZE); + re->skb = nskb; + re->mapaddr = pci_map_single(sky2->hw->pdev, nskb->data, + rx_buf_size, PCI_DMA_FROMDEVICE); + re->maplen = rx_buf_size; + } +resubmit: + BUG_ON(re->skb == skb); + sky2_rx_add(sky2, re); return skb; error: + if (status & GMR_FS_GOOD_FC) + goto resubmit; + if (netif_msg_rx_err(sky2)) printk(KERN_INFO PFX "%s: rx error, status 0x%x length %d\n", sky2->netdev->name, status, length); - - if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE)) + + if (status & (GMR_FS_LONG_ERR | GMR_FS_UN_SIZE)) sky2->net_stats.rx_length_errors++; if (status & GMR_FS_FRAGMENT) sky2->net_stats.rx_frame_errors++; if (status & GMR_FS_CRC_ERR) sky2->net_stats.rx_crc_errors++; -reuse: - re->skb = skb; - skb = NULL; - goto submit; + if (status & GMR_FS_RX_FF_OV) + sky2->net_stats.rx_fifo_errors++; + goto resubmit; } -static u16 get_tx_index(u8 port, u32 status, u16 len) +/* Transmit ring index in reported status block is encoded as: + * + * | TXS2 | TXA2 | TXS1 | TXA1 + */ +static inline u16 tx_index(u8 port, u32 status, u16 len) { if (port == 0) return status & 0xfff; @@ -1411,10 +1488,8 @@ static u16 get_tx_index(u8 port, u32 status, u16 len) } /* - * NAPI poll routine. * Both ports share the same status interrupt, therefore there is only * one poll routine. - * */ static int sky2_poll(struct net_device *dev, int *budget) { @@ -1422,31 +1497,27 @@ static int sky2_poll(struct net_device *dev, int *budget) struct sky2_hw *hw = sky2->hw; unsigned int to_do = min(dev->quota, *budget); unsigned int work_done = 0; + u16 hwidx; unsigned char summed[2] = { CHECKSUM_NONE, CHECKSUM_NONE }; - unsigned int csum[2] = { 0 }; - unsigned int rx_handled[2] = { 0, 0}; - u16 last; - - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - last = sky2_read16(hw, STAT_PUT_IDX); + unsigned int csum[2]; - while (hw->st_idx != last && work_done < to_do) { + hwidx = sky2_read16(hw, STAT_PUT_IDX); + rmb(); + while (hw->st_idx != hwidx && work_done < to_do) { struct sky2_status_le *le = hw->st_le + hw->st_idx; struct sk_buff *skb; u8 port; u32 status; u16 length; - rmb(); status = le32_to_cpu(le->status); length = le16_to_cpu(le->length); port = le->link; - BUG_ON(port >= hw->ports); + BUG_ON(port >= hw->ports || hw->dev[port] == NULL); - switch(le->opcode & ~HW_OWNER) { + switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: - ++rx_handled[port]; skb = sky2_receive(hw, port, length, status); if (likely(skb)) { __skb_put(skb, length); @@ -1475,7 +1546,7 @@ static int sky2_poll(struct net_device *dev, int *budget) case OP_TXINDEXLE: sky2_tx_complete(hw->dev[port], - get_tx_index(port, status, length)); + tx_index(port, status, length)); break; case OP_RXTIMESTAMP: @@ -1483,14 +1554,27 @@ static int sky2_poll(struct net_device *dev, int *budget) default: if (net_ratelimit()) - printk(KERN_WARNING PFX "unknown status opcode 0x%x\n", + printk(KERN_WARNING PFX + "unknown status opcode 0x%x\n", le->opcode); break; } - hw->st_idx = (hw->st_idx + 1) & (STATUS_RING_SIZE -1); + hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; + if (hw->st_idx == hwidx) { + hwidx = sky2_read16(hw, STAT_PUT_IDX); + rmb(); + } } + mmiowb(); + + if (hw->dev[0]) + rx_set_put(hw->dev[0]); + + if (hw->dev[1]) + rx_set_put(hw->dev[1]); + *budget -= work_done; dev->quota -= work_done; if (work_done < to_do) { @@ -1505,6 +1589,7 @@ static int sky2_poll(struct net_device *dev, int *budget) hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); + sky2_read32(hw, B0_IMSK); netif_rx_complete(dev); } @@ -1553,40 +1638,35 @@ static void sky2_hw_intr(struct sky2_hw *hw) { u32 status = sky2_read32(hw, B0_HWE_ISRC); - if (status & Y2_IS_TIST_OV) { - pr_debug (PFX "%s: unused timer overflow??\n", - pci_name(hw->pdev)); + if (status & Y2_IS_TIST_OV) sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); - } if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) { - u16 pci_err = sky2_read16(hw, PCI_C(PCI_STATUS)); + u16 pci_err; + + pci_read_config_word(hw->pdev, PCI_STATUS, &pci_err); printk(KERN_ERR PFX "%s: pci hw error (0x%x)\n", pci_name(hw->pdev), pci_err); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - sky2_write16(hw, PCI_C(PCI_STATUS), - pci_err | PCI_STATUS_ERROR_BITS); + pci_write_config_word(hw->pdev, PCI_STATUS, + pci_err | PCI_STATUS_ERROR_BITS); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); } if (status & Y2_IS_PCI_EXP) { /* PCI-Express uncorrectable Error occured */ - u32 pex_err = sky2_read32(hw, PCI_C(PEX_UNC_ERR_STAT)); + u32 pex_err; + + pci_read_config_dword(hw->pdev, PEX_UNC_ERR_STAT, &pex_err); - /* - * On PCI-Express bus bridges are called root complexes. - * PCI-Express errors are recognized by the root complex too, - * which requests the system to handle the problem. After error - * occurence it may be that no access to the adapter may be performed - * any longer. - */ printk(KERN_ERR PFX "%s: pci express error (0x%x)\n", pci_name(hw->pdev), pex_err); /* clear the interrupt */ sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - sky2_write32(hw, PCI_C(PEX_UNC_ERR_STAT), 0xffffffffUL); + pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT, + 0xffffffffUL); sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); if (pex_err & PEX_FATAL_ERRORS) { @@ -1622,7 +1702,6 @@ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port) ++sky2->net_stats.tx_fifo_errors; sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU); } - } static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) @@ -1641,19 +1720,22 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) u32 status; status = sky2_read32(hw, B0_Y2_SP_ISRC2); - if (status == 0 || status == ~0) /* hotplug or shared irq */ + if (status == 0 || status == ~0) return IRQ_NONE; if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); - if ((status & Y2_IS_STAT_BMU) && netif_rx_schedule_prep(hw->dev[0])) { + /* Do NAPI for Rx and Tx status */ + if ((status & Y2_IS_STAT_BMU) && netif_rx_schedule_test(hw->dev[0])) { + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + hw->intr_mask &= ~Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); __netif_rx_schedule(hw->dev[0]); } - if (status & Y2_IS_IRQ_PHY1) + if (status & Y2_IS_IRQ_PHY1) sky2_phy_intr(hw, 0); if (status & Y2_IS_IRQ_PHY2) @@ -1665,8 +1747,10 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) if (status & Y2_IS_IRQ_MAC2) sky2_mac_intr(hw, 1); - sky2_write32(hw, B0_Y2_SP_ICR, 2); + + sky2_read32(hw, B0_IMSK); + return IRQ_HANDLED; } @@ -1675,21 +1759,19 @@ static void sky2_netpoll(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); - disable_irq(dev->irq); - sky2_intr(dev->irq, sky2->hw, NULL); - enable_irq(dev->irq); + sky2_intr(sky2->hw->pdev->irq, sky2->hw, NULL); } #endif /* Chip internal frequency for clock calculations */ static inline u32 sky2_khz(const struct sky2_hw *hw) { - switch(hw->chip_id) { + switch (hw->chip_id) { case CHIP_ID_YUKON_EC: return 125000; /* 125 Mhz */ case CHIP_ID_YUKON_FE: return 100000; /* 100 Mhz */ - default: /* YUKON_XL */ + default: /* YUKON_XL */ return 156000; /* 156 Mhz */ } } @@ -1701,7 +1783,7 @@ static inline u32 sky2_ms2clk(const struct sky2_hw *hw, u32 ms) static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) { - return (sky2_khz(hw) * 75) / 1000; + return (sky2_khz(hw) * us) / 1000; } static int sky2_reset(struct sky2_hw *hw) @@ -1721,6 +1803,12 @@ static int sky2_reset(struct sky2_hw *hw) return -EOPNOTSUPP; } + /* ring for status responses */ + hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES, + &hw->st_dma); + if (!hw->st_le) + return -ENOMEM; + /* disable ASF */ if (hw->chip_id <= CHIP_ID_YUKON_EC) { sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); @@ -1732,17 +1820,19 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, B0_CTST, CS_RST_CLR); /* clear PCI errors, if any */ - status = sky2_read16(hw, PCI_C(PCI_STATUS)); + pci_read_config_word(hw->pdev, PCI_STATUS, &status); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - sky2_write16(hw, PCI_C(PCI_STATUS), - status | PCI_STATUS_ERROR_BITS); + pci_write_config_word(hw->pdev, PCI_STATUS, + status | PCI_STATUS_ERROR_BITS); sky2_write8(hw, B0_CTST, CS_MRST_CLR); /* clear any PEX errors */ if (is_pciex(hw)) { - sky2_write32(hw, PCI_C(PEX_UNC_ERR_STAT), 0xffffffffUL); - sky2_read16(hw, PCI_C(PEX_LNK_STAT)); + u16 lstat; + pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT, + 0xffffffffUL); + pci_read_config_word(hw->pdev, PEX_LNK_STAT, &lstat); } pmd_type = sky2_read8(hw, B2_PMD_TYP); @@ -1769,20 +1859,20 @@ static int sky2_reset(struct sky2_hw *hw) Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); - else - sky2_write8(hw, B2_Y2_CLK_GATE, 0); + else + sky2_write8(hw, B2_Y2_CLK_GATE, 0); /* Turn off phy power saving */ - power = sky2_read32(hw, PCI_C(PCI_DEV_REG1)); - power &= ~(PCI_Y2_PHY1_POWD|PCI_Y2_PHY2_POWD); + pci_read_config_dword(hw->pdev, PCI_DEV_REG1, &power); + power &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); - /* back asswards .. */ + /* looks like this xl is back asswards .. */ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { power |= PCI_Y2_PHY1_COMA; if (hw->ports > 1) power |= PCI_Y2_PHY2_COMA; } - sky2_write32(hw, PCI_C(PCI_DEV_REG1), power); + pci_write_config_dword(hw->pdev, PCI_DEV_REG1, power); for (i = 0; i < hw->ports; i++) { sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); @@ -1791,20 +1881,22 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); - sky2_write32(hw, B2_I2C_IRQ, 1); /* Clear I2C IRQ noise */ + /* Clear I2C IRQ noise */ + sky2_write32(hw, B2_I2C_IRQ, 1); /* turn off hardware timer (unused) */ sky2_write8(hw, B2_TI_CTRL, TIM_STOP); sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ); - + sky2_write8(hw, B0_Y2LED, LED_STAT_ON); - /* Turn on descriptor polling -- is this necessary? */ + /* Turn on descriptor polling (every 75us) */ sky2_write32(hw, B28_DPT_INI, sky2_us2clk(hw, 75)); sky2_write8(hw, B28_DPT_CTRL, DPT_START); /* Turn off receive timestamp */ sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP); + sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); /* enable the Tx Arbiters */ for (i = 0; i < hw->ports; i++) @@ -1812,7 +1904,7 @@ static int sky2_reset(struct sky2_hw *hw) /* Initialize ram interface */ for (i = 0; i < hw->ports; i++) { - sky2_write16(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR); + sky2_write8(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR); sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R1), SK_RI_TO_53); sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA1), SK_RI_TO_53); @@ -1828,35 +1920,27 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53); } - /* Optimize PCI Express access */ if (is_pciex(hw)) { - u16 ctrl = sky2_read32(hw, PCI_C(PEX_DEV_CTRL)); - ctrl &= ~PEX_DC_MAX_RRS_MSK; - ctrl |= PEX_DC_MAX_RD_RQ_SIZE(4); + u16 pctrl; + + /* change Max. Read Request Size to 2048 bytes */ + pci_read_config_word(hw->pdev, PEX_DEV_CTRL, &pctrl); + pctrl &= ~PEX_DC_MAX_RRS_MSK; + pctrl |= PEX_DC_MAX_RD_RQ_SIZE(4); + + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - sky2_write16(hw, PCI_C(PEX_DEV_CTRL), ctrl); + pci_write_config_word(hw->pdev, PEX_DEV_CTRL, pctrl); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); } sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); - hw->intr_mask = Y2_IS_BASE; - sky2_write32(hw, B0_IMSK, hw->intr_mask); - - /* disable all GMAC IRQ's */ - sky2_write8(hw, GMAC_IRQ_MSK, 0); - spin_lock_bh(&hw->phy_lock); for (i = 0; i < hw->ports; i++) sky2_phy_reset(hw, i); spin_unlock_bh(&hw->phy_lock); - /* Setup ring for status responses */ - hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES, - &hw->st_dma); - if (!hw->st_le) - return -ENOMEM; - memset(hw->st_le, 0, STATUS_LE_BYTES); hw->st_idx = 0; @@ -1864,30 +1948,25 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write32(hw, STAT_CTRL, SC_STAT_RST_CLR); sky2_write32(hw, STAT_LIST_ADDR_LO, hw->st_dma); - sky2_write32(hw, STAT_LIST_ADDR_HI, (u64)hw->st_dma >> 32); + sky2_write32(hw, STAT_LIST_ADDR_HI, (u64) hw->st_dma >> 32); /* Set the list last index */ - sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE-1); + sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1); + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_ms2clk(hw, 10)); + + /* 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, ST_TXTH_IDX_MASK); + sky2_write16(hw, STAT_TX_IDX_TH, 0xfff); /* Tx Threshold */ /* set Status-FIFO watermark */ sky2_write8(hw, STAT_FIFO_WM, 0x21); /* WA for dev. #4.18 */ /* set Status-FIFO ISR watermark */ - sky2_write8(hw, STAT_FIFO_ISR_WM, 0x07);/* WA for dev. #4.18 */ + sky2_write8(hw, STAT_FIFO_ISR_WM, 0x07); /* WA for dev. #4.18 */ - /* WA for dev. #4.3 and #4.18 */ - /* set Status-FIFO Tx timer init value */ - sky2_write32(hw, STAT_TX_TIMER_INI, sky2_ms2clk(hw, 10)); } else { - /* - * Theses settings should avoid the - * temporary hanging of the status BMU. - * May be not all required... still under investigation... - */ sky2_write16(hw, STAT_TX_IDX_TH, 0x000a); /* set Status-FIFO watermark */ @@ -1897,14 +1976,13 @@ static int sky2_reset(struct sky2_hw *hw) if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0) sky2_write8(hw, STAT_FIFO_ISR_WM, 0x10); - else /* WA 4109 */ + else /* WA 4109 */ sky2_write8(hw, STAT_FIFO_ISR_WM, 0x04); sky2_write32(hw, STAT_ISR_TIMER_INI, 0x0190); } - /* enable the prefetch unit */ - /* operational bit not functional for Yukon-EC, but fixed in Yukon-2? */ + /* enable status unit */ sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON); sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); @@ -1918,23 +1996,22 @@ static inline u32 sky2_supported_modes(const struct sky2_hw *hw) { u32 modes; if (hw->copper) { - modes = SUPPORTED_10baseT_Half - | SUPPORTED_10baseT_Full - | SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full - | SUPPORTED_Autoneg| SUPPORTED_TP; + modes = SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_TP; if (hw->chip_id != CHIP_ID_YUKON_FE) modes |= SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full; + | SUPPORTED_1000baseT_Full; } else modes = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE - | SUPPORTED_Autoneg; + | SUPPORTED_Autoneg; return modes; } -static int sky2_get_settings(struct net_device *dev, - struct ethtool_cmd *ecmd) +static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; @@ -1944,13 +2021,12 @@ static int sky2_get_settings(struct net_device *dev, ecmd->phy_address = PHY_ADDR_MARV; if (hw->copper) { ecmd->supported = SUPPORTED_10baseT_Half - - | SUPPORTED_10baseT_Full - | SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full - | SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg| SUPPORTED_TP; + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_TP; ecmd->port = PORT_TP; } else ecmd->port = PORT_FIBRE; @@ -1975,7 +2051,7 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) } else { u32 setting; - switch(ecmd->speed) { + switch (ecmd->speed) { case SPEED_1000: if (ecmd->duplex == DUPLEX_FULL) setting = SUPPORTED_1000baseT_Full; @@ -2035,8 +2111,8 @@ static void sky2_get_drvinfo(struct net_device *dev, } static const struct sky2_stat { - char name[ETH_GSTRING_LEN]; - u16 offset; + char name[ETH_GSTRING_LEN]; + u16 offset; } sky2_stats[] = { { "tx_bytes", GM_TXO_OK_HI }, { "rx_bytes", GM_RXO_OK_HI }, @@ -2061,7 +2137,6 @@ static const struct sky2_stat { { "rx_fcs_error", GM_RXF_FCS_ERR }, }; - static u32 sky2_get_rx_csum(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); @@ -2074,6 +2149,7 @@ static int sky2_set_rx_csum(struct net_device *dev, u32 data) struct sky2_port *sky2 = netdev_priv(dev); sky2->rx_csum = data; + sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR), data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); @@ -2086,22 +2162,21 @@ static u32 sky2_get_msglevel(struct net_device *netdev) return sky2->msg_enable; } -static void sky2_phy_stats(struct sky2_port *sky2, u64 *data) +static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count) { struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; int i; data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32 - | (u64) gma_read32(hw, port, GM_TXO_OK_LO); + | (u64) gma_read32(hw, port, GM_TXO_OK_LO); data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32 - | (u64) gma_read32(hw, port, GM_RXO_OK_LO); + | (u64) gma_read32(hw, port, GM_RXO_OK_LO); - for (i = 2; i < ARRAY_SIZE(sky2_stats); i++) + for (i = 2; i < count; i++) data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset); } - static void sky2_set_msglevel(struct net_device *netdev, u32 value) { struct sky2_port *sky2 = netdev_priv(netdev); @@ -2114,14 +2189,14 @@ static int sky2_get_stats_count(struct net_device *dev) } static void sky2_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) + struct ethtool_stats *stats, u64 * data) { struct sky2_port *sky2 = netdev_priv(dev); - sky2_phy_stats(sky2, data); + sky2_phy_stats(sky2, data, ARRAY_SIZE(sky2_stats)); } -static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 *data) +static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 * data) { int i; @@ -2141,9 +2216,9 @@ static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 *data) static struct net_device_stats *sky2_get_stats(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); - u64 data[ARRAY_SIZE(sky2_stats)]; + u64 data[13]; - sky2_phy_stats(sky2, data); + sky2_phy_stats(sky2, data, ARRAY_SIZE(data)); sky2->net_stats.tx_bytes = data[0]; sky2->net_stats.rx_bytes = data[1]; @@ -2167,9 +2242,9 @@ static int sky2_set_mac_address(struct net_device *dev, void *p) sky2_down(dev); memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); - memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port*8, + memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port * 8, dev->dev_addr, ETH_ALEN); - memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port*8, + memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port * 8, dev->dev_addr, ETH_ALEN); if (dev->flags & IFF_UP) err = sky2_up(dev); @@ -2190,11 +2265,11 @@ static void sky2_set_multicast(struct net_device *dev) reg = gma_read16(hw, port, GM_RX_CTRL); reg |= GM_RXCR_UCF_ENA; - if (dev->flags & IFF_PROMISC) /* promiscious */ + if (dev->flags & IFF_PROMISC) /* promiscious */ reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); - else if (dev->flags & IFF_ALLMULTI) /* all multicast */ + else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > 16) /* all multicast */ memset(filter, 0xff, sizeof(filter)); - else if (dev->mc_count == 0) /* no multicast */ + else if (dev->mc_count == 0) /* no multicast */ reg &= ~GM_RXCR_MCF_ENA; else { int i; @@ -2202,19 +2277,18 @@ static void sky2_set_multicast(struct net_device *dev) for (i = 0; list && i < dev->mc_count; i++, list = list->next) { u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f; - filter[bit/8] |= 1 << (bit%8); + filter[bit / 8] |= 1 << (bit % 8); } } - gma_write16(hw, port, GM_MC_ADDR_H1, - (u16)filter[0] | ((u16)filter[1] << 8)); + (u16) filter[0] | ((u16) filter[1] << 8)); gma_write16(hw, port, GM_MC_ADDR_H2, - (u16)filter[2] | ((u16)filter[3] << 8)); + (u16) filter[2] | ((u16) filter[3] << 8)); gma_write16(hw, port, GM_MC_ADDR_H3, - (u16)filter[4] | ((u16)filter[5] << 8)); + (u16) filter[4] | ((u16) filter[5] << 8)); gma_write16(hw, port, GM_MC_ADDR_H4, - (u16)filter[6] | ((u16)filter[7] << 8)); + (u16) filter[6] | ((u16) filter[7] << 8)); gma_write16(hw, port, GM_RX_CTRL, reg); } @@ -2224,24 +2298,38 @@ static void sky2_set_multicast(struct net_device *dev) */ static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) { + u16 pg; + spin_lock_bh(&hw->phy_lock); - gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0); - if (on) + switch (hw->chip_id) { + case CHIP_ID_YUKON_XL: + pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, + on ? (PHY_M_LEDC_LOS_CTRL(1) | + PHY_M_LEDC_INIT_CTRL(7) | + PHY_M_LEDC_STA1_CTRL(7) | + PHY_M_LEDC_STA0_CTRL(7)) + : 0); + + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + break; + + default: + gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0); gm_phy_write(hw, port, PHY_MARV_LED_OVER, - PHY_M_LED_MO_DUP(MO_LED_ON) | - PHY_M_LED_MO_10(MO_LED_ON) | - PHY_M_LED_MO_100(MO_LED_ON) | + on ? PHY_M_LED_MO_DUP(MO_LED_ON) | + PHY_M_LED_MO_10(MO_LED_ON) | + PHY_M_LED_MO_100(MO_LED_ON) | PHY_M_LED_MO_1000(MO_LED_ON) | - PHY_M_LED_MO_RX(MO_LED_ON)); - else - gm_phy_write(hw, port, PHY_MARV_LED_OVER, - - PHY_M_LED_MO_DUP(MO_LED_OFF) | - PHY_M_LED_MO_10(MO_LED_OFF) | - PHY_M_LED_MO_100(MO_LED_OFF) | + PHY_M_LED_MO_RX(MO_LED_ON) + : PHY_M_LED_MO_DUP(MO_LED_OFF) | + PHY_M_LED_MO_10(MO_LED_OFF) | + PHY_M_LED_MO_100(MO_LED_OFF) | PHY_M_LED_MO_1000(MO_LED_OFF) | PHY_M_LED_MO_RX(MO_LED_OFF)); + } spin_unlock_bh(&hw->phy_lock); } @@ -2251,19 +2339,26 @@ static int sky2_phys_id(struct net_device *dev, u32 data) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; - u16 ledctrl, ledover; + u16 ledctrl, ledover = 0; long ms; int onoff = 1; - if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) + if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ)) ms = jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT); else ms = data * 1000; /* save initial values */ spin_lock_bh(&hw->phy_lock); - ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); - ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); + 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); + ledctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + } else { + ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); + ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); + } spin_unlock_bh(&hw->phy_lock); while (ms > 0) { @@ -2277,8 +2372,15 @@ static int sky2_phys_id(struct net_device *dev, u32 data) /* resume regularly scheduled programming */ spin_lock_bh(&hw->phy_lock); - gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); - gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + 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); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ledctrl); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + } else { + gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); + gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + } spin_unlock_bh(&hw->phy_lock); return 0; @@ -2344,30 +2446,102 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } #endif +static void sky2_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct sky2_port *sky2 = netdev_priv(dev); + + ering->rx_max_pending = RX_MAX_PENDING; + ering->rx_mini_max_pending = 0; + ering->rx_jumbo_max_pending = 0; + ering->tx_max_pending = TX_RING_SIZE - 1; + + ering->rx_pending = sky2->rx_pending; + ering->rx_mini_pending = 0; + ering->rx_jumbo_pending = 0; + ering->tx_pending = sky2->tx_pending; +} + +static int sky2_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct sky2_port *sky2 = netdev_priv(dev); + int err = 0; + + if (ering->rx_pending > RX_MAX_PENDING || + ering->rx_pending < 8 || + ering->tx_pending < MAX_SKB_TX_LE || + ering->tx_pending > TX_RING_SIZE - 1) + return -EINVAL; + + if (netif_running(dev)) + sky2_down(dev); + + sky2->rx_pending = ering->rx_pending; + sky2->tx_pending = ering->tx_pending; + + if (netif_running(dev)) + err = sky2_up(dev); + + return err; +} + +#define SKY2_REGS_LEN 0x1000 +static int sky2_get_regs_len(struct net_device *dev) +{ + return SKY2_REGS_LEN; +} + +/* + * Returns copy of control register region + * I/O region is divided into banks and certain regions are unreadable + */ +static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + const struct sky2_port *sky2 = netdev_priv(dev); + unsigned long offs; + const void __iomem *io = sky2->hw->regs; + static const unsigned long bankmap = 0xfff3f305; + + regs->version = 1; + for (offs = 0; offs < regs->len; offs += 128) { + u32 len = min_t(u32, 128, regs->len - offs); + + if (bankmap & (1 << (offs / 128))) + memcpy_fromio(p + offs, io + offs, len); + else + memset(p + offs, 0, len); + } +} static struct ethtool_ops sky2_ethtool_ops = { - .get_settings = sky2_get_settings, - .set_settings = sky2_set_settings, - .get_drvinfo = sky2_get_drvinfo, - .get_msglevel = sky2_get_msglevel, - .set_msglevel = sky2_set_msglevel, - .get_link = ethtool_op_get_link, - .get_sg = ethtool_op_get_sg, - .set_sg = ethtool_op_set_sg, - .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = ethtool_op_set_tx_csum, - .get_tso = ethtool_op_get_tso, - .set_tso = ethtool_op_set_tso, - .get_rx_csum = sky2_get_rx_csum, - .set_rx_csum = sky2_set_rx_csum, - .get_strings = sky2_get_strings, + .get_settings = sky2_get_settings, + .set_settings = sky2_set_settings, + .get_drvinfo = sky2_get_drvinfo, + .get_msglevel = sky2_get_msglevel, + .set_msglevel = sky2_set_msglevel, + .get_regs_len = sky2_get_regs_len, + .get_regs = sky2_get_regs, + .get_link = ethtool_op_get_link, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, + .get_rx_csum = sky2_get_rx_csum, + .set_rx_csum = sky2_set_rx_csum, + .get_strings = sky2_get_strings, + .get_ringparam = sky2_get_ringparam, + .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, + .get_wol = sky2_get_wol, + .set_wol = sky2_set_wol, #endif - .phys_id = sky2_phys_id, + .phys_id = sky2_phys_id, .get_stats_count = sky2_get_stats_count, .get_ethtool_stats = sky2_get_ethtool_stats, }; @@ -2402,7 +2576,6 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = sky2_netpoll; #endif - dev->irq = hw->pdev->irq; sky2 = netdev_priv(dev); sky2->netdev = dev; @@ -2418,20 +2591,21 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, sky2->speed = -1; sky2->advertising = sky2_supported_modes(hw); sky2->rx_csum = 1; - sky2->rx_ring_size = is_ec_a1(hw) ? MIN_RX_BUFFERS : MAX_RX_BUFFERS; - tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long) sky2); + tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2); + sky2->tx_pending = TX_DEF_PENDING; + sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING; hw->dev[port] = dev; sky2->port = port; - dev->features |= NETIF_F_LLTX; + dev->features |= NETIF_F_LLTX | NETIF_F_TSO; if (highmem) dev->features |= NETIF_F_HIGHDMA; - dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO; + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; /* read the mac address */ - memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port*8, ETH_ALEN); + memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); /* device is off until link detection */ netif_carrier_off(dev); @@ -2454,20 +2628,22 @@ static inline void sky2_show_addr(struct net_device *dev) static int __devinit sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - struct net_device *dev, *dev1; + struct net_device *dev, *dev1 = NULL; struct sky2_hw *hw; int err, using_dac = 0; - if ((err = pci_enable_device(pdev))) { + err = pci_enable_device(pdev); + if (err) { printk(KERN_ERR PFX "%s cannot enable PCI device\n", pci_name(pdev)); goto err_out; } - if ((err = pci_request_regions(pdev, DRV_NAME))) { + err = pci_request_regions(pdev, DRV_NAME); + if (err) { printk(KERN_ERR PFX "%s cannot obtain PCI resources\n", pci_name(pdev)); - goto err_out_disable_pdev; + goto err_out; } pci_set_master(pdev); @@ -2486,7 +2662,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out_free_regions; } } - #ifdef __BIG_ENDIAN /* byte swap decriptors in hardware */ { @@ -2517,26 +2692,21 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out_free_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_iounmap; - } - pci_set_drvdata(pdev, hw); - err = sky2_reset(hw); if (err) - goto err_out_free_irq; + goto err_out_iounmap; - printk(KERN_INFO PFX "addr 0x%lx irq %d chip 0x%x (%s) rev %d\n", + printk(KERN_INFO PFX "addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n", pci_resource_start(pdev, 0), pdev->irq, - hw->chip_id, chip_name(hw->chip_id), hw->chip_rev); + yukon_name[hw->chip_id - CHIP_ID_YUKON], + hw->chip_id, hw->chip_rev); - if ((dev = sky2_init_netdev(hw, 0, using_dac)) == NULL) + dev = sky2_init_netdev(hw, 0, using_dac); + if (!dev) goto err_out_free_pci; - if ((err = register_netdev(dev))) { + err = register_netdev(dev); + if (err) { printk(KERN_ERR PFX "%s: cannot register net device\n", pci_name(pdev)); goto err_out_free_netdev; @@ -2549,20 +2719,37 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_show_addr(dev1); else { /* Failure to register second port need not be fatal */ - printk(KERN_WARNING PFX "register of second port failed\n"); + printk(KERN_WARNING PFX + "register of second port failed\n"); hw->dev[1] = NULL; free_netdev(dev1); } } + 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; + } + + hw->intr_mask = Y2_IS_BASE; + sky2_write32(hw, B0_IMSK, hw->intr_mask); + + pci_set_drvdata(pdev, hw); + return 0; +err_out_unregister: + if (dev1) { + unregister_netdev(dev1); + free_netdev(dev1); + } + unregister_netdev(dev); err_out_free_netdev: free_netdev(dev); - -err_out_free_irq: - free_irq(pdev->irq, hw); err_out_free_pci: + sky2_write8(hw, B0_CTST, CS_RST_SET); pci_free_consistent(hw->pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); err_out_iounmap: iounmap(hw->regs); @@ -2570,33 +2757,34 @@ err_out_free_hw: kfree(hw); err_out_free_regions: pci_release_regions(pdev); -err_out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_out: return err; } static void __devexit sky2_remove(struct pci_dev *pdev) { - struct sky2_hw *hw = pci_get_drvdata(pdev); + struct sky2_hw *hw = pci_get_drvdata(pdev); struct net_device *dev0, *dev1; - if(!hw) + if (!hw) return; - if ((dev1 = hw->dev[1])) - unregister_netdev(dev1); dev0 = hw->dev[0]; + dev1 = hw->dev[1]; + if (dev1) + unregister_netdev(dev1); unregister_netdev(dev0); + sky2_write32(hw, B0_IMSK, 0); sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); + sky2_write8(hw, B0_CTST, CS_RST_SET); free_irq(pdev->irq, hw); - pci_free_consistent(pdev, STATUS_LE_BYTES, - hw->st_le, hw->st_dma); + pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); pci_release_regions(pdev); pci_disable_device(pdev); + if (dev1) free_netdev(dev1); free_netdev(dev0); @@ -2608,7 +2796,7 @@ static void __devexit sky2_remove(struct pci_dev *pdev) #ifdef CONFIG_PM static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) { - struct sky2_hw *hw = pci_get_drvdata(pdev); + struct sky2_hw *hw = pci_get_drvdata(pdev); int i, wol = 0; for (i = 0; i < 2; i++) { @@ -2635,7 +2823,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) static int sky2_resume(struct pci_dev *pdev) { - struct sky2_hw *hw = pci_get_drvdata(pdev); + struct sky2_hw *hw = pci_get_drvdata(pdev); int i; pci_set_power_state(pdev, PCI_D0); @@ -2657,19 +2845,18 @@ static int sky2_resume(struct pci_dev *pdev) #endif static struct pci_driver sky2_driver = { - .name = DRV_NAME, - .id_table = sky2_id_table, - .probe = sky2_probe, - .remove = __devexit_p(sky2_remove), + .name = DRV_NAME, + .id_table = sky2_id_table, + .probe = sky2_probe, + .remove = __devexit_p(sky2_remove), #ifdef CONFIG_PM - .suspend = sky2_suspend, - .resume = sky2_resume, + .suspend = sky2_suspend, + .resume = sky2_resume, #endif }; static int __init sky2_init_module(void) { - return pci_module_init(&sky2_driver); } -- cgit v1.2.3 From 79e57d32fe6d2d5bd38378f6dccfdbe6bd1d1dab Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 19 Sep 2005 15:42:33 -0700 Subject: [PATCH] sky2: fix FIFO DMA alignment problems The Yukon2 chip FIFO has a problem recovering from a pause frame if the receive buffer is not aligned. The workaround is to just leave receive buffer at original alignment, which will make IP header unaligned. Rework receive logic for clarity. Need to ignore receive overrun interrupts (or mac hangs). These fixes make the sky2 driver generally usable. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 38 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index bc95aacab20..c057694cf04 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -76,7 +76,7 @@ #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) #define RX_MAX_PENDING (RX_LE_SIZE/2 - 1) #define RX_DEF_PENDING 128 -#define RX_COPY_THRESHOLD 128 +#define RX_COPY_THRESHOLD 256 #define TX_RING_SIZE 512 #define TX_DEF_PENDING (TX_RING_SIZE - 1) @@ -692,18 +692,10 @@ static void sky2_rx_clean(struct sky2_port *sky2) } } -static inline struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, - unsigned int size, - unsigned int gfp_mask) +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +static inline unsigned sky2_rx_size(const struct sky2_port *sky2) { - struct sk_buff *skb; - - skb = alloc_skb(size + NET_IP_ALIGN, gfp_mask); - if (likely(skb)) { - skb->dev = sky2->netdev; - skb_reserve(skb, NET_IP_ALIGN); - } - return skb; + return roundup(sky2->netdev->mtu + ETH_HLEN + 4, 8); } /* @@ -711,22 +703,28 @@ static inline struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, * In case of 64 bit dma, there are 2X as many list elements * available as ring entries * and need to reserve one list element so we don't wrap around. + * + * It appears the hardware has a bug in the FIFO logic that + * cause it to hang if the FIFO gets overrun and the receive buffer + * is not aligned. This means we can't use skb_reserve to align + * the IP header. */ static int sky2_rx_fill(struct sky2_port *sky2) { unsigned i; - const unsigned rx_buf_size = sky2->netdev->mtu + ETH_HLEN + 8; + unsigned size = sky2_rx_size(sky2); + pr_debug("rx_fill size=%d\n", size); for (i = 0; i < sky2->rx_pending; i++) { struct ring_info *re = sky2->rx_ring + i; - re->skb = sky2_rx_alloc(sky2, rx_buf_size, GFP_KERNEL); + re->skb = dev_alloc_skb(size); if (!re->skb) goto nomem; re->mapaddr = pci_map_single(sky2->hw->pdev, re->skb->data, - rx_buf_size, PCI_DMA_FROMDEVICE); - re->maplen = rx_buf_size; + size, PCI_DMA_FROMDEVICE); + re->maplen = size; sky2_rx_add(sky2, re); } @@ -1408,8 +1406,8 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, struct net_device *dev = hw->dev[port]; struct sky2_port *sky2 = netdev_priv(dev); struct ring_info *re = sky2->rx_ring + sky2->rx_next; - struct sk_buff *skb, *nskb; - const unsigned int rx_buf_size = dev->mtu + ETH_HLEN + 8; + struct sk_buff *skb = NULL; + const unsigned int bufsize = sky2_rx_size(sky2); if (unlikely(netif_msg_rx_status(sky2))) printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", @@ -1417,43 +1415,49 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; - skb = NULL; if (!(status & GMR_FS_RX_OK) || (status & GMR_FS_ANY_ERR) || (length << 16) != (status & GMR_FS_LEN) - || length > rx_buf_size) + || length > bufsize) goto error; if (length < RX_COPY_THRESHOLD) { - nskb = sky2_rx_alloc(sky2, length, GFP_ATOMIC); - if (!nskb) + skb = alloc_skb(length + 2, GFP_ATOMIC); + if (!skb) goto resubmit; + skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->mapaddr, length, PCI_DMA_FROMDEVICE); - memcpy(nskb->data, re->skb->data, length); + memcpy(skb->data, re->skb->data, length); pci_dma_sync_single_for_device(sky2->hw->pdev, re->mapaddr, length, PCI_DMA_FROMDEVICE); - skb = nskb; } else { - nskb = sky2_rx_alloc(sky2, rx_buf_size, GFP_ATOMIC); + struct sk_buff *nskb; + + nskb = dev_alloc_skb(bufsize); if (!nskb) goto resubmit; skb = re->skb; + re->skb = nskb; pci_unmap_single(sky2->hw->pdev, re->mapaddr, re->maplen, PCI_DMA_FROMDEVICE); prefetch(skb->data); - re->skb = nskb; re->mapaddr = pci_map_single(sky2->hw->pdev, nskb->data, - rx_buf_size, PCI_DMA_FROMDEVICE); - re->maplen = rx_buf_size; + bufsize, PCI_DMA_FROMDEVICE); + re->maplen = bufsize; } + skb->dev = dev; + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + resubmit: - BUG_ON(re->skb == skb); sky2_rx_add(sky2, re); + return skb; error: @@ -1472,6 +1476,7 @@ error: sky2->net_stats.rx_crc_errors++; if (status & GMR_FS_RX_FF_OV) sky2->net_stats.rx_fifo_errors++; + goto resubmit; } @@ -1502,6 +1507,7 @@ static int sky2_poll(struct net_device *dev, int *budget) unsigned int csum[2]; hwidx = sky2_read16(hw, STAT_PUT_IDX); + BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); while (hw->st_idx != hwidx && work_done < to_do) { struct sky2_status_le *le = hw->st_le + hw->st_idx; @@ -1520,22 +1526,18 @@ static int sky2_poll(struct net_device *dev, int *budget) case OP_RXSTAT: skb = sky2_receive(hw, port, length, status); if (likely(skb)) { - __skb_put(skb, length); - skb->protocol = eth_type_trans(skb, dev); - /* Add hw checksum if available */ skb->ip_summed = summed[port]; skb->csum = csum[port]; - /* Clear for next packet */ - csum[port] = 0; - summed[port] = CHECKSUM_NONE; - netif_receive_skb(skb); - - dev->last_rx = jiffies; ++work_done; } + + /* Clear for next packet */ + csum[port] = 0; + summed[port] = CHECKSUM_NONE; + break; case OP_RXCHKS: -- cgit v1.2.3 From 6e4cbb34d4cabec48f25f0bbad68935221bd457b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 19 Sep 2005 15:47:57 -0700 Subject: [PATCH] sky2: allow ethtool debug access to all of PCI space For debugging, it is useful to allow ethtool to dump all of the PCI mapped space. There is one small set of registers for RAM diagnostics that needs to be avoided to prevent hanging, but all the rest is accessible. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index c057694cf04..1acca982ac1 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2488,33 +2488,30 @@ static int sky2_set_ringparam(struct net_device *dev, return err; } -#define SKY2_REGS_LEN 0x1000 static int sky2_get_regs_len(struct net_device *dev) { - return SKY2_REGS_LEN; + return 0x4000; } /* * Returns copy of control register region - * I/O region is divided into banks and certain regions are unreadable + * Note: access to the RAM address register set will cause timeouts. */ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { const struct sky2_port *sky2 = netdev_priv(dev); - unsigned long offs; const void __iomem *io = sky2->hw->regs; - static const unsigned long bankmap = 0xfff3f305; + BUG_ON(regs->len < B3_RI_WTO_R1); regs->version = 1; - for (offs = 0; offs < regs->len; offs += 128) { - u32 len = min_t(u32, 128, regs->len - offs); + memset(p, 0, regs->len); - if (bankmap & (1 << (offs / 128))) - memcpy_fromio(p + offs, io + offs, len); - else - memset(p + offs, 0, len); - } + memcpy_fromio(p, io, B3_RAM_ADDR); + + memcpy_fromio(p + B3_RI_WTO_R1, + io + B3_RI_WTO_R1, + regs->len - B3_RI_WTO_R1); } static struct ethtool_ops sky2_ethtool_ops = { -- cgit v1.2.3 From ecfd7f32aa9e279f3732c2e5049b1a4a13428572 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 19 Sep 2005 15:49:13 -0700 Subject: [PATCH] sky2: version 0.5 Bump version (we are half way to a stable driver??) Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1acca982ac1..d2c0d04be72 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -54,7 +54,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.4" +#define DRV_VERSION "0.5" #define PFX DRV_NAME " " /* -- cgit v1.2.3 From 6b1a3aefd897fd9be410d192f950656ef2b59694 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Tue, 27 Sep 2005 15:02:55 -0700 Subject: [PATCH] sky2: changing mtu doesn't have to reset link Changing the MTU of the network device doesn't mean the whole link has to be brought down and back up again. Just stopping the receive engine is good enough. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 147 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 63 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d2c0d04be72..0cc57293faf 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -468,9 +468,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* serial mode register */ reg = DATA_BLIND_VAL(DATA_BLIND_DEF) | - GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); + GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); - if (hw->dev[port]->mtu > 1500) + if (hw->dev[port]->mtu > ETH_DATA_LEN) reg |= GM_SMOD_JUMBO_ENA; gma_write16(hw, port, GM_SERIAL_MODE, reg); @@ -515,8 +515,6 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) len /= 8; end = start + len - 1; - pr_debug("sky2_ramset start=%d end=%d\n", start, end); - sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR); sky2_write32(hw, RB_ADDR(q, RB_START), start); sky2_write32(hw, RB_ADDR(q, RB_END), end); @@ -528,7 +526,6 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) rxlo = len/2; rxup = rxlo + len/4; - pr_debug(" utpp=%d ltpp=%d\n", rxup, rxlo); /* Set thresholds on receive queue's */ sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), rxup); @@ -662,16 +659,44 @@ static void rx_set_checksum(struct sky2_port *sky2) le->ctrl = 0; le->opcode = OP_TCPSTART | HW_OWNER; - sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port], - PREF_UNIT_PUT_IDX), sky2->rx_put); - sky2_read16(sky2->hw, Y2_QADDR(rxqaddr[sky2->port], PREF_UNIT_PUT_IDX)); - mdelay(1); sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR), sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM); } +/* + * The RX Stop command will not work for Yukon-2 if the BMU does not + * reach the end of packet and since we can't make sure that we have + * incoming data, we must reset the BMU while it is not doing a DMA + * transfer. Since it is possible that the RX path is still active, + * the RX RAM buffer will be stopped first, so any possible incoming + * data will not trigger a DMA. After the RAM buffer is stopped, the + * BMU is polled until any DMA in progress is ended and only then it + * will be reset. + */ +static void sky2_rx_stop(struct sky2_port *sky2) +{ + struct sky2_hw *hw = sky2->hw; + unsigned rxq = rxqaddr[sky2->port]; + int i; + + /* disable the RAM Buffer receive queue */ + sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD); + + for (i = 0; i < 0xffff; i++) + if (sky2_read8(hw, RB_ADDR(rxq, Q_RSL)) + == sky2_read8(hw, RB_ADDR(rxq, Q_RL))) + goto stopped; + + printk(KERN_WARNING PFX "%s: receiver stop failed\n", + sky2->netdev->name); +stopped: + sky2_write32(hw, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST); + + /* reset the Rx prefetch unit */ + sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); +} /* Cleanout receive buffer area, assumes receiver hardware stopped */ static void sky2_rx_clean(struct sky2_port *sky2) @@ -693,7 +718,7 @@ static void sky2_rx_clean(struct sky2_port *sky2) } #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) -static inline unsigned sky2_rx_size(const struct sky2_port *sky2) +static inline unsigned rx_size(const struct sky2_port *sky2) { return roundup(sky2->netdev->mtu + ETH_HLEN + 4, 8); } @@ -709,12 +734,18 @@ static inline unsigned sky2_rx_size(const struct sky2_port *sky2) * is not aligned. This means we can't use skb_reserve to align * the IP header. */ -static int sky2_rx_fill(struct sky2_port *sky2) +static int sky2_rx_start(struct sky2_port *sky2) { - unsigned i; - unsigned size = sky2_rx_size(sky2); + struct sky2_hw *hw = sky2->hw; + unsigned size = rx_size(sky2); + unsigned rxq = rxqaddr[sky2->port]; + int i; + + sky2->rx_put = sky2->rx_next = 0; + sky2_qset(hw, rxq, is_pciex(hw) ? 0x80 : 0x600); + sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1); - pr_debug("rx_fill size=%d\n", size); + rx_set_checksum(sky2); for (i = 0; i < sky2->rx_pending; i++) { struct ring_info *re = sky2->rx_ring + i; @@ -722,12 +753,15 @@ static int sky2_rx_fill(struct sky2_port *sky2) if (!re->skb) goto nomem; - re->mapaddr = pci_map_single(sky2->hw->pdev, re->skb->data, + re->mapaddr = pci_map_single(hw->pdev, re->skb->data, size, PCI_DMA_FROMDEVICE); re->maplen = size; sky2_rx_add(sky2, re); } + /* 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); @@ -792,28 +826,14 @@ static int sky2_up(struct net_device *dev) sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL), RB_RST_SET); - sky2_qset(hw, rxqaddr[port], is_pciex(hw) ? 0x80 : 0x600); sky2_qset(hw, txqaddr[port], 0x600); + sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, + TX_RING_SIZE - 1); - sky2->rx_put = sky2->rx_next = 0; - sky2_prefetch_init(hw, rxqaddr[port], sky2->rx_le_map, RX_LE_SIZE - 1); - - rx_set_checksum(sky2); - - err = sky2_rx_fill(sky2); + err = sky2_rx_start(sky2); if (err) goto err_out; - /* Give buffers to receiver */ - sky2_write16(sky2->hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX), - sky2->rx_put); - sky2->rx_last_put = sky2_read16(sky2->hw, - Y2_QADDR(rxqaddr[port], - PREF_UNIT_PUT_IDX)); - - sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, - TX_RING_SIZE - 1); - /* Enable interrupts from phy/mac for port */ hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; sky2_write32(hw, B0_IMSK, hw->intr_mask); @@ -1076,7 +1096,6 @@ static int sky2_down(struct net_device *dev) struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; u16 ctrl; - int i; if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); @@ -1121,30 +1140,7 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET); - /* - * The RX Stop command will not work for Yukon-2 if the BMU does not - * reach the end of packet and since we can't make sure that we have - * incoming data, we must reset the BMU while it is not doing a DMA - * transfer. Since it is possible that the RX path is still active, - * the RX RAM buffer will be stopped first, so any possible incoming - * data will not trigger a DMA. After the RAM buffer is stopped, the - * BMU is polled until any DMA in progress is ended and only then it - * will be reset. - */ - - /* disable the RAM Buffer receive queue */ - sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_DIS_OP_MD); - - for (i = 0; i < 0xffff; i++) - if (sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RSL)) - == sky2_read8(hw, RB_ADDR(rxqaddr[port], Q_RL))) - break; - - sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), - BMU_RST_SET | BMU_FIFO_RST); - /* reset the Rx prefetch unit */ - sky2_write32(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_CTRL), - PREF_UNIT_RST_SET); + sky2_rx_stop(sky2); 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); @@ -1379,19 +1375,44 @@ static void sky2_tx_timeout(struct net_device *dev) static int sky2_change_mtu(struct net_device *dev, int new_mtu) { - int err = 0; + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + int err; + u16 ctl, mode; if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) return -EINVAL; - if (netif_running(dev)) - sky2_down(dev); + if (!netif_running(dev)) { + dev->mtu = new_mtu; + return 0; + } + + local_irq_disable(); + sky2_write32(hw, B0_IMSK, 0); + + 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); + sky2_rx_clean(sky2); dev->mtu = new_mtu; + mode = DATA_BLIND_VAL(DATA_BLIND_DEF) | + GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); - if (netif_running(dev)) - err = sky2_up(dev); + if (dev->mtu > ETH_DATA_LEN) + mode |= GM_SMOD_JUMBO_ENA; + + gma_write16(hw, sky2->port, GM_SERIAL_MODE, mode); + + sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD); + + err = sky2_rx_start(sky2); + gma_write16(hw, sky2->port, GM_GP_CTRL, ctl); + sky2_write32(hw, B0_IMSK, hw->intr_mask); + sky2_read32(hw, B0_IMSK); + local_irq_enable(); return err; } @@ -1407,7 +1428,7 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, struct sky2_port *sky2 = netdev_priv(dev); struct ring_info *re = sky2->rx_ring + sky2->rx_next; struct sk_buff *skb = NULL; - const unsigned int bufsize = sky2_rx_size(sky2); + const unsigned int bufsize = rx_size(sky2); if (unlikely(netif_msg_rx_status(sky2))) printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", -- cgit v1.2.3 From d11c13e752c4e34777d33579ee0378e0178ef52d Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Tue, 27 Sep 2005 15:02:56 -0700 Subject: [PATCH] sky2: cleanup interrupt processing The receive processing can be cleaned up by not using local variables to store checksum status, instead just put it directly onto the expected skb. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 65 +++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0cc57293faf..8527a49e3ff 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1040,13 +1040,14 @@ out_unlock: * NB: the hardware will tell us about partial completion of multi-part * buffers; these are defered until completion. */ -static void sky2_tx_complete(struct net_device *dev, u16 done) +static void sky2_tx_complete(struct sky2_port *sky2, u16 done) { - struct sky2_port *sky2 = netdev_priv(dev); + struct net_device *dev = sky2->netdev; unsigned i; - if (netif_msg_tx_done(sky2)) - printk(KERN_DEBUG "%s: tx done, upto %u\n", dev->name, done); + if (unlikely(netif_msg_tx_done(sky2))) + printk(KERN_DEBUG "%s: tx done, upto %u\n", + dev->name, done); spin_lock(&sky2->tx_lock); @@ -1086,7 +1087,7 @@ out: /* Cleanup all untransmitted buffers, assume transmitter not running */ static inline void sky2_tx_clean(struct sky2_port *sky2) { - sky2_tx_complete(sky2->netdev, sky2->tx_prod); + sky2_tx_complete(sky2, sky2->tx_prod); } /* Network shutdown */ @@ -1421,18 +1422,17 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) * For small packets or errors, just reuse existing skb. * For larger pakects, get new buffer. */ -static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, +static struct sk_buff *sky2_receive(struct sky2_port *sky2, u16 length, u32 status) { - struct net_device *dev = hw->dev[port]; - struct sky2_port *sky2 = netdev_priv(dev); struct ring_info *re = sky2->rx_ring + sky2->rx_next; struct sk_buff *skb = NULL; + struct net_device *dev; const unsigned int bufsize = rx_size(sky2); if (unlikely(netif_msg_rx_status(sky2))) printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", - dev->name, sky2->rx_next, status, length); + sky2->netdev->name, sky2->rx_next, status, length); sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; @@ -1451,6 +1451,8 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->mapaddr, length, PCI_DMA_FROMDEVICE); memcpy(skb->data, re->skb->data, length); + skb->ip_summed = re->skb->ip_summed; + skb->csum = re->skb->csum; pci_dma_sync_single_for_device(sky2->hw->pdev, re->mapaddr, length, PCI_DMA_FROMDEVICE); } else { @@ -1471,12 +1473,14 @@ static struct sk_buff *sky2_receive(struct sky2_hw *hw, unsigned port, re->maplen = bufsize; } - skb->dev = dev; skb_put(skb, length); + dev = sky2->netdev; + skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; resubmit: + re->skb->ip_summed = CHECKSUM_NONE; sky2_rx_add(sky2, re); return skb; @@ -1517,59 +1521,46 @@ static inline u16 tx_index(u8 port, u32 status, u16 len) * Both ports share the same status interrupt, therefore there is only * one poll routine. */ -static int sky2_poll(struct net_device *dev, int *budget) +static int sky2_poll(struct net_device *dev0, int *budget) { - struct sky2_port *sky2 = netdev_priv(dev); - struct sky2_hw *hw = sky2->hw; - unsigned int to_do = min(dev->quota, *budget); + 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; - unsigned char summed[2] = { CHECKSUM_NONE, CHECKSUM_NONE }; - unsigned int csum[2]; hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); while (hw->st_idx != hwidx && work_done < to_do) { struct sky2_status_le *le = hw->st_le + hw->st_idx; + struct sky2_port *sky2; struct sk_buff *skb; - u8 port; u32 status; u16 length; + BUG_ON(le->link >= hw->ports); + sky2 = netdev_priv(hw->dev[le->link]); status = le32_to_cpu(le->status); length = le16_to_cpu(le->length); - port = le->link; - - BUG_ON(port >= hw->ports || hw->dev[port] == NULL); switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: - skb = sky2_receive(hw, port, length, status); + skb = sky2_receive(sky2, length, status); if (likely(skb)) { - /* Add hw checksum if available */ - skb->ip_summed = summed[port]; - skb->csum = csum[port]; - netif_receive_skb(skb); ++work_done; } - - /* Clear for next packet */ - csum[port] = 0; - summed[port] = CHECKSUM_NONE; - break; case OP_RXCHKS: - /* Save computed checksum for next rx */ - csum[port] = le16_to_cpu(status & 0xffff); - summed[port] = CHECKSUM_HW; + skb = sky2->rx_ring[sky2->rx_next].skb; + skb->ip_summed = CHECKSUM_HW; + skb->csum = le16_to_cpu(status); break; case OP_TXINDEXLE: - sky2_tx_complete(hw->dev[port], - tx_index(port, status, length)); + sky2_tx_complete(sky2, + tx_index(sky2->port, status, length)); break; case OP_RXTIMESTAMP: @@ -1599,7 +1590,7 @@ static int sky2_poll(struct net_device *dev, int *budget) rx_set_put(hw->dev[1]); *budget -= work_done; - dev->quota -= work_done; + dev0->quota -= work_done; if (work_done < to_do) { /* * Another chip workaround, need to restart TX timer if status @@ -1613,7 +1604,7 @@ static int sky2_poll(struct net_device *dev, int *budget) hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_read32(hw, B0_IMSK); - netif_rx_complete(dev); + netif_rx_complete(dev0); } return work_done >= to_do; -- cgit v1.2.3 From d1f1370863f7fa3d76dc7d7779debdda854a5a60 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Tue, 27 Sep 2005 15:02:57 -0700 Subject: [PATCH] sky2: add hardware VLAN acceleration support Use the hardware to do VLAN. Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 101 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 14 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 8527a49e3ff..6d9c1db8896 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -26,7 +26,6 @@ /* * TODO * - coalescing setting? - * - vlan support * * TOTEST * - variable ring size @@ -48,9 +47,14 @@ #include #include #include +#include #include +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#define SKY2_VLAN_TAG_USED 1 +#endif + #include "sky2.h" #define DRV_NAME "sky2" @@ -489,7 +493,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* Configure Rx MAC FIFO */ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), - GMF_OPER_ON | GMF_RX_F_FL_ON); + GMF_RX_CTRL_DEF); /* Flush Rx MAC FIFO on any flowcontrol or error */ reg = GMR_FS_ANY_ERR; @@ -717,6 +721,41 @@ static void sky2_rx_clean(struct sky2_port *sky2) } } +#ifdef SKY2_VLAN_TAG_USED +static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + u16 port = sky2->port; + unsigned long flags; + + spin_lock_irqsave(&sky2->tx_lock, flags); + + sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON); + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON); + sky2->vlgrp = grp; + + spin_unlock_irqrestore(&sky2->tx_lock, flags); +} + +static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + u16 port = sky2->port; + unsigned long flags; + + spin_lock_irqsave(&sky2->tx_lock, flags); + + 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; + + spin_unlock_irqrestore(&sky2->tx_lock, flags); +} +#endif + #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) static inline unsigned rx_size(const struct sky2_port *sky2) { @@ -894,7 +933,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - struct sky2_tx_le *le; + struct sky2_tx_le *le = NULL; struct ring_info *re; unsigned long flags; unsigned i, len; @@ -961,8 +1000,23 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) sky2->tx_last_mss = mss; } - /* Handle TCP checksum offload */ ctrl = 0; +#ifdef SKY2_VLAN_TAG_USED + /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */ + if (sky2->vlgrp && vlan_tx_tag_present(skb)) { + if (!le) { + le = get_tx_le(sky2); + le->tx.addr = 0; + le->opcode = OP_VLAN|HW_OWNER; + le->ctrl = 0; + } else + le->opcode |= OP_VLAN; + le->length = cpu_to_be16(vlan_tx_tag_get(skb)); + ctrl |= INS_VLAN; + } +#endif + + /* Handle TCP checksum offload */ if (skb->ip_summed == CHECKSUM_HW) { u16 hdr = skb->h.raw - skb->data; u16 offset = hdr + skb->csum; @@ -1436,10 +1490,7 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; - if (!(status & GMR_FS_RX_OK) - || (status & GMR_FS_ANY_ERR) - || (length << 16) != (status & GMR_FS_LEN) - || length > bufsize) + if (!(status & GMR_FS_RX_OK) || (status & GMR_FS_ANY_ERR)) goto error; if (length < RX_COPY_THRESHOLD) { @@ -1539,6 +1590,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) u16 length; BUG_ON(le->link >= hw->ports); + if (!hw->dev[le->link]) + goto skip; + sky2 = netdev_priv(hw->dev[le->link]); status = le32_to_cpu(le->status); length = le16_to_cpu(le->length); @@ -1546,12 +1600,27 @@ static int sky2_poll(struct net_device *dev0, int *budget) switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: skb = sky2_receive(sky2, length, status); - if (likely(skb)) { + if (!skb) + break; +#ifdef SKY2_VLAN_TAG_USED + if (sky2->vlgrp && (status & GMR_FS_VLAN)) { + vlan_hwaccel_receive_skb(skb, + sky2->vlgrp, + be16_to_cpu(sky2->rx_tag)); + } else +#endif netif_receive_skb(skb); - ++work_done; - } break; +#ifdef SKY2_VLAN_TAG_USED + case OP_RXVLAN: + sky2->rx_tag = length; + break; + + case OP_RXCHKSVLAN: + sky2->rx_tag = length; + /* fall through */ +#endif case OP_RXCHKS: skb = sky2->rx_ring[sky2->rx_next].skb; skb->ip_summed = CHECKSUM_HW; @@ -1563,9 +1632,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) tx_index(sky2->port, status, length)); break; - case OP_RXTIMESTAMP: - break; - default: if (net_ratelimit()) printk(KERN_WARNING PFX @@ -1574,6 +1640,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) break; } + skip: hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; if (hw->st_idx == hwidx) { hwidx = sky2_read16(hw, STAT_PUT_IDX); @@ -2615,6 +2682,12 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, dev->features |= NETIF_F_HIGHDMA; dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; +#ifdef SKY2_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = sky2_vlan_rx_register; + dev->vlan_rx_kill_vid = sky2_vlan_rx_kill_vid; +#endif + /* read the mac address */ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); -- cgit v1.2.3 From 5afa0a9cfb79556f45c02957e71a2ac48a5bb6a1 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Tue, 27 Sep 2005 15:03:00 -0700 Subject: [PATCH] sky2: explicit set power state Add better power management, and power down the chip on device removal Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 156 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 45 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6d9c1db8896..1d00cdcdaa6 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -175,6 +175,91 @@ ready: return gma_read16(hw, port, GM_SMI_DATA); } +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); + + pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_PMC, &power_control); + vaux = (sky2_read8(hw, B0_CTST) & Y2_VAUX_AVAIL) && + (power_control & PCI_PM_CAP_PME_D3cold); + + pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_CTRL, &power_control); + + power_control |= PCI_PM_CTRL_PME_STATUS; + power_control &= ~(PCI_PM_CTRL_STATE_MASK); + + switch (state) { + case PCI_D0: + /* switch power to VCC (WA for VAUX problem) */ + sky2_write8(hw, B0_POWER_CTRL, + PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); + + /* disable Core Clock Division, */ + sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); + + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + /* enable bits are inverted */ + sky2_write8(hw, B2_Y2_CLK_GATE, + Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | + Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | + Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); + else + sky2_write8(hw, B2_Y2_CLK_GATE, 0); + + /* Turn off phy power saving */ + pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); + reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); + + /* looks like this xl is back asswards .. */ + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { + reg1 |= PCI_Y2_PHY1_COMA; + if (hw->ports > 1) + reg1 |= PCI_Y2_PHY2_COMA; + } + pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); + break; + + case PCI_D3hot: + case PCI_D3cold: + /* Turn on phy power saving */ + pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); + else + reg1 |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); + pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); + + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + sky2_write8(hw, B2_Y2_CLK_GATE, 0); + else + /* enable bits are inverted */ + sky2_write8(hw, B2_Y2_CLK_GATE, + Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | + Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | + Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); + + /* switch power to VAUX */ + if (vaux && state != PCI_D3cold) + sky2_write8(hw, B0_POWER_CTRL, + (PC_VAUX_ENA | PC_VCC_ENA | + PC_VAUX_ON | PC_VCC_OFF)); + break; + default: + printk(KERN_ERR PFX "Unknown power state %d\n", state); + ret = -1; + } + + pci_write_config_byte(hw->pdev, 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) { u16 reg; @@ -1869,7 +1954,7 @@ static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) static int sky2_reset(struct sky2_hw *hw) { - u32 ctst, power; + u32 ctst; u16 status; u8 t8, pmd_type; int i; @@ -1927,33 +2012,7 @@ static int sky2_reset(struct sky2_hw *hw) } hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4; - /* switch power to VCC (WA for VAUX problem) */ - sky2_write8(hw, B0_POWER_CTRL, - PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); - - /* disable Core Clock Division, */ - sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); - - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) - /* enable bits are inverted */ - sky2_write8(hw, B2_Y2_CLK_GATE, - Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | - Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | - Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); - else - sky2_write8(hw, B2_Y2_CLK_GATE, 0); - - /* Turn off phy power saving */ - pci_read_config_dword(hw->pdev, PCI_DEV_REG1, &power); - power &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); - - /* looks like this xl is back asswards .. */ - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { - power |= PCI_Y2_PHY1_COMA; - if (hw->ports > 1) - power |= PCI_Y2_PHY2_COMA; - } - pci_write_config_dword(hw->pdev, PCI_DEV_REG1, power); + sky2_set_power_state(hw, PCI_D0); for (i = 0; i < hw->ports; i++) { sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); @@ -2714,7 +2773,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, { struct net_device *dev, *dev1 = NULL; struct sky2_hw *hw; - int err, using_dac = 0; + int err, pm_cap, using_dac = 0; err = pci_enable_device(pdev); if (err) { @@ -2732,6 +2791,15 @@ static int __devinit sky2_probe(struct pci_dev *pdev, pci_set_master(pdev); + /* Find power-management capability. */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap == 0) { + printk(KERN_ERR PFX "Cannot find PowerManagement capability, " + "aborting.\n"); + err = -EIO; + goto err_out_free_regions; + } + if (sizeof(dma_addr_t) > sizeof(u32)) { err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (!err) @@ -2775,6 +2843,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, pci_name(pdev)); goto err_out_free_hw; } + hw->pm_cap = pm_cap; err = sky2_reset(hw); if (err) @@ -2861,8 +2930,10 @@ static void __devexit sky2_remove(struct pci_dev *pdev) 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_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); @@ -2874,6 +2945,7 @@ static void __devexit sky2_remove(struct pci_dev *pdev) free_netdev(dev0); iounmap(hw->regs); kfree(hw); + pci_set_drvdata(pdev, NULL); } @@ -2881,28 +2953,21 @@ static void __devexit sky2_remove(struct pci_dev *pdev) static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) { struct sky2_hw *hw = pci_get_drvdata(pdev); - int i, wol = 0; + int i; for (i = 0; i < 2; i++) { struct net_device *dev = hw->dev[i]; if (dev) { - struct sky2_port *sky2 = netdev_priv(dev); - if (netif_running(dev)) { - netif_carrier_off(dev); - sky2_down(dev); - } + if (!netif_running(dev)) + continue; + + sky2_down(dev); netif_device_detach(dev); - wol |= sky2->wol; } } - pci_save_state(pdev); - pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; + return sky2_set_power_state(hw, pci_choose_state(pdev, state)); } static int sky2_resume(struct pci_dev *pdev) @@ -2910,18 +2975,19 @@ static int sky2_resume(struct pci_dev *pdev) struct sky2_hw *hw = pci_get_drvdata(pdev); int i; - pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D0, 0); + sky2_set_power_state(hw, PCI_D0); sky2_reset(hw); for (i = 0; i < 2; i++) { struct net_device *dev = hw->dev[i]; if (dev) { - netif_device_attach(dev); - if (netif_running(dev)) + if (netif_running(dev)) { + netif_device_attach(dev); sky2_up(dev); + } } } return 0; -- cgit v1.2.3 From 724bca3ca4d2126b67dd62e64d3f97805f9672de Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Tue, 27 Sep 2005 15:03:01 -0700 Subject: [PATCH] sky2: version 0.6 Verion number change, comment update and one simple optimization Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1d00cdcdaa6..a6c7bb9cea0 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -28,10 +28,8 @@ * - coalescing setting? * * TOTEST - * - variable ring size * - speed setting - * - power management - * - netpoll + * - suspend/resume */ #include @@ -58,7 +56,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.5" +#define DRV_VERSION "0.6" #define PFX DRV_NAME " " /* @@ -1159,7 +1157,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) re->idx = sky2->tx_prod; le->ctrl |= EOP; - sky2_put_idx(sky2->hw, txqaddr[sky2->port], sky2->tx_prod, + sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod, &sky2->tx_last_put, TX_RING_SIZE); if (tx_avail(sky2) < MAX_SKB_TX_LE + 1) -- cgit v1.2.3 From 9a7ae0a978263adb7d6b630f0c3c37c0133325e0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 27 Sep 2005 15:28:42 -0700 Subject: [PATCH] sky2: nway reset (BONUS FEATURE) Here is support for ethtool controlled renegotiation. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index a6c7bb9cea0..57583fa86a9 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2300,6 +2300,24 @@ static u32 sky2_get_msglevel(struct net_device *netdev) return sky2->msg_enable; } +static int sky2_nway_reset(struct net_device *dev) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + + if (sky2->autoneg != AUTONEG_ENABLE) + return -EINVAL; + + netif_stop_queue(dev); + + spin_lock_irq(&hw->phy_lock); + sky2_phy_reset(hw, sky2->port); + sky2_phy_init(hw, sky2->port); + spin_unlock_irq(&hw->phy_lock); + + return 0; +} + static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count) { struct sky2_hw *hw = sky2->hw; @@ -2656,6 +2674,7 @@ static struct ethtool_ops sky2_ethtool_ops = { .get_drvinfo = sky2_get_drvinfo, .get_msglevel = sky2_get_msglevel, .set_msglevel = sky2_set_msglevel, + .nway_reset = sky2_nway_reset, .get_regs_len = sky2_get_regs_len, .get_regs = sky2_get_regs, .get_link = ethtool_op_get_link, -- cgit v1.2.3 From 2995bfb7855aabd493f840af361f3dd7d221caea Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 28 Sep 2005 10:01:03 -0700 Subject: [PATCH] sky2: add permanent address support. Add permanent address support Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 57583fa86a9..06beb71cb3c 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2698,6 +2698,7 @@ static struct ethtool_ops sky2_ethtool_ops = { .phys_id = sky2_phys_id, .get_stats_count = sky2_get_stats_count, .get_ethtool_stats = sky2_get_ethtool_stats, + .get_perm_addr = ethtool_op_get_perm_addr, }; /* Initialize network device */ @@ -2766,6 +2767,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, /* read the mac address */ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); /* device is off until link detection */ netif_carrier_off(dev); -- cgit v1.2.3 From b2f5ad4fec76e78103e7fbb4e808587ea8a6d824 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 26 Oct 2005 12:16:08 -0700 Subject: [PATCH] sky2: use kzalloc Can use kzalloc to save a little code. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 06beb71cb3c..f5b101bf469 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -910,12 +910,11 @@ static int sky2_up(struct net_device *dev) if (!sky2->tx_le) goto err_out; - sky2->tx_ring = kmalloc(TX_RING_SIZE * sizeof(struct ring_info), + sky2->tx_ring = kzalloc(TX_RING_SIZE * sizeof(struct ring_info), GFP_KERNEL); if (!sky2->tx_ring) goto err_out; sky2->tx_prod = sky2->tx_cons = 0; - memset(sky2->tx_ring, 0, TX_RING_SIZE * sizeof(struct ring_info)); sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES, &sky2->rx_le_map); @@ -923,7 +922,7 @@ static int sky2_up(struct net_device *dev) goto err_out; memset(sky2->rx_le, 0, RX_LE_BYTES); - sky2->rx_ring = kmalloc(sky2->rx_pending * sizeof(struct ring_info), + sky2->rx_ring = kzalloc(sky2->rx_pending * sizeof(struct ring_info), GFP_KERNEL); if (!sky2->rx_ring) goto err_out; -- cgit v1.2.3 From d571b694df3ebc66f7a4c507f5a32579e43c2294 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 26 Oct 2005 12:16:09 -0700 Subject: [PATCH] sky2: spelling fixes Cosmetic cleanup's: mostly spelling fixes etc. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f5b101bf469..92d69fff875 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -214,7 +214,7 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); - /* looks like this xl is back asswards .. */ + /* looks like this XL is back asswards .. */ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) { reg1 |= PCI_Y2_PHY1_COMA; if (hw->ports > 1) @@ -461,7 +461,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) if (ledover) gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); - /* Enable phy interrupt on autonegotiation complete (or link up) */ + /* Enable phy interrupt on auto-negotiation complete (or link up) */ if (sky2->autoneg == AUTONEG_ENABLE) gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL); else @@ -578,10 +578,10 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_CTRL_DEF); - /* Flush Rx MAC FIFO on any flowcontrol or error */ + /* Flush Rx MAC FIFO on any flow control or error */ reg = GMR_FS_ANY_ERR; if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev <= 1) - reg = 0; /* WA Dev #4115 */ + reg = 0; /* WA dev #4.115 */ sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), reg); /* Set threshold to 0xa (64 bytes) @@ -662,7 +662,7 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2) } /* - * This is a workaround code taken from syskonnect sk98lin driver + * 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 inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, @@ -785,7 +785,7 @@ stopped: sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); } -/* Cleanout receive buffer area, assumes receiver hardware stopped */ +/* Clean out receive buffer area, assumes receiver hardware stopped */ static void sky2_rx_clean(struct sky2_port *sky2) { unsigned i; @@ -1174,7 +1174,7 @@ out_unlock: * Free ring elements from starting at tx_cons until "done" * * NB: the hardware will tell us about partial completion of multi-part - * buffers; these are defered until completion. + * buffers; these are deferred until completion. */ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) { @@ -1182,7 +1182,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) unsigned i; if (unlikely(netif_msg_tx_done(sky2))) - printk(KERN_DEBUG "%s: tx done, upto %u\n", + printk(KERN_DEBUG "%s: tx done, up to %u\n", dev->name, done); spin_lock(&sky2->tx_lock); @@ -1282,7 +1282,7 @@ 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); - /* turn off led's */ + /* turn off LED's */ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); sky2_tx_clean(sky2); @@ -1364,7 +1364,7 @@ static void sky2_link_up(struct sky2_port *sky2) if (netif_msg_link(sky2)) printk(KERN_INFO PFX - "%s: Link is up at %d Mbps, %s duplex, flowcontrol %s\n", + "%s: Link is up at %d Mbps, %s duplex, flow control %s\n", sky2->netdev->name, sky2->speed, sky2->duplex == DUPLEX_FULL ? "full" : "half", (sky2->tx_pause && sky2->rx_pause) ? "both" : @@ -1451,7 +1451,7 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) } /* - * Interrrupt from PHY are handled in tasklet (soft irq) + * Interrupt from PHY are handled in tasklet (soft irq) * because accessing phy registers requires spin wait which might * cause excess interrupt latency. */ @@ -1556,7 +1556,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) /* * Receive one packet. * For small packets or errors, just reuse existing skb. - * For larger pakects, get new buffer. + * For larger packets, get new buffer. */ static struct sk_buff *sky2_receive(struct sky2_port *sky2, u16 length, u32 status) @@ -1818,7 +1818,7 @@ static void sky2_hw_intr(struct sky2_hw *hw) } if (status & Y2_IS_PCI_EXP) { - /* PCI-Express uncorrectable Error occured */ + /* PCI-Express uncorrectable Error occurred */ u32 pex_err; pci_read_config_dword(hw->pdev, PEX_UNC_ERR_STAT, &pex_err); @@ -2113,7 +2113,7 @@ static int sky2_reset(struct sky2_hw *hw) if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0) sky2_write8(hw, STAT_FIFO_ISR_WM, 0x10); - else /* WA 4109 */ + else /* WA dev 4.109 */ sky2_write8(hw, STAT_FIFO_ISR_WM, 0x04); sky2_write32(hw, STAT_ISR_TIMER_INI, 0x0190); @@ -2420,7 +2420,7 @@ static void sky2_set_multicast(struct net_device *dev) reg = gma_read16(hw, port, GM_RX_CTRL); reg |= GM_RXCR_UCF_ENA; - if (dev->flags & IFF_PROMISC) /* promiscious */ + if (dev->flags & IFF_PROMISC) /* promiscuous */ reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > 16) /* all multicast */ memset(filter, 0xff, sizeof(filter)); @@ -2833,7 +2833,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } } #ifdef __BIG_ENDIAN - /* byte swap decriptors in hardware */ + /* byte swap descriptors in hardware */ { u32 reg; -- cgit v1.2.3 From bea86103313fae2e29f2d6eb9a4bd7cbeabd4d32 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 26 Oct 2005 12:16:10 -0700 Subject: [PATCH] sky2: fix NAPI and receive handling Speed up the receive and interrupt processing and eliminate a couple of race conditions from NAPI code. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 63 ++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 92d69fff875..a85a8d16908 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -76,7 +76,7 @@ #define RX_LE_SIZE 256 #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) -#define RX_MAX_PENDING (RX_LE_SIZE/2 - 1) +#define RX_MAX_PENDING (RX_LE_SIZE/2 - 2) #define RX_DEF_PENDING 128 #define RX_COPY_THRESHOLD 256 @@ -690,7 +690,7 @@ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, setnew: sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx); } - *last = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)); + *last = idx; } @@ -723,15 +723,6 @@ static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re) le->opcode = OP_PACKET | HW_OWNER; } -/* Tell receiver about new buffers. */ -static inline void rx_set_put(struct net_device *dev) -{ - struct sky2_port *sky2 = netdev_priv(dev); - - if (sky2->rx_last_put != sky2->rx_put) - sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put, - &sky2->rx_last_put, RX_LE_SIZE); -} /* Tell chip where to start receive checksum. * Actually has two checksums, but set both same to avoid possible byte @@ -1616,6 +1607,10 @@ resubmit: re->skb->ip_summed = CHECKSUM_NONE; sky2_rx_add(sky2, re); + /* Tell receiver about new buffers. */ + sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put, + &sky2->rx_last_put, RX_LE_SIZE); + return skb; error: @@ -1664,16 +1659,26 @@ static int sky2_poll(struct net_device *dev0, int *budget) hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); - while (hw->st_idx != hwidx && work_done < to_do) { + + do { struct sky2_status_le *le = hw->st_le + hw->st_idx; struct sky2_port *sky2; struct sk_buff *skb; u32 status; u16 length; - BUG_ON(le->link >= hw->ports); - if (!hw->dev[le->link]) - goto skip; + /* Are we done yet? */ + if (hw->st_idx == hwidx) { + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + hwidx = sky2_read16(hw, STAT_PUT_IDX); + if (hwidx == hw->st_idx) + break; + } + + hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; + prefetch(&hw->st_le[hw->st_idx]); + + BUG_ON(le->link >= hw->ports || !hw->dev[le->link]); sky2 = netdev_priv(hw->dev[le->link]); status = le32_to_cpu(le->status); @@ -1692,6 +1697,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } else #endif netif_receive_skb(skb); + ++work_done; break; #ifdef SKY2_VLAN_TAG_USED @@ -1722,22 +1728,11 @@ static int sky2_poll(struct net_device *dev0, int *budget) break; } - skip: - hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; - if (hw->st_idx == hwidx) { - hwidx = sky2_read16(hw, STAT_PUT_IDX); - rmb(); - } - } + le->opcode = 0; /* paranoia */ + } while (work_done < to_do); mmiowb(); - if (hw->dev[0]) - rx_set_put(hw->dev[0]); - - if (hw->dev[1]) - rx_set_put(hw->dev[1]); - *budget -= work_done; dev0->quota -= work_done; if (work_done < to_do) { @@ -1750,10 +1745,10 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); } + netif_rx_complete(dev0); hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_read32(hw, B0_IMSK); - netif_rx_complete(dev0); } return work_done >= to_do; @@ -1880,6 +1875,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) 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; status = sky2_read32(hw, B0_Y2_SP_ISRC2); @@ -1890,12 +1886,13 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) sky2_hw_intr(hw); /* Do NAPI for Rx and Tx status */ - if ((status & Y2_IS_STAT_BMU) && netif_rx_schedule_test(hw->dev[0])) { - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - + if (status & Y2_IS_STAT_BMU) { hw->intr_mask &= ~Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); - __netif_rx_schedule(hw->dev[0]); + prefetch(&hw->st_le[hw->st_idx]); + + if (netif_rx_schedule_test(dev0)) + __netif_rx_schedule(dev0); } if (status & Y2_IS_IRQ_PHY1) -- cgit v1.2.3 From f1e691a24955ea987f021f378324fb5003b1b208 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 26 Oct 2005 12:16:11 -0700 Subject: [PATCH] sky2: version 0.7 Change version number Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index a85a8d16908..e08695a2ca5 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -56,7 +56,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.6" +#define DRV_VERSION "0.7" #define PFX DRV_NAME " " /* -- cgit v1.2.3 From d0bbccfa3297d3ef6ae4691585abde9a6c26b186 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 10 Nov 2005 15:29:27 -0800 Subject: [PATCH] sky2 needs dma_mapping.h On alpha: drivers/net/sky2.c: In function `sky2_probe': drivers/net/sky2.c:2819: error: `DMA_64BIT_MASK' undeclared (first use in this function) drivers/net/sky2.c:2819: error: (Each undeclared identifier is reported only once drivers/net/sky2.c:2819: error: for each function it appears in.) drivers/net/sky2.c:2825: error: `DMA_32BIT_MASK' undeclared (first use in this function) Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index e08695a2ca5..9f89000e5ad 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 214376434863f9ca109a7853fbbd6db284d3fba7 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:11 -0800 Subject: [PATCH] sky2: eliminate special case for EC-A1 Eliminate special case for EC-A1. The overhead isn't so great that having config option is worth it. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 9f89000e5ad..d7a94d62d80 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -67,13 +67,9 @@ * a receive requires one (or two if using 64 bit dma). */ -#ifdef CONFIG_SKY2_EC_A1 #define is_ec_a1(hw) \ - ((hw)->chip_id == CHIP_ID_YUKON_EC && \ - (hw)->chip_rev == CHIP_REV_YU_EC_A1) -#else -#define is_ec_a1(hw) 0 -#endif + unlikely((hw)->chip_id == CHIP_ID_YUKON_EC && \ + (hw)->chip_rev == CHIP_REV_YU_EC_A1) #define RX_LE_SIZE 256 #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) -- cgit v1.2.3 From ef743d3359813795fb38c4308bff2311eb30651f Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:12 -0800 Subject: [PATCH] sky2: add MII support Add MII ioctl interface to sky2. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 8 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d7a94d62d80..0b9b7017416 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -136,7 +137,7 @@ static const char *yukon_name[] = { /* Access to external PHY */ -static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) +static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) { int i; @@ -146,13 +147,15 @@ static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) for (i = 0; i < PHY_RETRIES; i++) { if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY)) - return; + return 0; udelay(1); } + printk(KERN_WARNING PFX "%s: phy write timeout\n", hw->dev[port]->name); + return -ETIMEDOUT; } -static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) +static int __gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg, u16 *val) { int i; @@ -160,14 +163,24 @@ static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD); for (i = 0; i < PHY_RETRIES; i++) { - if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL) - goto ready; + if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL) { + *val = gma_read16(hw, port, GM_SMI_DATA); + return 0; + } + udelay(1); } - printk(KERN_WARNING PFX "%s: phy read timeout\n", hw->dev[port]->name); -ready: - return gma_read16(hw, port, GM_SMI_DATA); + return -ETIMEDOUT; +} + +static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg) +{ + u16 v; + + if (__gm_phy_read(hw, port, reg, &v) != 0) + printk(KERN_WARNING PFX "%s: phy read timeout\n", hw->dev[port]->name); + return v; } static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) @@ -792,6 +805,44 @@ static void sky2_rx_clean(struct sky2_port *sky2) } } +/* Basic MII support */ +static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mii_ioctl_data *data = if_mii(ifr); + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + int err = -EOPNOTSUPP; + + if (!netif_running(dev)) + return -ENODEV; /* Phy still in reset */ + + switch(cmd) { + case SIOCGMIIPHY: + data->phy_id = PHY_ADDR_MARV; + + /* fallthru */ + case SIOCGMIIREG: { + u16 val = 0; + spin_lock_bh(&hw->phy_lock); + err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val); + spin_unlock_bh(&hw->phy_lock); + data->val_out = val; + break; + } + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_bh(&hw->phy_lock); + err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f, + data->val_in); + spin_unlock_bh(&hw->phy_lock); + break; + } + return err; +} + #ifdef SKY2_VLAN_TAG_USED static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { @@ -2708,8 +2759,10 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &hw->pdev->dev); + dev->irq = hw->pdev->irq; dev->open = sky2_up; dev->stop = sky2_down; + dev->do_ioctl = sky2_ioctl; dev->hard_start_xmit = sky2_xmit_frame; dev->get_stats = sky2_get_stats; dev->set_multicast_list = sky2_set_multicast; -- cgit v1.2.3 From 42eeea0145a1c7570624ddeaa15d3f357a52edf8 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:13 -0800 Subject: [PATCH] sky2: fix receive flush/pause issues Fix issues with pause and flush. This code works on all chip versions tested. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0b9b7017416..83361aed49f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -485,8 +485,8 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) int i; const u8 *addr = hw->dev[port]->dev_addr; - sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); - sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); + sky2_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET); + sky2_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR|GPC_ENA_PAUSE); sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); @@ -589,11 +589,8 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) GMF_RX_CTRL_DEF); /* Flush Rx MAC FIFO on any flow control or error */ - reg = GMR_FS_ANY_ERR; - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev <= 1) - reg = 0; /* WA dev #4.115 */ + sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR); - sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), reg); /* Set threshold to 0xa (64 bytes) * ASF disabled so no need to do WA dev #4.30 */ @@ -1363,9 +1360,6 @@ static void sky2_link_up(struct sky2_port *sky2) unsigned port = sky2->port; u16 reg; - /* disable Rx GMAC FIFO flush mode */ - sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_F_FL_OFF); - /* Enable Transmit FIFO Underrun */ sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK); @@ -1611,9 +1605,12 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; - if (!(status & GMR_FS_RX_OK) || (status & GMR_FS_ANY_ERR)) + if (status & GMR_FS_ANY_ERR) goto error; + if (!(status & GMR_FS_RX_OK)) + goto resubmit; + if (length < RX_COPY_THRESHOLD) { skb = alloc_skb(length + 2, GFP_ATOMIC); if (!skb) @@ -1662,9 +1659,6 @@ resubmit: return skb; error: - if (status & GMR_FS_GOOD_FC) - goto resubmit; - if (netif_msg_rx_err(sky2)) printk(KERN_INFO PFX "%s: rx error, status 0x%x length %d\n", sky2->netdev->name, status, length); -- cgit v1.2.3 From 13210ce5c06ed9537558b85e9c0df4248b28f1f7 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:14 -0800 Subject: [PATCH] sky2: improve receive performance Changes to receive side processing: * bigger receive ring * clean up polling loop Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 66 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 32 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 83361aed49f..e6f71c1232b 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -72,10 +72,10 @@ unlikely((hw)->chip_id == CHIP_ID_YUKON_EC && \ (hw)->chip_rev == CHIP_REV_YU_EC_A1) -#define RX_LE_SIZE 256 +#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) -#define RX_DEF_PENDING 128 +#define RX_DEF_PENDING RX_MAX_PENDING #define RX_COPY_THRESHOLD 256 #define TX_RING_SIZE 512 @@ -1596,7 +1596,6 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, { struct ring_info *re = sky2->rx_ring + sky2->rx_next; struct sk_buff *skb = NULL; - struct net_device *dev; const unsigned int bufsize = rx_size(sky2); if (unlikely(netif_msg_rx_status(sky2))) @@ -1643,11 +1642,6 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, } skb_put(skb, length); - dev = sky2->netdev; - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - dev->last_rx = jiffies; - resubmit: re->skb->ip_summed = CHECKSUM_NONE; sky2_rx_add(sky2, re); @@ -1702,35 +1696,42 @@ static int sky2_poll(struct net_device *dev0, int *budget) BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); - do { - struct sky2_status_le *le = hw->st_le + hw->st_idx; + while (hwidx != hw->st_idx) { + struct sky2_status_le *le = hw->st_le + hw->st_idx; + struct net_device *dev; struct sky2_port *sky2; struct sk_buff *skb; u32 status; u16 length; + u8 op; - /* Are we done yet? */ - if (hw->st_idx == hwidx) { - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - hwidx = sky2_read16(hw, STAT_PUT_IDX); - if (hwidx == hw->st_idx) - break; - } - + le = hw->st_le + hw->st_idx; hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; - prefetch(&hw->st_le[hw->st_idx]); + prefetch(hw->st_le + hw->st_idx); BUG_ON(le->link >= hw->ports || !hw->dev[le->link]); - sky2 = netdev_priv(hw->dev[le->link]); + BUG_ON(le->link >= 2); + dev = hw->dev[le->link]; + if (dev == NULL || !netif_running(dev)) + continue; + + sky2 = netdev_priv(dev); status = le32_to_cpu(le->status); length = le16_to_cpu(le->length); + op = le->opcode & ~HW_OWNER; + le->opcode = 0; - switch (le->opcode & ~HW_OWNER) { + switch (op) { case OP_RXSTAT: skb = sky2_receive(sky2, length, status); if (!skb) break; + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + #ifdef SKY2_VLAN_TAG_USED if (sky2->vlgrp && (status & GMR_FS_VLAN)) { vlan_hwaccel_receive_skb(skb, @@ -1739,7 +1740,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) } else #endif netif_receive_skb(skb); - ++work_done; + + if (++work_done >= to_do) + goto exit_loop; break; #ifdef SKY2_VLAN_TAG_USED @@ -1765,18 +1768,15 @@ static int sky2_poll(struct net_device *dev0, int *budget) default: if (net_ratelimit()) printk(KERN_WARNING PFX - "unknown status opcode 0x%x\n", - le->opcode); + "unknown status opcode 0x%x\n", op); break; } + } - le->opcode = 0; /* paranoia */ - } while (work_done < to_do); +exit_loop: mmiowb(); - *budget -= work_done; - dev0->quota -= work_done; if (work_done < to_do) { /* * Another chip workaround, need to restart TX timer if status @@ -1790,11 +1790,13 @@ static int sky2_poll(struct net_device *dev0, int *budget) netif_rx_complete(dev0); hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); - sky2_read32(hw, B0_IMSK); + mmiowb(); + return 0; + } else { + *budget -= work_done; + dev0->quota -= work_done; + return 1; } - - return work_done >= to_do; - } static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status) -- cgit v1.2.3 From 5a5b1ea026572ac0e5e03d7322deb546d60f9e6e Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:15 -0800 Subject: [PATCH] sky2: add Yukon-EC ultra support Add support for Yukon-EC Ultra chip as implemented in SysKonnect's driver version 8.26. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index e6f71c1232b..aff347f1ea4 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -114,9 +114,11 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4347) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, { 0 } }; @@ -130,6 +132,7 @@ static const char *yukon_name[] = { [CHIP_ID_YUKON_LITE - CHIP_ID_YUKON] = "Lite", /* 0xb0 */ [CHIP_ID_YUKON_LP - CHIP_ID_YUKON] = "LP", /* 0xb2 */ [CHIP_ID_YUKON_XL - CHIP_ID_YUKON] = "XL", /* 0xb3 */ + [CHIP_ID_YUKON_EC_U - CHIP_ID_YUKON] = "EC Ultra", /* 0xb4 */ [CHIP_ID_YUKON_EC - CHIP_ID_YUKON] = "EC", /* 0xb6 */ [CHIP_ID_YUKON_FE - CHIP_ID_YUKON] = "FE", /* 0xb7 */ @@ -599,6 +602,18 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) /* Configure Tx MAC FIFO */ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR); sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON); + + if (hw->chip_id == CHIP_ID_YUKON_EC_U) { + sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8); + sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8); + if (hw->dev[port]->mtu > ETH_DATA_LEN) { + /* set Tx GMAC FIFO Almost Empty Threshold */ + sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR), 0x180); + /* Disable Store & Forward mode for TX */ + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_DIS); + } + } + } static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) @@ -984,6 +999,10 @@ static int sky2_up(struct net_device *dev) RB_RST_SET); sky2_qset(hw, txqaddr[port], 0x600); + if (hw->chip_id == CHIP_ID_YUKON_EC_U) + sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0); + + sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, TX_RING_SIZE - 1); @@ -1553,6 +1572,9 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) return -EINVAL; + if (hw->chip_id == CHIP_ID_YUKON_EC_U && new_mtu > ETH_DATA_LEN) + return -EINVAL; + if (!netif_running(dev)) { dev->mtu = new_mtu; return 0; @@ -1972,6 +1994,7 @@ static inline u32 sky2_khz(const struct sky2_hw *hw) { switch (hw->chip_id) { case CHIP_ID_YUKON_EC: + case CHIP_ID_YUKON_EC_U: return 125000; /* 125 Mhz */ case CHIP_ID_YUKON_FE: return 100000; /* 100 Mhz */ @@ -2796,7 +2819,9 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, sky2->port = port; - dev->features |= NETIF_F_LLTX | NETIF_F_TSO; + dev->features |= NETIF_F_LLTX; + if (hw->chip_id != CHIP_ID_YUKON_EC_U) + dev->features |= NETIF_F_TSO; if (highmem) dev->features |= NETIF_F_HIGHDMA; dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; -- cgit v1.2.3 From a018e3305fe1e500e28830666b1757b75c6b4df5 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:16 -0800 Subject: [PATCH] sky2: handle DMA boundary crossing Handle the case of DMA spanning 32 bit boundary. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index aff347f1ea4..2253140ff4d 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -723,11 +723,17 @@ static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2) return le; } +/* Return high part of DMA address (could be 32 or 64 bit) */ +static inline u32 high32(dma_addr_t a) +{ + return (a >> 16) >> 16; +} + /* Build description to hardware about buffer */ static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re) { struct sky2_rx_le *le; - u32 hi = (re->mapaddr >> 16) >> 16; + u32 hi = high32(re->mapaddr); re->idx = sky2->rx_put; if (sky2->rx_addr64 != hi) { @@ -735,7 +741,7 @@ static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re) le->addr = cpu_to_le32(hi); le->ctrl = 0; le->opcode = OP_ADDR64 | HW_OWNER; - sky2->rx_addr64 = hi; + sky2->rx_addr64 = high32(re->mapaddr + re->maplen); } le = sky2_next_rx(sky2); @@ -1100,17 +1106,17 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) len = skb_headlen(skb); mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); - addr64 = (mapping >> 16) >> 16; + addr64 = high32(mapping); re = sky2->tx_ring + sky2->tx_prod; - /* Send high bits if changed */ - if (addr64 != sky2->tx_addr64) { + /* Send high bits if changed or crosses boundary */ + if (addr64 != sky2->tx_addr64 || high32(mapping + len) != sky2->tx_addr64) { le = get_tx_le(sky2); le->tx.addr = cpu_to_le32(addr64); le->ctrl = 0; le->opcode = OP_ADDR64 | HW_OWNER; - sky2->tx_addr64 = addr64; + sky2->tx_addr64 = high32(mapping + len); } /* Check for TCP Segmentation Offload */ -- cgit v1.2.3 From 0a1225769763779288d759e904c4f5a660844ce4 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:17 -0800 Subject: [PATCH] sky2: change netif_rx_schedule_test to __netif_schedule_prep I didn't like the name netif_rx_schedule_test(), in earlier patches and changed to __netif_rx_schedule_prep to be more consistent. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 2253140ff4d..f56de989420 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1961,10 +1961,11 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) if (status & Y2_IS_STAT_BMU) { hw->intr_mask &= ~Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); - prefetch(&hw->st_le[hw->st_idx]); - if (netif_rx_schedule_test(dev0)) + if (likely(__netif_rx_schedule_prep(dev0))) { + prefetch(&hw->st_le[hw->st_idx]); __netif_rx_schedule(dev0); + } } if (status & Y2_IS_IRQ_PHY1) -- cgit v1.2.3 From 018d1c667ef4dce5299dd79d38447840789c97d6 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:18 -0800 Subject: [PATCH] sky2: race with MTU change Avoid possible race conditions when doing MTU and change and shutdown. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f56de989420..1d5c3039f53 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1298,8 +1298,16 @@ static int sky2_down(struct net_device *dev) if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); + /* Stop more packets from being queued */ netif_stop_queue(dev); + /* Disable port IRQ */ + local_irq_disable(); + hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); + sky2_write32(hw, B0_IMSK, hw->intr_mask); + local_irq_enable(); + + sky2_phy_reset(hw, port); /* Stop transmitter */ @@ -1346,6 +1354,8 @@ static int sky2_down(struct net_device *dev) /* turn off LED's */ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); + synchronize_irq(hw->pdev->irq); + sky2_tx_clean(sky2); sky2_rx_clean(sky2); @@ -1586,9 +1596,12 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) return 0; } - local_irq_disable(); sky2_write32(hw, B0_IMSK, 0); + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_stop_queue(dev); + netif_poll_disable(hw->dev[0]); + 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); @@ -1608,9 +1621,10 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) err = sky2_rx_start(sky2); gma_write16(hw, sky2->port, GM_GP_CTRL, ctl); + netif_poll_disable(hw->dev[0]); + netif_wake_queue(dev); sky2_write32(hw, B0_IMSK, hw->intr_mask); - sky2_read32(hw, B0_IMSK); - local_irq_enable(); + return err; } -- cgit v1.2.3 From 2224795d7e4c7f7e44fe21f0fa067d62539308fb Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:19 -0800 Subject: [PATCH] sky2: dual port tx completion Sometimes on dual port cards, one tx complete may cover both ports. To handle that rearrange poll routine to lookup at end. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1d5c3039f53..52c7b188631 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1242,6 +1242,9 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) struct net_device *dev = sky2->netdev; unsigned i; + if (done == sky2->tx_cons) + return; + if (unlikely(netif_msg_tx_done(sky2))) printk(KERN_DEBUG "%s: tx done, up to %u\n", dev->name, done); @@ -1711,16 +1714,18 @@ error: goto resubmit; } -/* Transmit ring index in reported status block is encoded as: - * - * | TXS2 | TXA2 | TXS1 | TXA1 +/* + * Check for transmit complete */ -static inline u16 tx_index(u8 port, u32 status, u16 len) +static inline void sky2_tx_check(struct sky2_hw *hw, int port) { - if (port == 0) - return status & 0xfff; - else - return ((status >> 24) & 0xff) | (len & 0xf) << 8; + struct net_device *dev = hw->dev[port]; + + if (dev && netif_running(dev)) { + sky2_tx_complete(netdev_priv(dev), + sky2_read16(hw, port == 0 + ? STAT_TXA1_RIDX : STAT_TXA2_RIDX)); + } } /* @@ -1803,8 +1808,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) break; case OP_TXINDEXLE: - sky2_tx_complete(sky2, - tx_index(sky2->port, status, length)); + /* pick up transmit status later */ break; default: @@ -1816,6 +1820,8 @@ static int sky2_poll(struct net_device *dev0, int *budget) } exit_loop: + sky2_tx_check(hw, 0); + sky2_tx_check(hw, 1); mmiowb(); -- cgit v1.2.3 From af4ed7e6ba0cba388cf691f3a330089bd46e4d9e Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:21 -0800 Subject: [PATCH] sky2: remove pci-express hacks Eliminate special case tuning for PCI-Express. This code causes receive hangs and doesn't help performance much anyway. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 52c7b188631..c6c3275fc5e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -651,12 +651,12 @@ static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len) } /* Setup Bus Memory Interface */ -static void sky2_qset(struct sky2_hw *hw, u16 q, u32 wm) +static void sky2_qset(struct sky2_hw *hw, u16 q) { sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_RESET); sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_OPER_INIT); sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_FIFO_OP_ON); - sky2_write32(hw, Q_ADDR(q, Q_WM), wm); + sky2_write32(hw, Q_ADDR(q, Q_WM), BMU_WM_DEFAULT); } /* Setup prefetch unit registers. This is the interface between @@ -921,7 +921,7 @@ static int sky2_rx_start(struct sky2_port *sky2) int i; sky2->rx_put = sky2->rx_next = 0; - sky2_qset(hw, rxq, is_pciex(hw) ? 0x80 : 0x600); + sky2_qset(hw, rxq); sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1); rx_set_checksum(sky2); @@ -1004,7 +1004,7 @@ static int sky2_up(struct net_device *dev) sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL), RB_RST_SET); - sky2_qset(hw, txqaddr[port], 0x600); + sky2_qset(hw, txqaddr[port]); if (hw->chip_id == CHIP_ID_YUKON_EC_U) sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0); @@ -2148,20 +2148,6 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53); } - if (is_pciex(hw)) { - u16 pctrl; - - /* change Max. Read Request Size to 2048 bytes */ - pci_read_config_word(hw->pdev, PEX_DEV_CTRL, &pctrl); - pctrl &= ~PEX_DC_MAX_RRS_MSK; - pctrl |= PEX_DC_MAX_RD_RQ_SIZE(4); - - - sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - pci_write_config_word(hw->pdev, PEX_DEV_CTRL, pctrl); - sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); - } - sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); spin_lock_bh(&hw->phy_lock); -- cgit v1.2.3 From 50241c4c59453fae01555f67daa96d799d85b968 Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:22 -0800 Subject: [PATCH] sky2: use pci_register_driver Switch to using pci_register_driver as per current convention. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index c6c3275fc5e..aa79ad4376d 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3106,7 +3106,7 @@ static struct pci_driver sky2_driver = { static int __init sky2_init_module(void) { - return pci_module_init(&sky2_driver); + return pci_register_driver(&sky2_driver); } static void __exit sky2_cleanup_module(void) -- cgit v1.2.3 From 5f4f9dc113a7aedca072851f3a51cc563f8e143f Mon Sep 17 00:00:00 2001 From: "shemminger@osdl.org" Date: Wed, 30 Nov 2005 11:45:23 -0800 Subject: [PATCH] sky2: update version number Update version number and print version in boot message. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index aa79ad4376d..a1884b472cd 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -58,7 +58,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.7" +#define DRV_VERSION "0.9" #define PFX DRV_NAME " " /* @@ -2948,8 +2948,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev, if (err) goto err_out_iounmap; - printk(KERN_INFO PFX "addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n", - pci_resource_start(pdev, 0), pdev->irq, + printk(KERN_INFO PFX "v%s addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n", + DRV_VERSION, pci_resource_start(pdev, 0), pdev->irq, yukon_name[hw->chip_id - CHIP_ID_YUKON], hw->chip_id, hw->chip_rev); @@ -3120,3 +3120,4 @@ module_exit(sky2_cleanup_module); MODULE_DESCRIPTION("Marvell Yukon 2 Gigabit Ethernet driver"); MODULE_AUTHOR("Stephen Hemminger "); MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- cgit v1.2.3 From f89c2b464558a21fd3be8d578b7d13e810fb6b8a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 1 Dec 2005 08:41:32 -0800 Subject: [PATCH] sky2: interrupt not cleared. One of the rearrangements dropped this and it means sky2 spins in napi/interrupt. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index a1884b472cd..7dc5b4821ae 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1739,6 +1739,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) unsigned int work_done = 0; u16 hwidx; + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); -- cgit v1.2.3 From 92f965e8c5433408d5a3805c6882b657eb6cbdb1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:53 -0800 Subject: [PATCH] sky2: don't die if we see chip rev 0xb5 There maybe new versions of Yukon2 in the future, so make the driver more robust to handle this. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 7dc5b4821ae..0f694a917fb 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -128,17 +128,15 @@ MODULE_DEVICE_TABLE(pci, sky2_id_table); static const unsigned txqaddr[] = { Q_XA1, Q_XA2 }; static const unsigned rxqaddr[] = { Q_R1, Q_R2 }; -static const char *yukon_name[] = { - [CHIP_ID_YUKON_LITE - CHIP_ID_YUKON] = "Lite", /* 0xb0 */ - [CHIP_ID_YUKON_LP - CHIP_ID_YUKON] = "LP", /* 0xb2 */ - [CHIP_ID_YUKON_XL - CHIP_ID_YUKON] = "XL", /* 0xb3 */ - [CHIP_ID_YUKON_EC_U - CHIP_ID_YUKON] = "EC Ultra", /* 0xb4 */ - - [CHIP_ID_YUKON_EC - CHIP_ID_YUKON] = "EC", /* 0xb6 */ - [CHIP_ID_YUKON_FE - CHIP_ID_YUKON] = "FE", /* 0xb7 */ +/* This driver supports yukon2 chipset only */ +static const char *yukon2_name[] = { + "XL", /* 0xb3 */ + "EC Ultra", /* 0xb4 */ + "UNKNOWN", /* 0xb5 */ + "EC", /* 0xb6 */ + "FE", /* 0xb7 */ }; - /* Access to external PHY */ static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) { @@ -2951,7 +2949,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, printk(KERN_INFO PFX "v%s addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n", DRV_VERSION, pci_resource_start(pdev, 0), pdev->irq, - yukon_name[hw->chip_id - CHIP_ID_YUKON], + yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL], hw->chip_id, hw->chip_rev); dev = sky2_init_netdev(hw, 0, using_dac); -- cgit v1.2.3 From bdb5c58ebe4301f9c3470cc35adeff1ef7ee99eb Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:55 -0800 Subject: [PATCH] sky2: copy threshold as module parameter Make the copy threshold a module parameter for those who may want to turn it off. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0f694a917fb..f2f8f60a091 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -76,7 +76,6 @@ #define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le)) #define RX_MAX_PENDING (RX_LE_SIZE/2 - 2) #define RX_DEF_PENDING RX_MAX_PENDING -#define RX_COPY_THRESHOLD 256 #define TX_RING_SIZE 512 #define TX_DEF_PENDING (TX_RING_SIZE - 1) @@ -99,6 +98,10 @@ static int debug = -1; /* defaults above */ module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +static int copybreak __read_mostly = 256; +module_param(copybreak, int, 0); +MODULE_PARM_DESC(copybreak, "Receive copy threshold"); + static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, @@ -1653,7 +1656,7 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, if (!(status & GMR_FS_RX_OK)) goto resubmit; - if (length < RX_COPY_THRESHOLD) { + if (length < copybreak) { skb = alloc_skb(length + 2, GFP_ATOMIC); if (!skb) goto resubmit; -- cgit v1.2.3 From fb17358fe31e01baf902a9fd1fce0e29e3493517 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:56 -0800 Subject: [PATCH] sky2: ethtool get/set interrupt coalescing Add support for get/set the interrupt coalescing settings. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 12 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f2f8f60a091..0ab376845e3 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -24,9 +24,6 @@ */ /* - * TODO - * - coalescing setting? - * * TOTEST * - speed setting * - suspend/resume @@ -2019,29 +2016,30 @@ static void sky2_netpoll(struct net_device *dev) #endif /* Chip internal frequency for clock calculations */ -static inline u32 sky2_khz(const struct sky2_hw *hw) +static inline u32 sky2_mhz(const struct sky2_hw *hw) { switch (hw->chip_id) { case CHIP_ID_YUKON_EC: case CHIP_ID_YUKON_EC_U: - return 125000; /* 125 Mhz */ + return 125; /* 125 Mhz */ case CHIP_ID_YUKON_FE: - return 100000; /* 100 Mhz */ + return 100; /* 100 Mhz */ default: /* YUKON_XL */ - return 156000; /* 156 Mhz */ + return 156; /* 156 Mhz */ } } -static inline u32 sky2_ms2clk(const struct sky2_hw *hw, u32 ms) +static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) { - return sky2_khz(hw) * ms; + return sky2_mhz(hw) * us; } -static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us) +static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk) { - return (sky2_khz(hw) * us) / 1000; + return clk / sky2_mhz(hw); } + static int sky2_reset(struct sky2_hw *hw) { u32 ctst; @@ -2169,7 +2167,7 @@ static int sky2_reset(struct sky2_hw *hw) /* Set the list last index */ sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1); - sky2_write32(hw, STAT_TX_TIMER_INI, sky2_ms2clk(hw, 10)); + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); /* These status setup values are copied from SysKonnect's driver */ if (is_ec_a1(hw)) { @@ -2680,6 +2678,97 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } #endif +static int sky2_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + + if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_STOP) + ecmd->tx_coalesce_usecs = 0; + else { + u32 clks = sky2_read32(hw, STAT_TX_TIMER_INI); + ecmd->tx_coalesce_usecs = sky2_clk2us(hw, clks); + } + ecmd->tx_max_coalesced_frames = sky2_read16(hw, STAT_TX_IDX_TH); + + if (sky2_read8(hw, STAT_LEV_TIMER_CTRL) == TIM_STOP) + ecmd->rx_coalesce_usecs = 0; + else { + u32 clks = sky2_read32(hw, STAT_LEV_TIMER_INI); + ecmd->rx_coalesce_usecs = sky2_clk2us(hw, clks); + } + ecmd->rx_max_coalesced_frames = sky2_read8(hw, STAT_FIFO_WM); + + if (sky2_read8(hw, STAT_ISR_TIMER_CTRL) == TIM_STOP) + ecmd->rx_coalesce_usecs_irq = 0; + else { + u32 clks = sky2_read32(hw, STAT_ISR_TIMER_INI); + ecmd->rx_coalesce_usecs_irq = sky2_clk2us(hw, clks); + } + + ecmd->rx_max_coalesced_frames_irq = sky2_read8(hw, STAT_FIFO_ISR_WM); + + return 0; +} + +/* Note: this affect both ports */ +static int sky2_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + 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->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 > 0xffff) + return -EINVAL; + if (ecmd->rx_max_coalesced_frames > 0xff) + return -EINVAL; + if (ecmd->rx_max_coalesced_frames_irq > 0xff) + return -EINVAL; + + if (ecmd->tx_coalesce_usecs == 0) + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); + else { + sky2_write32(hw, STAT_TX_TIMER_INI, + sky2_us2clk(hw, ecmd->tx_coalesce_usecs)); + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); + } + sky2_write16(hw, STAT_TX_IDX_TH, ecmd->tx_max_coalesced_frames); + + if (ecmd->rx_coalesce_usecs == 0) + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_STOP); + else { + sky2_write32(hw, STAT_LEV_TIMER_INI, + sky2_us2clk(hw, ecmd->rx_coalesce_usecs)); + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START); + } + sky2_write8(hw, STAT_FIFO_WM, ecmd->rx_max_coalesced_frames); + + if (ecmd->rx_coalesce_usecs_irq == 0) + sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_STOP); + else { + sky2_write32(hw, STAT_TX_TIMER_INI, + sky2_us2clk(hw, ecmd->rx_coalesce_usecs_irq)); + sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START); + } + sky2_write8(hw, STAT_FIFO_ISR_WM, ecmd->rx_max_coalesced_frames_irq); + return 0; +} + static void sky2_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { @@ -2765,6 +2854,8 @@ static struct ethtool_ops sky2_ethtool_ops = { .get_rx_csum = sky2_get_rx_csum, .set_rx_csum = sky2_set_rx_csum, .get_strings = sky2_get_strings, + .get_coalesce = sky2_get_coalesce, + .set_coalesce = sky2_set_coalesce, .get_ringparam = sky2_get_ringparam, .set_ringparam = sky2_set_ringparam, .get_pauseparam = sky2_get_pauseparam, -- cgit v1.2.3 From 91c86df5a8a44157b456bf1e91fc6d878582e68c Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:57 -0800 Subject: [PATCH] sky2: phy processing in workqueue rather than tasklet Do phy processing in a work queue rather than a tasklet. This means we can let bottom halves run. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 58 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0ab376845e3..2cb6406c963 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -535,9 +536,9 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC)); - spin_lock_bh(&hw->phy_lock); + down(&sky2->phy_sema); sky2_phy_init(hw, port); - spin_unlock_bh(&hw->phy_lock); + up(&sky2->phy_sema); /* MIB clear */ reg = gma_read16(hw, port, GM_PHY_ADDR); @@ -839,9 +840,11 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* fallthru */ case SIOCGMIIREG: { u16 val = 0; - spin_lock_bh(&hw->phy_lock); + + down(&sky2->phy_sema); err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val); - spin_unlock_bh(&hw->phy_lock); + up(&sky2->phy_sema); + data->val_out = val; break; } @@ -850,10 +853,10 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_bh(&hw->phy_lock); + down(&sky2->phy_sema); err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f, data->val_in); - spin_unlock_bh(&hw->phy_lock); + up(&sky2->phy_sema); break; } return err; @@ -1308,6 +1311,7 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, B0_IMSK, hw->intr_mask); local_irq_enable(); + flush_scheduled_work(); sky2_phy_reset(hw, port); @@ -1520,17 +1524,17 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) } /* - * Interrupt from PHY are handled in tasklet (soft irq) + * 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(unsigned long data) +static void sky2_phy_task(void *arg) { - struct sky2_port *sky2 = (struct sky2_port *)data; + struct sky2_port *sky2 = arg; struct sky2_hw *hw = sky2->hw; u16 istatus, phystat; - spin_lock(&hw->phy_lock); + 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); @@ -1558,7 +1562,7 @@ static void sky2_phy_task(unsigned long data) sky2_link_down(sky2); } out: - spin_unlock(&hw->phy_lock); + up(&sky2->phy_sema); local_irq_disable(); hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; @@ -1960,7 +1964,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); sky2_write32(hw, B0_IMSK, hw->intr_mask); - tasklet_schedule(&sky2->phy_task); + schedule_work(&sky2->phy_task); } static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) @@ -2150,10 +2154,8 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK); - spin_lock_bh(&hw->phy_lock); for (i = 0; i < hw->ports; i++) sky2_phy_reset(hw, i); - spin_unlock_bh(&hw->phy_lock); memset(hw->st_le, 0, STATUS_LE_BYTES); hw->st_idx = 0; @@ -2386,10 +2388,10 @@ static int sky2_nway_reset(struct net_device *dev) netif_stop_queue(dev); - spin_lock_irq(&hw->phy_lock); + down(&sky2->phy_sema); sky2_phy_reset(hw, sky2->port); sky2_phy_init(hw, sky2->port); - spin_unlock_irq(&hw->phy_lock); + up(&sky2->phy_sema); return 0; } @@ -2528,11 +2530,10 @@ static void sky2_set_multicast(struct net_device *dev) /* Can have one global because blinking is controlled by * ethtool and that is always under RTNL mutex */ -static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) +static void sky2_led(struct sky2_hw *hw, unsigned port, int on) { u16 pg; - spin_lock_bh(&hw->phy_lock); switch (hw->chip_id) { case CHIP_ID_YUKON_XL: pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); @@ -2562,7 +2563,6 @@ static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on) PHY_M_LED_MO_RX(MO_LED_OFF)); } - spin_unlock_bh(&hw->phy_lock); } /* blink LED's for finding board */ @@ -2573,6 +2573,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) unsigned port = sky2->port; u16 ledctrl, ledover = 0; long ms; + int interrupted; int onoff = 1; if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ)) @@ -2581,7 +2582,7 @@ static int sky2_phys_id(struct net_device *dev, u32 data) ms = data * 1000; /* save initial values */ - spin_lock_bh(&hw->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); @@ -2591,19 +2592,20 @@ static int sky2_phys_id(struct net_device *dev, u32 data) ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL); ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER); } - spin_unlock_bh(&hw->phy_lock); - while (ms > 0) { + interrupted = 0; + while (!interrupted && ms > 0) { sky2_led(hw, port, onoff); onoff = !onoff; - if (msleep_interruptible(250)) - break; /* interrupted */ + up(&sky2->phy_sema); + interrupted = msleep_interruptible(250); + down(&sky2->phy_sema); + ms -= 250; } /* resume regularly scheduled programming */ - spin_lock_bh(&hw->phy_lock); 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); @@ -2613,7 +2615,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(&hw->phy_lock); + up(&sky2->phy_sema); return 0; } @@ -2917,7 +2919,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, sky2->speed = -1; sky2->advertising = sky2_supported_modes(hw); sky2->rx_csum = 1; - tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2); + INIT_WORK(&sky2->phy_task, sky2_phy_task, sky2); + init_MUTEX(&sky2->phy_sema); sky2->tx_pending = TX_DEF_PENDING; sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING; @@ -3027,7 +3030,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev, memset(hw, 0, sizeof(*hw)); hw->pdev = pdev; - spin_lock_init(&hw->phy_lock); hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); if (!hw->regs) { -- cgit v1.2.3 From f2e46561cc1afa82b18b2fc6efc8510ec57c7d7d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:58 -0800 Subject: [PATCH] sky2: no irq disable needed during tx Don't need to disable interrupts during the transmit routine. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 2cb6406c963..eae270d8180 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -868,15 +868,14 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; u16 port = sky2->port; - unsigned long flags; - spin_lock_irqsave(&sky2->tx_lock, flags); + spin_lock(&sky2->tx_lock); sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON); sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON); sky2->vlgrp = grp; - spin_unlock_irqrestore(&sky2->tx_lock, flags); + spin_unlock(&sky2->tx_lock); } static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) @@ -884,16 +883,15 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; u16 port = sky2->port; - unsigned long flags; - spin_lock_irqsave(&sky2->tx_lock, flags); + spin_lock(&sky2->tx_lock); 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; - spin_unlock_irqrestore(&sky2->tx_lock, flags); + spin_unlock(&sky2->tx_lock); } #endif @@ -1072,6 +1070,8 @@ static inline unsigned tx_le_req(const struct sk_buff *skb) * A single packet can generate multiple list elements, and * the number of ring elements will probably be less than the number * of list elements used. + * + * No BH disabling for tx_lock here (like tg3) */ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) { @@ -1079,22 +1079,18 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) struct sky2_hw *hw = sky2->hw; struct sky2_tx_le *le = NULL; struct ring_info *re; - unsigned long flags; unsigned i, len; dma_addr_t mapping; u32 addr64; u16 mss; u8 ctrl; - local_irq_save(flags); - if (!spin_trylock(&sky2->tx_lock)) { - local_irq_restore(flags); + if (!spin_trylock(&sky2->tx_lock)) return NETDEV_TX_LOCKED; - } if (unlikely(tx_avail(sky2) < tx_le_req(skb))) { netif_stop_queue(dev); - spin_unlock_irqrestore(&sky2->tx_lock, flags); + spin_unlock(&sky2->tx_lock); printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", dev->name); @@ -1226,7 +1222,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) out_unlock: mmiowb(); - spin_unlock_irqrestore(&sky2->tx_lock, flags); + spin_unlock(&sky2->tx_lock); dev->trans_start = jiffies; return NETDEV_TX_OK; -- cgit v1.2.3 From 129372d0524c9124d5693f63c1d3c1ce2e3714ce Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:34:59 -0800 Subject: [PATCH] sky2: ring distance optimization Faster way to compute ring distance. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index eae270d8180..2dd46a05fb3 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1039,7 +1039,7 @@ err_out: /* Modular subtraction in ring */ static inline int tx_dist(unsigned tail, unsigned head) { - return (head >= tail ? head : head + TX_RING_SIZE) - tail; + return (head - tail) % TX_RING_SIZE; } /* Number of list elements available for next tx */ -- cgit v1.2.3 From 734d18684695dd1c6a9527b50e01bba4acab4738 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:00 -0800 Subject: [PATCH] sky2: map length optimization Don't need to keep track of mapping length in ring structure because we can get the same info from other info. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 57 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 2dd46a05fb3..b1a5886a48a 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -729,23 +729,23 @@ static inline u32 high32(dma_addr_t a) } /* Build description to hardware about buffer */ -static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re) +static inline void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map) { struct sky2_rx_le *le; - u32 hi = high32(re->mapaddr); + u32 hi = high32(map); + u16 len = sky2->rx_bufsize; - re->idx = sky2->rx_put; if (sky2->rx_addr64 != hi) { le = sky2_next_rx(sky2); le->addr = cpu_to_le32(hi); le->ctrl = 0; le->opcode = OP_ADDR64 | HW_OWNER; - sky2->rx_addr64 = high32(re->mapaddr + re->maplen); + sky2->rx_addr64 = high32(map + len); } le = sky2_next_rx(sky2); - le->addr = cpu_to_le32((u32) re->mapaddr); - le->length = cpu_to_le16(re->maplen); + le->addr = cpu_to_le32((u32) map); + le->length = cpu_to_le16(len); le->ctrl = 0; le->opcode = OP_PACKET | HW_OWNER; } @@ -814,7 +814,7 @@ static void sky2_rx_clean(struct sky2_port *sky2) if (re->skb) { pci_unmap_single(sky2->hw->pdev, - re->mapaddr, re->maplen, + re->mapaddr, sky2->rx_bufsize, PCI_DMA_FROMDEVICE); kfree_skb(re->skb); re->skb = NULL; @@ -895,12 +895,6 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) } #endif -#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) -static inline unsigned rx_size(const struct sky2_port *sky2) -{ - return roundup(sky2->netdev->mtu + ETH_HLEN + 4, 8); -} - /* * Allocate and setup receiver buffer pool. * In case of 64 bit dma, there are 2X as many list elements @@ -915,7 +909,6 @@ static inline unsigned rx_size(const struct sky2_port *sky2) static int sky2_rx_start(struct sky2_port *sky2) { struct sky2_hw *hw = sky2->hw; - unsigned size = rx_size(sky2); unsigned rxq = rxqaddr[sky2->port]; int i; @@ -927,14 +920,13 @@ static int sky2_rx_start(struct sky2_port *sky2) for (i = 0; i < sky2->rx_pending; i++) { struct ring_info *re = sky2->rx_ring + i; - re->skb = dev_alloc_skb(size); + re->skb = dev_alloc_skb(sky2->rx_bufsize); if (!re->skb) goto nomem; re->mapaddr = pci_map_single(hw->pdev, re->skb->data, - size, PCI_DMA_FROMDEVICE); - re->maplen = size; - sky2_rx_add(sky2, re); + sky2->rx_bufsize, PCI_DMA_FROMDEVICE); + sky2_rx_add(sky2, re->mapaddr); } /* Tell chip about available buffers */ @@ -1182,7 +1174,6 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) /* Record the transmit mapping info */ re->skb = skb; re->mapaddr = mapping; - re->maplen = len; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; @@ -1209,7 +1200,6 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; fre->skb = NULL; fre->mapaddr = mapping; - fre->maplen = frag->size; } re->idx = sky2->tx_prod; le->ctrl |= EOP; @@ -1258,8 +1248,8 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) goto out; skb = re->skb; - pci_unmap_single(sky2->hw->pdev, - re->mapaddr, re->maplen, PCI_DMA_TODEVICE); + pci_unmap_single(sky2->hw->pdev, re->mapaddr, + skb_headlen(skb), PCI_DMA_TODEVICE); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct ring_info *fre; @@ -1267,7 +1257,8 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) sky2->tx_ring + (sky2->tx_cons + i + 1) % TX_RING_SIZE; pci_unmap_page(sky2->hw->pdev, fre->mapaddr, - fre->maplen, PCI_DMA_TODEVICE); + skb_shinfo(skb)->frags[i].size, + PCI_DMA_TODEVICE); } dev_kfree_skb_any(skb); @@ -1579,6 +1570,14 @@ static void sky2_tx_timeout(struct net_device *dev) sky2_tx_clean(sky2); } + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +/* Want receive buffer size to be multiple of 64 bits, and incl room for vlan */ +static inline unsigned sky2_buf_size(int mtu) +{ + return roundup(mtu + ETH_HLEN + 4, 8); +} + static int sky2_change_mtu(struct net_device *dev, int new_mtu) { struct sky2_port *sky2 = netdev_priv(dev); @@ -1609,6 +1608,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) sky2_rx_clean(sky2); dev->mtu = new_mtu; + sky2->rx_bufsize = sky2_buf_size(new_mtu); mode = DATA_BLIND_VAL(DATA_BLIND_DEF) | GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); @@ -1639,7 +1639,6 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, { struct ring_info *re = sky2->rx_ring + sky2->rx_next; struct sk_buff *skb = NULL; - const unsigned int bufsize = rx_size(sky2); if (unlikely(netif_msg_rx_status(sky2))) printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n", @@ -1669,25 +1668,24 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, } else { struct sk_buff *nskb; - nskb = dev_alloc_skb(bufsize); + nskb = dev_alloc_skb(sky2->rx_bufsize); if (!nskb) goto resubmit; skb = re->skb; re->skb = nskb; pci_unmap_single(sky2->hw->pdev, re->mapaddr, - re->maplen, PCI_DMA_FROMDEVICE); + sky2->rx_bufsize, PCI_DMA_FROMDEVICE); prefetch(skb->data); re->mapaddr = pci_map_single(sky2->hw->pdev, nskb->data, - bufsize, PCI_DMA_FROMDEVICE); - re->maplen = bufsize; + sky2->rx_bufsize, PCI_DMA_FROMDEVICE); } skb_put(skb, length); resubmit: re->skb->ip_summed = CHECKSUM_NONE; - sky2_rx_add(sky2, re); + sky2_rx_add(sky2, re->mapaddr); /* Tell receiver about new buffers. */ sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put, @@ -2919,6 +2917,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, init_MUTEX(&sky2->phy_sema); sky2->tx_pending = TX_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; -- cgit v1.2.3 From 6cdbbdf3055f4657c9d6ccc79257bbcac1a9a1fc Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:01 -0800 Subject: [PATCH] sky2: tx/rx ring data structure split Split Tx and Rx ring into two different data structures. Tx needs the next value (to handle partial status), and Rx always needs the mapaddr (to handle resubmitting same buffer). Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index b1a5886a48a..1eefacbfcd2 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -958,7 +958,7 @@ static int sky2_up(struct net_device *dev) if (!sky2->tx_le) goto err_out; - sky2->tx_ring = kzalloc(TX_RING_SIZE * sizeof(struct ring_info), + sky2->tx_ring = kcalloc(TX_RING_SIZE, sizeof(struct tx_ring_info), GFP_KERNEL); if (!sky2->tx_ring) goto err_out; @@ -970,7 +970,7 @@ static int sky2_up(struct net_device *dev) goto err_out; memset(sky2->rx_le, 0, RX_LE_BYTES); - sky2->rx_ring = kzalloc(sky2->rx_pending * sizeof(struct ring_info), + sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct ring_info), GFP_KERNEL); if (!sky2->rx_ring) goto err_out; @@ -1070,7 +1070,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; struct sky2_tx_le *le = NULL; - struct ring_info *re; + struct tx_ring_info *re; unsigned i, len; dma_addr_t mapping; u32 addr64; @@ -1173,11 +1173,11 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) /* Record the transmit mapping info */ re->skb = skb; - re->mapaddr = mapping; + pci_unmap_addr_set(re, mapaddr, mapping); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - struct ring_info *fre; + struct tx_ring_info *fre; mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); @@ -1198,9 +1198,9 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) fre = sky2->tx_ring + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; - fre->skb = NULL; - fre->mapaddr = mapping; + pci_unmap_addr_set(fre, mapaddr, mapping); } + re->idx = sky2->tx_prod; le->ctrl |= EOP; @@ -1239,7 +1239,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) spin_lock(&sky2->tx_lock); while (sky2->tx_cons != done) { - struct ring_info *re = sky2->tx_ring + sky2->tx_cons; + struct tx_ring_info *re = sky2->tx_ring + sky2->tx_cons; struct sk_buff *skb; /* Check for partial status */ @@ -1248,15 +1248,17 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) goto out; skb = re->skb; - pci_unmap_single(sky2->hw->pdev, re->mapaddr, + pci_unmap_single(sky2->hw->pdev, + pci_unmap_addr(re, mapaddr), skb_headlen(skb), PCI_DMA_TODEVICE); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - struct ring_info *fre; + struct tx_ring_info *fre; fre = sky2->tx_ring + (sky2->tx_cons + i + 1) % TX_RING_SIZE; - pci_unmap_page(sky2->hw->pdev, fre->mapaddr, + pci_unmap_page(sky2->hw->pdev, + pci_unmap_addr(fre, mapaddr), skb_shinfo(skb)->frags[i].size, PCI_DMA_TODEVICE); } -- cgit v1.2.3 From 0e3ff6aab1ff2d093996bd7b8c9c06c3d823855c Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:02 -0800 Subject: [PATCH] sky2: transmit logic fixes Some transmit side small fixes: * When computing number of list elements per transmit, do full comparision to check for checksuming. * Get rid of racy check for tx_complete * Change stop test to match wake condition. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1eefacbfcd2..8dfd27d4d6e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1051,7 +1051,7 @@ static inline unsigned tx_le_req(const struct sk_buff *skb) if (skb_shinfo(skb)->tso_size) ++count; - if (skb->ip_summed) + if (skb->ip_summed == CHECKSUM_HW) ++count; return count; @@ -1207,7 +1207,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod, &sky2->tx_last_put, TX_RING_SIZE); - if (tx_avail(sky2) < MAX_SKB_TX_LE + 1) + if (tx_avail(sky2) <= MAX_SKB_TX_LE) netif_stop_queue(dev); out_unlock: @@ -1229,8 +1229,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) struct net_device *dev = sky2->netdev; unsigned i; - if (done == sky2->tx_cons) - return; + BUG_ON(done >= TX_RING_SIZE); if (unlikely(netif_msg_tx_done(sky2))) printk(KERN_DEBUG "%s: tx done, up to %u\n", -- cgit v1.2.3 From 13b97b74b964f9da9b5dab6c57708bccd3ada542 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:03 -0800 Subject: [PATCH] sky2: transmit complete index optimization Change transmit complete handling to use the status values in the poll list because they are in-cache, rather than reading non-cached memory for chips status. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 8dfd27d4d6e..ff6557dd079 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1274,7 +1274,7 @@ out: } /* Cleanup all untransmitted buffers, assume transmitter not running */ -static inline void sky2_tx_clean(struct sky2_port *sky2) +static void sky2_tx_clean(struct sky2_port *sky2) { sky2_tx_complete(sky2, sky2->tx_prod); } @@ -1714,14 +1714,16 @@ error: /* * Check for transmit complete */ -static inline void sky2_tx_check(struct sky2_hw *hw, int port) -{ - struct net_device *dev = hw->dev[port]; +#define TX_NO_STATUS 0xffff - if (dev && netif_running(dev)) { - sky2_tx_complete(netdev_priv(dev), - sky2_read16(hw, port == 0 - ? STAT_TXA1_RIDX : STAT_TXA2_RIDX)); +static inline void sky2_tx_check(struct sky2_hw *hw, int port, u16 last) +{ + if (last != TX_NO_STATUS) { + struct net_device *dev = hw->dev[port]; + if (dev && netif_running(dev)) { + struct sky2_port *sky2 = netdev_priv(dev); + sky2_tx_complete(sky2, last); + } } } @@ -1735,6 +1737,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) 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 }; sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); hwidx = sky2_read16(hw, STAT_PUT_IDX); @@ -1806,7 +1809,10 @@ static int sky2_poll(struct net_device *dev0, int *budget) break; case OP_TXINDEXLE: - /* pick up transmit status later */ + /* TX index reports status for both ports */ + tx_done[0] = status & 0xffff; + tx_done[1] = ((status >> 24) & 0xff) + | (u16)(length & 0xf) << 8; break; default: @@ -1818,16 +1824,13 @@ static int sky2_poll(struct net_device *dev0, int *budget) } exit_loop: - sky2_tx_check(hw, 0); - sky2_tx_check(hw, 1); - mmiowb(); + sky2_tx_check(hw, 0, tx_done[0]); + sky2_tx_check(hw, 1, tx_done[1]); + if (work_done < to_do) { - /* - * Another chip workaround, need to restart TX timer if status - * LE was handled. WA_DEV_43_418 - */ + /* need to restart TX timer */ if (is_ec_a1(hw)) { sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); -- cgit v1.2.3 From af2a58acb12f6b19e6f04b730203e199abedc44a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:04 -0800 Subject: [PATCH] sky2: transmit complete routine optimization Hand optimize transmit completion routine. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ff6557dd079..21aeb0c91fa 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1227,6 +1227,8 @@ out_unlock: static void sky2_tx_complete(struct sky2_port *sky2, u16 done) { struct net_device *dev = sky2->netdev; + struct pci_dev *pdev = sky2->hw->pdev; + u16 nxt, put; unsigned i; BUG_ON(done >= TX_RING_SIZE); @@ -1235,39 +1237,34 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) printk(KERN_DEBUG "%s: tx done, up to %u\n", dev->name, done); - spin_lock(&sky2->tx_lock); + for (put = sky2->tx_cons; put != done; put = nxt) { + struct tx_ring_info *re = sky2->tx_ring + put; + struct sk_buff *skb = re->skb; - while (sky2->tx_cons != done) { - struct tx_ring_info *re = sky2->tx_ring + sky2->tx_cons; - struct sk_buff *skb; + nxt = re->idx; + BUG_ON(nxt >= TX_RING_SIZE); /* Check for partial status */ - if (tx_dist(sky2->tx_cons, done) - < tx_dist(sky2->tx_cons, re->idx)) - goto out; + if (tx_dist(put, done) < tx_dist(put, nxt)) + break; skb = re->skb; - pci_unmap_single(sky2->hw->pdev, - pci_unmap_addr(re, mapaddr), + pci_unmap_single(pdev, pci_unmap_addr(re, mapaddr), skb_headlen(skb), PCI_DMA_TODEVICE); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct tx_ring_info *fre; - fre = - sky2->tx_ring + (sky2->tx_cons + i + - 1) % TX_RING_SIZE; - pci_unmap_page(sky2->hw->pdev, - pci_unmap_addr(fre, mapaddr), - skb_shinfo(skb)->frags[i].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, PCI_DMA_TODEVICE); } dev_kfree_skb_any(skb); - - sky2->tx_cons = re->idx; } -out: + spin_lock(&sky2->tx_lock); + sky2->tx_cons = put; if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE) netif_wake_queue(dev); spin_unlock(&sky2->tx_lock); @@ -1742,7 +1739,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); - rmb(); + rmb(); while (hwidx != hw->st_idx) { struct sky2_status_le *le = hw->st_le + hw->st_idx; -- cgit v1.2.3 From 3e4b32e11674a40cf50e48a79764f12988641e20 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:05 -0800 Subject: [PATCH] sky2: interrupt/poll optimization Optimize poll routine by not clearing interrupt till after processing, and checking for more work before re-enable. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 21aeb0c91fa..af9739c1ef5 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1736,7 +1736,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) u16 hwidx; u16 tx_done[2] = { TX_NO_STATUS, TX_NO_STATUS }; - sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); @@ -1754,8 +1753,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; prefetch(hw->st_le + hw->st_idx); - BUG_ON(le->link >= hw->ports || !hw->dev[le->link]); - BUG_ON(le->link >= 2); dev = hw->dev[le->link]; if (dev == NULL || !netif_running(dev)) @@ -1821,12 +1818,13 @@ static int sky2_poll(struct net_device *dev0, int *budget) } exit_loop: + sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); mmiowb(); sky2_tx_check(hw, 0, tx_done[0]); sky2_tx_check(hw, 1, tx_done[1]); - if (work_done < to_do) { + if (sky2_read16(hw, STAT_PUT_IDX) == hw->st_idx) { /* need to restart TX timer */ if (is_ec_a1(hw)) { sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); -- cgit v1.2.3 From 69634ee7366d05b26b2650584bed180edf923125 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:06 -0800 Subject: [PATCH] sky2: interrupt coalescing tuning Adjust interrupt deferral values for better performance and avoid starvation issues under stress. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index af9739c1ef5..0b82dbba16c 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2115,9 +2115,8 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, B0_Y2LED, LED_STAT_ON); - /* Turn on descriptor polling (every 75us) */ - sky2_write32(hw, B28_DPT_INI, sky2_us2clk(hw, 75)); - sky2_write8(hw, B28_DPT_CTRL, DPT_START); + /* Turn off descriptor polling */ + sky2_write32(hw, B28_DPT_CTRL, DPT_STOP); /* Turn off receive timestamp */ sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP); @@ -2162,8 +2161,6 @@ static int sky2_reset(struct sky2_hw *hw) /* Set the list last index */ sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1); - sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); - /* These status setup values are copied from SysKonnect's driver */ if (is_ec_a1(hw)) { /* WA for dev. #4.3 */ @@ -2174,21 +2171,20 @@ static int sky2_reset(struct sky2_hw *hw) /* 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, 0x000a); - - /* set Status-FIFO watermark */ - sky2_write8(hw, STAT_FIFO_WM, 0x10); + 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, 0x10); - - else /* WA dev 4.109 */ - sky2_write8(hw, STAT_FIFO_ISR_WM, 0x04); + sky2_write8(hw, STAT_FIFO_ISR_WM, 4); + else + sky2_write8(hw, STAT_FIFO_ISR_WM, 16); - sky2_write32(hw, STAT_ISR_TIMER_INI, 0x0190); + sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); + sky2_write32(hw, STAT_LEV_TIMER_INI, sky2_us2clk(hw, 100)); + sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 20)); } /* enable status unit */ -- cgit v1.2.3 From 8cc048e338ad4034255f3ff72ef1f631963e1263 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:07 -0800 Subject: [PATCH] sky2: handle tx timeout Put more logic in to try and handle transmit timeout recovery. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 0b82dbba16c..e1733aa1779 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -661,7 +661,7 @@ static void sky2_qset(struct sky2_hw *hw, u16 q) /* Setup prefetch unit registers. This is the interface between * hardware and driver list elements */ -static inline void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr, +static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr, u64 addr, u32 last) { sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); @@ -1558,14 +1558,25 @@ out: 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]; if (netif_msg_timer(sky2)) printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); - sky2_write32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR), BMU_STOP); - sky2_read32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR)); + netif_stop_queue(dev); + + sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP); + sky2_read32(hw, Q_ADDR(txq, Q_CSR)); + + sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); sky2_tx_clean(sky2); + + sky2_qset(hw, txq); + sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1); + + netif_wake_queue(dev); } -- cgit v1.2.3 From 8c463ef7928d7a42bb9ca410df9b294dc01c1850 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:08 -0800 Subject: [PATCH] sky2: quiet ring full message in case of race Don't print ring full message if we lose race. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index e1733aa1779..54947ae9948 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1081,11 +1081,16 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_LOCKED; if (unlikely(tx_avail(sky2) < tx_le_req(skb))) { - netif_stop_queue(dev); + /* There is a known but harmless race with lockless tx + * and netif_stop_queue. + */ + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); + printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", + dev->name); + } spin_unlock(&sky2->tx_lock); - printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", - dev->name); return NETDEV_TX_BUSY; } -- cgit v1.2.3 From d70cd51ac0585a9273abea4c333e8f0dd8385d27 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:09 -0800 Subject: [PATCH] sky2: prefetch tuning Add a couple more prefetches to where we walk the rings. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 54947ae9948..39916e7cf6f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -1248,6 +1249,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) nxt = re->idx; BUG_ON(nxt >= TX_RING_SIZE); + prefetch(sky2->tx_ring + nxt); /* Check for partial status */ if (tx_dist(put, done) < tx_dist(put, nxt)) @@ -1659,6 +1661,7 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, sky2->netdev->name, sky2->rx_next, status, length); sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; + prefetch(sky2->rx_ring + sky2->rx_next); if (status & GMR_FS_ANY_ERR) goto error; -- cgit v1.2.3 From 585b56012f08a6edc598d17b28a6c434134bfaff Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:10 -0800 Subject: [PATCH] sky2: turn on tx flow control Turn symmetric flow control on by default. This was an oversight in the initial versions. Signed-off-by: Stephen HEmminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 39916e7cf6f..cdcbf727f88 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2921,7 +2921,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, spin_lock_init(&sky2->tx_lock); /* Auto speed and flow control */ sky2->autoneg = AUTONEG_ENABLE; - sky2->tx_pause = 0; + sky2->tx_pause = 1; sky2->rx_pause = 1; sky2->duplex = -1; sky2->speed = -1; -- cgit v1.2.3 From 75d070c56fa77913b165947f046304efcd38e516 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:11 -0800 Subject: [PATCH] sky2: disable rx checksum on Yukon XL Under load, my SysKonnect SK-9S22 sees duplicate checksums from earlier packets. Doesn't happen on other platforms so just disable receive checksum there. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index cdcbf727f88..b9c0aafb031 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2926,7 +2926,13 @@ 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; + + /* 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; -- cgit v1.2.3 From fed954dafc0ab03e00501862df397e8f8e2211fd Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Dec 2005 11:35:12 -0800 Subject: [PATCH] sky2: version 0.10 Change version number. Still don't have enough history on this to call it 1.0 yet. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index b9c0aafb031..7af72cdd473 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -57,7 +57,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.9" +#define DRV_VERSION "0.10" #define PFX DRV_NAME " " /* -- cgit v1.2.3 From 1b537565a85cbe5cc9d5174d348a9014381af718 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 20 Dec 2005 15:08:07 -0800 Subject: [PATCH] sky2: handle out of memory on admin changes Don't die if we run out of memory on mtu or ring parameter change. For other admin operations, don't rebuild Rx ring, just restart the PHY. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 84 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 32 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 7af72cdd473..1d183d5177a 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -481,6 +481,14 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); } +/* Force a renegotiation */ +static void sky2_phy_reinit(struct sky2_port *sky2) +{ + down(&sky2->phy_sema); + sky2_phy_init(sky2->hw, sky2->port); + up(&sky2->phy_sema); +} + static void sky2_mac_init(struct sky2_hw *hw, unsigned port) { struct sky2_port *sky2 = netdev_priv(hw->dev[port]); @@ -1014,18 +1022,22 @@ static int sky2_up(struct net_device *dev) return 0; err_out: - if (sky2->rx_le) + if (sky2->rx_le) { pci_free_consistent(hw->pdev, RX_LE_BYTES, sky2->rx_le, sky2->rx_le_map); - if (sky2->tx_le) + sky2->rx_le = NULL; + } + if (sky2->tx_le) { pci_free_consistent(hw->pdev, TX_RING_SIZE * sizeof(struct sky2_tx_le), sky2->tx_le, sky2->tx_le_map); - if (sky2->tx_ring) - kfree(sky2->tx_ring); - if (sky2->rx_ring) - kfree(sky2->rx_ring); + sky2->tx_le = NULL; + } + kfree(sky2->tx_ring); + kfree(sky2->rx_ring); + sky2->tx_ring = NULL; + sky2->rx_ring = NULL; return err; } @@ -1291,6 +1303,10 @@ static int sky2_down(struct net_device *dev) unsigned port = sky2->port; u16 ctrl; + /* Never really got started! */ + if (!sky2->tx_le) + return 0; + if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); @@ -1365,6 +1381,12 @@ static int sky2_down(struct net_device *dev) sky2->tx_le, sky2->tx_le_map); kfree(sky2->tx_ring); + sky2->tx_le = NULL; + sky2->rx_le = NULL; + + sky2->rx_ring = NULL; + sky2->tx_ring = NULL; + return 0; } @@ -1636,12 +1658,17 @@ 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); - gma_write16(hw, sky2->port, GM_GP_CTRL, ctl); - - netif_poll_disable(hw->dev[0]); - netif_wake_queue(dev); sky2_write32(hw, B0_IMSK, hw->intr_mask); + if (err) + dev_close(dev); + else { + gma_write16(hw, sky2->port, GM_GP_CTRL, ctl); + + netif_poll_enable(hw->dev[0]); + netif_wake_queue(dev); + } + return err; } @@ -2315,10 +2342,8 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) sky2->autoneg = ecmd->autoneg; sky2->advertising = ecmd->advertising; - if (netif_running(dev)) { - sky2_down(dev); - sky2_up(dev); - } + if (netif_running(dev)) + sky2_phy_reinit(sky2); return 0; } @@ -2389,17 +2414,11 @@ static u32 sky2_get_msglevel(struct net_device *netdev) static int sky2_nway_reset(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); - struct sky2_hw *hw = sky2->hw; if (sky2->autoneg != AUTONEG_ENABLE) return -EINVAL; - netif_stop_queue(dev); - - down(&sky2->phy_sema); - sky2_phy_reset(hw, sky2->port); - sky2_phy_init(hw, sky2->port); - up(&sky2->phy_sema); + sky2_phy_reinit(sky2); return 0; } @@ -2477,20 +2496,20 @@ static int sky2_set_mac_address(struct net_device *dev, void *p) { struct sky2_port *sky2 = netdev_priv(dev); struct sockaddr *addr = p; - int err = 0; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - sky2_down(dev); memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port * 8, dev->dev_addr, ETH_ALEN); memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port * 8, dev->dev_addr, ETH_ALEN); - if (dev->flags & IFF_UP) - err = sky2_up(dev); - return err; + + if (netif_running(dev)) + sky2_phy_reinit(sky2); + + return 0; } static void sky2_set_multicast(struct net_device *dev) @@ -2648,10 +2667,7 @@ static int sky2_set_pauseparam(struct net_device *dev, sky2->tx_pause = ecmd->tx_pause != 0; sky2->rx_pause = ecmd->rx_pause != 0; - if (netif_running(dev)) { - sky2_down(dev); - err = sky2_up(dev); - } + sky2_phy_reinit(sky2); return err; } @@ -2813,8 +2829,11 @@ static int sky2_set_ringparam(struct net_device *dev, sky2->rx_pending = ering->rx_pending; sky2->tx_pending = ering->tx_pending; - if (netif_running(dev)) + if (netif_running(dev)) { err = sky2_up(dev); + if (err) + dev_close(dev); + } return err; } @@ -3195,7 +3214,8 @@ static int sky2_resume(struct pci_dev *pdev) if (dev) { if (netif_running(dev)) { netif_device_attach(dev); - sky2_up(dev); + if (sky2_up(dev)) + dev_close(dev); } } } -- cgit v1.2.3 From 6ed995bb29f042eece8392d9bae0b83f84bcdb23 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 20 Dec 2005 15:08:08 -0800 Subject: [PATCH] sky2: don't lose multicast addresses Don't lose multicast addresses when link goes down or ring parameters change. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 1d183d5177a..c7b4da1d717 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1478,8 +1478,6 @@ static void sky2_link_down(struct sky2_port *sky2) | PHY_M_AN_ASP); } - sky2_phy_reset(hw, port); - netif_carrier_off(sky2->netdev); netif_stop_queue(sky2->netdev); @@ -2833,6 +2831,8 @@ static int sky2_set_ringparam(struct net_device *dev, err = sky2_up(dev); if (err) dev_close(dev); + else + sky2_set_multicast(dev); } return err; -- cgit v1.2.3 From 6e15b7124990e4b91645601e7a90d3fe72873336 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 20 Dec 2005 15:08:09 -0800 Subject: [PATCH] sky2: handle hardware packet overrun It is possible for hardware to get confused when an oversized frame is received. In that case, just drop the packet and increment a counter. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index c7b4da1d717..9b428e423c3 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1694,6 +1694,9 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, if (!(status & GMR_FS_RX_OK)) goto resubmit; + if ((status >> 16) != length || length > sky2->rx_bufsize) + goto oversize; + if (length < copybreak) { skb = alloc_skb(length + 2, GFP_ATOMIC); if (!skb) @@ -1735,7 +1738,13 @@ resubmit: return skb; +oversize: + ++sky2->net_stats.rx_over_errors; + goto resubmit; + error: + ++sky2->net_stats.rx_errors; + if (netif_msg_rx_err(sky2)) printk(KERN_INFO PFX "%s: rx error, status 0x%x length %d\n", sky2->netdev->name, status, length); -- cgit v1.2.3 From c4c48d83e9ffb1c25cccd59d5ea9dda6ded01faf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 20 Dec 2005 15:08:10 -0800 Subject: [PATCH] sky2: version 0.11 Version number change. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/sky2.c') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 9b428e423c3..f5d697c0c03 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -57,7 +57,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "0.10" +#define DRV_VERSION "0.11" #define PFX DRV_NAME " " /* -- cgit v1.2.3