#include <linux/crc32.h>
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/dma-mapping.h>
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.20"
+#define DRV_VERSION "1.25"
#define PFX DRV_NAME " "
/*
#define RX_MAX_PENDING (RX_LE_SIZE/6 - 2)
#define RX_DEF_PENDING RX_MAX_PENDING
-#define TX_RING_SIZE 512
-#define TX_DEF_PENDING (TX_RING_SIZE - 1)
-#define TX_MIN_PENDING 64
-#define MAX_SKB_TX_LE (4 + (sizeof(dma_addr_t)/sizeof(u32))*MAX_SKB_FRAGS)
+/* This is the worst case number of transmit list elements for a single skb:
+ VLAN:GSO + CKSUM + Data + skb_frags * DMA */
+#define MAX_SKB_TX_LE (2 + (sizeof(dma_addr_t)/sizeof(u32))*(MAX_SKB_FRAGS+1))
+#define TX_MIN_PENDING (MAX_SKB_TX_LE+1)
+#define TX_MAX_PENDING 4096
+#define TX_DEF_PENDING 127
#define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */
#define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le))
module_param(disable_msi, int, 0);
MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
-static const struct pci_device_id sky2_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4352) }, /* 88E8038 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4353) }, /* 88E8039 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4354) }, /* 88E8040 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4355) }, /* 88E8040T */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4356) }, /* 88EC033 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4357) }, /* 88E8042 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x435A) }, /* 88E8048 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436C) }, /* 88E8072 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
{ 0 }
};
static const unsigned rxqaddr[] = { Q_R1, Q_R2 };
static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 };
-/* This driver supports yukon2 chipset only */
-static const char *yukon2_name[] = {
- "XL", /* 0xb3 */
- "EC Ultra", /* 0xb4 */
- "Extreme", /* 0xb5 */
- "EC", /* 0xb6 */
- "FE", /* 0xb7 */
- "FE+", /* 0xb8 */
-};
-
static void sky2_set_multicast(struct net_device *dev);
/* Access to PHY via serial interconnect */
sky2_read32(hw, B2_GP_IO);
}
+
+ /* Turn on "driver loaded" LED */
+ sky2_write16(hw, B0_CTST, Y2_LED_STAT_ON);
}
static void sky2_power_aux(struct sky2_hw *hw)
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 (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL)
+ /* switch power to VAUX if supported and PME from D3cold */
+ if ( (sky2_read32(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
+ pci_pme_capable(hw->pdev, PCI_D3cold))
sky2_write8(hw, B0_POWER_CTRL,
(PC_VAUX_ENA | PC_VCC_ENA |
PC_VAUX_ON | PC_VCC_OFF));
+
+ /* turn off "driver loaded LED" */
+ sky2_write16(hw, B0_CTST, Y2_LED_STAT_OFF);
}
static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port)
struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;
- if (sky2->autoneg == AUTONEG_ENABLE &&
+ if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
!(hw->flags & SKY2_HW_NEWER_PHY)) {
u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
/* downshift on PHY 88E1112 and 88E1149 is changed */
- if (sky2->autoneg == AUTONEG_ENABLE
+ if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED)
&& (hw->flags & SKY2_HW_NEWER_PHY)) {
/* set downshift counter to 3x and enable downshift */
ctrl &= ~PHY_M_PC_DSC_MSK;
adv = PHY_AN_CSMA;
reg = 0;
- if (sky2->autoneg == AUTONEG_ENABLE) {
+ if (sky2->flags & SKY2_FLAG_AUTO_SPEED) {
if (sky2_is_copper(hw)) {
if (sky2->advertising & ADVERTISED_1000baseT_Full)
ct1000 |= PHY_M_1000C_AFD;
if (sky2->advertising & ADVERTISED_10baseT_Half)
adv |= PHY_M_AN_10_HD;
- adv |= copper_fc_adv[sky2->flow_mode];
} else { /* special defines for FIBER (88E1040S only) */
if (sky2->advertising & ADVERTISED_1000baseT_Full)
adv |= PHY_M_AN_1000X_AFD;
if (sky2->advertising & ADVERTISED_1000baseT_Half)
adv |= PHY_M_AN_1000X_AHD;
-
- adv |= fiber_fc_adv[sky2->flow_mode];
}
/* Restart Auto-negotiation */
/* forced speed/duplex settings */
ct1000 = PHY_M_1000C_MSE;
- /* Disable auto update for duplex flow control and speed */
- reg |= GM_GPCR_AU_ALL_DIS;
+ /* Disable auto update for duplex flow control and duplex */
+ reg |= GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_SPD_DIS;
switch (sky2->speed) {
case SPEED_1000:
ctrl |= PHY_CT_DUP_MD;
} else if (sky2->speed < SPEED_1000)
sky2->flow_mode = FC_NONE;
+ }
-
+ if (sky2->flags & SKY2_FLAG_AUTO_PAUSE) {
+ if (sky2_is_copper(hw))
+ adv |= copper_fc_adv[sky2->flow_mode];
+ else
+ adv |= fiber_fc_adv[sky2->flow_mode];
+ } else {
+ reg |= GM_GPCR_AU_FCT_DIS;
reg |= gm_fc_disable[sky2->flow_mode];
/* Forward pause packets to GMAC? */
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;
+ ledover |= PHY_M_LED_MO_RX(MO_LED_OFF);
}
- if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
- hw->chip_rev == CHIP_REV_YU_EC_U_A1) {
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_UL_2) {
/* apply fixes in PHY AFE */
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255);
gm_phy_write(hw, port, 0x18, 0xaa99);
gm_phy_write(hw, port, 0x17, 0x2011);
- /* fix for IEEE A/B Symmetry failure in 1000BASE-T */
- gm_phy_write(hw, port, 0x18, 0xa204);
- gm_phy_write(hw, port, 0x17, 0x2002);
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+ /* fix for IEEE A/B Symmetry failure in 1000BASE-T */
+ gm_phy_write(hw, port, 0x18, 0xa204);
+ gm_phy_write(hw, port, 0x17, 0x2002);
+ }
/* set page register to 0 */
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
/* apply workaround for integrated resistors calibration */
gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
- } else if (hw->chip_id != CHIP_ID_YUKON_EX) {
+ } else if (hw->chip_id != CHIP_ID_YUKON_EX &&
+ hw->chip_id < CHIP_ID_YUKON_SUPR) {
/* no effect on Yukon-XL */
gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
- if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) {
+ if ( !(sky2->flags & SKY2_FLAG_AUTO_SPEED)
+ || sky2->speed == SPEED_100) {
/* turn on 100 Mbps LED (LED_LINK100) */
- ledover |= PHY_M_LED_MO_100;
+ ledover |= PHY_M_LED_MO_100(MO_LED_ON);
}
if (ledover)
}
/* Enable phy interrupt on auto-negotiation complete (or link up) */
- if (sky2->autoneg == AUTONEG_ENABLE)
+ if (sky2->flags & SKY2_FLAG_AUTO_SPEED)
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_phy_power(struct sky2_hw *hw, unsigned port, int onoff)
+static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
+static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA };
+
+static void sky2_phy_power_up(struct sky2_hw *hw, unsigned port)
{
u32 reg1;
- static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
- static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA };
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
- /* Turn on/off phy power saving */
- if (onoff)
- reg1 &= ~phy_power[port];
- else
- reg1 |= phy_power[port];
+ reg1 &= ~phy_power[port];
- if (onoff && hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
reg1 |= coma_mode[port];
sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
- reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+ sky2_pci_read32(hw, PCI_DEV_REG1);
- udelay(100);
+ if (hw->chip_id == CHIP_ID_YUKON_FE)
+ gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_ANE);
+ else if (hw->flags & SKY2_HW_ADV_POWER_CTL)
+ sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+}
+
+static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
+{
+ u32 reg1;
+ u16 ctrl;
+
+ /* release GPHY Control reset */
+ sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+
+ /* release GMAC reset */
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+
+ if (hw->flags & SKY2_HW_NEWER_PHY) {
+ /* select page 2 to access MAC control register */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ /* allow GMII Power Down */
+ ctrl &= ~PHY_M_MAC_GMIF_PUP;
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+ /* set page register back to 0 */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+ }
+
+ /* setup General Purpose Control Register */
+ gma_write16(hw, port, GM_GP_CTRL,
+ GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 |
+ GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS |
+ GM_GPCR_AU_SPD_DIS);
+
+ if (hw->chip_id != CHIP_ID_YUKON_EC) {
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
+ /* select page 2 to access MAC control register */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ /* enable Power Down */
+ ctrl |= PHY_M_PC_POW_D_ENA;
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+ /* set page register back to 0 */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+ }
+
+ /* set IEEE compatible Power Down Mode (dev. #4.99) */
+ gm_phy_write(hw, port, PHY_MARV_CTRL, PHY_CT_PDOWN);
+ }
+
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+ reg1 |= phy_power[port]; /* set PHY to PowerDown/COMA Mode */
+ sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
}
/* Force a renegotiation */
sky2->advertising &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full);
sky2->flow_mode = FC_NONE;
- sky2_phy_power(hw, port, 1);
- sky2_phy_reinit(sky2);
+
+ spin_lock_bh(&sky2->phy_lock);
+ sky2_phy_power_up(hw, port);
+ sky2_phy_init(hw, port);
+ spin_unlock_bh(&sky2->phy_lock);
sky2->flow_mode = save_mode;
sky2->advertising = ctrl;
if (sky2->wol & WAKE_MAGIC)
ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT;
else
- ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;;
+ ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;
ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT;
sky2_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl);
sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
spin_lock_bh(&sky2->phy_lock);
+ sky2_phy_power_up(hw, port);
sky2_phy_init(hw, port);
spin_unlock_bh(&sky2->phy_lock);
sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
/* On chips without ram buffer, pause is controled by MAC level */
- if (sky2_read8(hw, B2_E_0) == 0) {
+ if (!(hw->flags & SKY2_HW_RAM_BUFFER)) {
sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
* hardware and driver list elements
*/
static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
- u64 addr, u32 last)
+ dma_addr_t addr, u32 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_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), upper_32_bits(addr));
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), lower_32_bits(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)
+static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2, u16 *slot)
{
- struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
+ struct sky2_tx_le *le = sky2->tx_le + *slot;
+ struct tx_ring_info *re = sky2->tx_ring + *slot;
- sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE);
+ *slot = RING_NEXT(*slot, sky2->tx_ring_size);
+ re->flags = 0;
+ re->skb = NULL;
le->ctrl = 0;
return le;
}
sky2->tx_tcpsum = 0;
sky2->tx_last_mss = 0;
- le = get_tx_le(sky2);
+ le = get_tx_le(sky2, &sky2->tx_prod);
le->addr = 0;
le->opcode = OP_ADDR64 | HW_OWNER;
-}
-
-static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
- struct sky2_tx_le *le)
-{
- return sky2->tx_ring + (le - sky2->tx_le);
+ sky2->tx_last_upper = 0;
}
/* Update chip's next pointer */
}
le = sky2_next_rx(sky2);
- le->addr = cpu_to_le32((u32) map);
+ le->addr = cpu_to_le32(lower_32_bits(map));
le->length = cpu_to_le16(len);
le->opcode = op | HW_OWNER;
}
}
-static void sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
+static int sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
unsigned size)
{
struct sk_buff *skb = re->skb;
int i;
re->data_addr = pci_map_single(pdev, skb->data, size, PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(pdev, re->data_addr)))
+ return -EIO;
+
pci_unmap_len_set(re, data_size, size);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_shinfo(skb)->frags[i].page_offset,
skb_shinfo(skb)->frags[i].size,
PCI_DMA_FROMDEVICE);
+ return 0;
}
static void sky2_rx_unmap_skb(struct pci_dev *pdev, struct rx_ring_info *re)
sky2_write32(sky2->hw,
Q_ADDR(rxqaddr[sky2->port], Q_CSR),
- sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+ (sky2->flags & SKY2_FLAG_RX_CHECKSUM)
+ ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
}
/*
}
case SIOCSMIIREG:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
spin_lock_bh(&sky2->phy_lock);
err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
data->val_in);
}
#ifdef SKY2_VLAN_TAG_USED
-static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+static void sky2_set_vlan_mode(struct sky2_hw *hw, u16 port, bool onoff)
{
- struct sky2_port *sky2 = netdev_priv(dev);
- struct sky2_hw *hw = sky2->hw;
- u16 port = sky2->port;
-
- netif_tx_lock_bh(dev);
- napi_disable(&hw->napi);
-
- sky2->vlgrp = grp;
- if (grp) {
+ if (onoff) {
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T),
RX_VLAN_STRIP_ON);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
TX_VLAN_TAG_OFF);
}
+}
+
+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;
+
+ netif_tx_lock_bh(dev);
+ napi_disable(&hw->napi);
+
+ sky2->vlgrp = grp;
+ sky2_set_vlan_mode(hw, port, grp != NULL);
sky2_read32(hw, B0_Y2_SP_LISR);
napi_enable(&hw->napi);
}
#endif
+/* Amount of required worst case padding in rx buffer */
+static inline unsigned sky2_rx_pad(const struct sky2_hw *hw)
+{
+ return (hw->flags & SKY2_HW_RAM_BUFFER) ? 8 : 2;
+}
+
/*
* Allocate an skb for receiving. If the MTU is large enough
* make the skb non-linear with a fragment list of pages.
struct sk_buff *skb;
int i;
- if (sky2->hw->flags & SKY2_HW_FIFO_HANG_CHECK) {
+ skb = netdev_alloc_skb(sky2->netdev,
+ sky2->rx_data_size + sky2_rx_pad(sky2->hw));
+ if (!skb)
+ goto nomem;
+
+ if (sky2->hw->flags & SKY2_HW_RAM_BUFFER) {
unsigned char *start;
/*
* Workaround for a bug in FIFO that cause hang
* The buffer returned from netdev_alloc_skb is
* aligned except if slab debugging is enabled.
*/
- skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + 8);
- if (!skb)
- goto nomem;
start = PTR_ALIGN(skb->data, 8);
skb_reserve(skb, start - skb->data);
- } else {
- skb = netdev_alloc_skb(sky2->netdev,
- sky2->rx_data_size + NET_IP_ALIGN);
- if (!skb)
- goto nomem;
+ } else
skb_reserve(skb, NET_IP_ALIGN);
- }
for (i = 0; i < sky2->rx_nfrags; i++) {
struct page *page = alloc_page(GFP_ATOMIC);
if (!re->skb)
goto nomem;
- sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size);
+ if (sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size)) {
+ dev_kfree_skb(re->skb);
+ re->skb = NULL;
+ goto nomem;
+ }
+
sky2_rx_submit(sky2, re);
}
return -ENOMEM;
}
+static int sky2_alloc_buffers(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+
+ /* must be power of 2 */
+ sky2->tx_le = pci_alloc_consistent(hw->pdev,
+ sky2->tx_ring_size *
+ sizeof(struct sky2_tx_le),
+ &sky2->tx_le_map);
+ if (!sky2->tx_le)
+ goto nomem;
+
+ sky2->tx_ring = kcalloc(sky2->tx_ring_size, sizeof(struct tx_ring_info),
+ GFP_KERNEL);
+ if (!sky2->tx_ring)
+ goto nomem;
+
+ sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES,
+ &sky2->rx_le_map);
+ if (!sky2->rx_le)
+ goto nomem;
+ memset(sky2->rx_le, 0, RX_LE_BYTES);
+
+ sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
+ GFP_KERNEL);
+ if (!sky2->rx_ring)
+ goto nomem;
+
+ return 0;
+nomem:
+ return -ENOMEM;
+}
+
+static void sky2_free_buffers(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+
+ if (sky2->rx_le) {
+ pci_free_consistent(hw->pdev, RX_LE_BYTES,
+ sky2->rx_le, sky2->rx_le_map);
+ sky2->rx_le = NULL;
+ }
+ if (sky2->tx_le) {
+ pci_free_consistent(hw->pdev,
+ sky2->tx_ring_size * sizeof(struct sky2_tx_le),
+ sky2->tx_le, sky2->tx_le_map);
+ sky2->tx_le = NULL;
+ }
+ kfree(sky2->tx_ring);
+ kfree(sky2->rx_ring);
+
+ sky2->tx_ring = NULL;
+ sky2->rx_ring = NULL;
+}
+
/* Bring up network interface. */
static int sky2_up(struct net_device *dev)
{
struct sky2_hw *hw = sky2->hw;
unsigned port = sky2->port;
u32 imask, ramsize;
- int cap, err = -ENOMEM;
+ int cap, err;
struct net_device *otherdev = hw->dev[sky2->port^1];
/*
}
- if (netif_msg_ifup(sky2))
- printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
-
netif_carrier_off(dev);
- /* 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 = kcalloc(TX_RING_SIZE, sizeof(struct tx_ring_info),
- GFP_KERNEL);
- if (!sky2->tx_ring)
+ err = sky2_alloc_buffers(sky2);
+ if (err)
goto err_out;
tx_init(sky2);
- 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 = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
- GFP_KERNEL);
- if (!sky2->rx_ring)
- goto err_out;
-
- sky2_phy_power(hw, port, 1);
-
sky2_mac_init(hw, port);
/* Register is number of 4K blocks on internal RAM buffer. */
sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
- TX_RING_SIZE - 1);
+ sky2->tx_ring_size - 1);
+
+#ifdef SKY2_VLAN_TAG_USED
+ sky2_set_vlan_mode(hw, port, sky2->vlgrp != NULL);
+#endif
err = sky2_rx_start(sky2);
if (err)
imask = sky2_read32(hw, B0_IMSK);
imask |= portirq_msk[port];
sky2_write32(hw, B0_IMSK, imask);
+ sky2_read32(hw, B0_IMSK);
+
+ if (netif_msg_ifup(sky2))
+ printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
return 0;
err_out:
- if (sky2->rx_le) {
- pci_free_consistent(hw->pdev, RX_LE_BYTES,
- sky2->rx_le, sky2->rx_le_map);
- 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);
- sky2->tx_le = NULL;
- }
- kfree(sky2->tx_ring);
- kfree(sky2->rx_ring);
-
- sky2->tx_ring = NULL;
- sky2->rx_ring = NULL;
+ sky2_free_buffers(sky2);
return err;
}
/* Modular subtraction in ring */
-static inline int tx_dist(unsigned tail, unsigned head)
+static inline int tx_inuse(const struct sky2_port *sky2)
{
- return (head - tail) & (TX_RING_SIZE - 1);
+ return (sky2->tx_prod - sky2->tx_cons) & (sky2->tx_ring_size - 1);
}
/* Number of list elements available for next tx */
static inline int tx_avail(const struct sky2_port *sky2)
{
- return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod);
+ return sky2->tx_pending - tx_inuse(sky2);
}
/* Estimate of number of transmit list elements required */
{
unsigned count;
- count = sizeof(dma_addr_t) / sizeof(u32);
- count += skb_shinfo(skb)->nr_frags * count;
+ count = (skb_shinfo(skb)->nr_frags + 1)
+ * (sizeof(dma_addr_t) / sizeof(u32));
if (skb_is_gso(skb))
++count;
+ else if (sizeof(dma_addr_t) == sizeof(u32))
+ ++count; /* possible vlan */
if (skb->ip_summed == CHECKSUM_PARTIAL)
++count;
return count;
}
+static void sky2_tx_unmap(struct pci_dev *pdev,
+ const struct tx_ring_info *re)
+{
+ if (re->flags & TX_MAP_SINGLE)
+ pci_unmap_single(pdev, pci_unmap_addr(re, mapaddr),
+ pci_unmap_len(re, maplen),
+ PCI_DMA_TODEVICE);
+ else if (re->flags & TX_MAP_PAGE)
+ pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
+ pci_unmap_len(re, maplen),
+ PCI_DMA_TODEVICE);
+}
+
/*
* 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)
+static netdev_tx_t 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 tx_ring_info *re;
unsigned i, len;
dma_addr_t mapping;
+ u32 upper;
+ u16 slot;
u16 mss;
u8 ctrl;
if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
return NETDEV_TX_BUSY;
- 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);
+ if (pci_dma_mapping_error(hw->pdev, mapping))
+ goto mapping_error;
+
+ slot = sky2->tx_prod;
+ if (unlikely(netif_msg_tx_queued(sky2)))
+ printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
+ dev->name, slot, skb->len);
+
/* Send high bits if needed */
- if (sizeof(dma_addr_t) > sizeof(u32)) {
- le = get_tx_le(sky2);
- le->addr = cpu_to_le32(upper_32_bits(mapping));
+ upper = upper_32_bits(mapping);
+ if (upper != sky2->tx_last_upper) {
+ le = get_tx_le(sky2, &slot);
+ le->addr = cpu_to_le32(upper);
+ sky2->tx_last_upper = upper;
le->opcode = OP_ADDR64 | HW_OWNER;
}
mss += ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
if (mss != sky2->tx_last_mss) {
- le = get_tx_le(sky2);
+ le = get_tx_le(sky2, &slot);
le->addr = cpu_to_le32(mss);
if (hw->flags & SKY2_HW_NEW_LE)
/* 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 = get_tx_le(sky2, &slot);
le->addr = 0;
le->opcode = OP_VLAN|HW_OWNER;
} else
if (tcpsum != sky2->tx_tcpsum) {
sky2->tx_tcpsum = tcpsum;
- le = get_tx_le(sky2);
+ le = get_tx_le(sky2, &slot);
le->addr = cpu_to_le32(tcpsum);
le->length = 0; /* initial checksum value */
le->ctrl = 1; /* one packet */
}
}
- le = get_tx_le(sky2);
- le->addr = cpu_to_le32((u32) mapping);
+ re = sky2->tx_ring + slot;
+ re->flags = TX_MAP_SINGLE;
+ pci_unmap_addr_set(re, mapaddr, mapping);
+ pci_unmap_len_set(re, maplen, len);
+
+ le = get_tx_le(sky2, &slot);
+ le->addr = cpu_to_le32(lower_32_bits(mapping));
le->length = cpu_to_le16(len);
le->ctrl = ctrl;
le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
- re = tx_le_re(sky2, 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++) {
const 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(dma_addr_t) > sizeof(u32)) {
- le = get_tx_le(sky2);
- le->addr = cpu_to_le32(upper_32_bits(mapping));
- le->ctrl = 0;
+ if (pci_dma_mapping_error(hw->pdev, mapping))
+ goto mapping_unwind;
+
+ upper = upper_32_bits(mapping);
+ if (upper != sky2->tx_last_upper) {
+ le = get_tx_le(sky2, &slot);
+ le->addr = cpu_to_le32(upper);
+ sky2->tx_last_upper = upper;
le->opcode = OP_ADDR64 | HW_OWNER;
}
- le = get_tx_le(sky2);
- le->addr = cpu_to_le32((u32) mapping);
+ re = sky2->tx_ring + slot;
+ re->flags = TX_MAP_PAGE;
+ pci_unmap_addr_set(re, mapaddr, mapping);
+ pci_unmap_len_set(re, maplen, frag->size);
+
+ le = get_tx_le(sky2, &slot);
+ le->addr = cpu_to_le32(lower_32_bits(mapping));
le->length = cpu_to_le16(frag->size);
le->ctrl = ctrl;
le->opcode = OP_BUFFER | HW_OWNER;
-
- re = tx_le_re(sky2, le);
- re->skb = skb;
- pci_unmap_addr_set(re, mapaddr, mapping);
- pci_unmap_len_set(re, maplen, frag->size);
}
+ re->skb = skb;
le->ctrl |= EOP;
+ sky2->tx_prod = slot;
+
if (tx_avail(sky2) <= MAX_SKB_TX_LE)
netif_stop_queue(dev);
sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
- dev->trans_start = jiffies;
+ return NETDEV_TX_OK;
+
+mapping_unwind:
+ for (i = sky2->tx_prod; i != slot; i = RING_NEXT(i, sky2->tx_ring_size)) {
+ re = sky2->tx_ring + i;
+
+ sky2_tx_unmap(hw->pdev, re);
+ }
+
+mapping_error:
+ if (net_ratelimit())
+ dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name);
+ dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
/*
* Free ring elements from starting at tx_cons until "done"
*
- * NB: the hardware will tell us about partial completion of multi-part
+ * NB:
+ * 1. The hardware will tell us about partial completion of multi-part
* buffers so make sure not to free skb to early.
+ * 2. This may run in parallel start_xmit because the it only
+ * looks at the tail of the queue of FIFO (tx_cons), not
+ * the head (tx_prod)
*/
static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
{
struct net_device *dev = sky2->netdev;
- struct pci_dev *pdev = sky2->hw->pdev;
unsigned idx;
- BUG_ON(done >= TX_RING_SIZE);
+ BUG_ON(done >= sky2->tx_ring_size);
for (idx = sky2->tx_cons; idx != done;
- idx = RING_NEXT(idx, TX_RING_SIZE)) {
- struct sky2_tx_le *le = sky2->tx_le + idx;
+ idx = RING_NEXT(idx, sky2->tx_ring_size)) {
struct tx_ring_info *re = sky2->tx_ring + idx;
+ struct sk_buff *skb = re->skb;
- switch(le->opcode & ~HW_OWNER) {
- case OP_LARGESEND:
- case OP_PACKET:
- pci_unmap_single(pdev,
- pci_unmap_addr(re, mapaddr),
- pci_unmap_len(re, maplen),
- PCI_DMA_TODEVICE);
- break;
- case OP_BUFFER:
- pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
- pci_unmap_len(re, maplen),
- PCI_DMA_TODEVICE);
- break;
- }
+ sky2_tx_unmap(sky2->hw->pdev, re);
- if (le->ctrl & EOP) {
+ if (skb) {
if (unlikely(netif_msg_tx_done(sky2)))
printk(KERN_DEBUG "%s: tx done %u\n",
dev->name, idx);
dev->stats.tx_packets++;
- dev->stats.tx_bytes += re->skb->len;
+ dev->stats.tx_bytes += skb->len;
- dev_kfree_skb_any(re->skb);
- sky2->tx_next = RING_NEXT(idx, TX_RING_SIZE);
+ dev_kfree_skb_any(skb);
+
+ sky2->tx_next = RING_NEXT(idx, sky2->tx_ring_size);
}
}
netif_wake_queue(dev);
}
-/* Cleanup all untransmitted buffers, assume transmitter not running */
-static void sky2_tx_clean(struct net_device *dev)
+static void sky2_tx_reset(struct sky2_hw *hw, unsigned port)
{
- struct sky2_port *sky2 = netdev_priv(dev);
+ /* 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);
- netif_tx_lock_bh(dev);
- sky2_tx_complete(sky2, sky2->tx_prod);
- netif_tx_unlock_bh(dev);
+ /* 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);
+ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
}
/* Network shutdown */
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 */
- imask = sky2_read32(hw, B0_IMSK);
- imask &= ~portirq_msk[port];
- sky2_write32(hw, B0_IMSK, imask);
-
- synchronize_irq(hw->pdev->irq);
-
- sky2_gmac_reset(hw, port);
+ /* Force flow control off */
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
/* Stop transmitter */
sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP);
ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
gma_write16(hw, port, GM_GP_CTRL, ctrl);
- /* Make sure no packets are pending */
- napi_synchronize(&hw->napi);
-
sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
/* Workaround shared GMAC reset */
&& 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_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
- sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+ /* Force any delayed status interrrupt and NAPI */
+ sky2_write32(hw, STAT_LEV_TIMER_CNT, 0);
+ sky2_write32(hw, STAT_TX_TIMER_CNT, 0);
+ sky2_write32(hw, STAT_ISR_TIMER_CNT, 0);
+ sky2_read8(hw, STAT_ISR_TIMER_CTRL);
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);
-
- sky2_phy_power(hw, port, 0);
-
- netif_carrier_off(dev);
+ /* Disable port IRQ */
+ imask = sky2_read32(hw, B0_IMSK);
+ imask &= ~portirq_msk[port];
+ sky2_write32(hw, B0_IMSK, imask);
+ sky2_read32(hw, B0_IMSK);
- /* turn off LED's */
- sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+ synchronize_irq(hw->pdev->irq);
+ napi_synchronize(&hw->napi);
- sky2_tx_clean(dev);
- sky2_rx_clean(sky2);
+ spin_lock_bh(&sky2->phy_lock);
+ sky2_phy_power_down(hw, port);
+ spin_unlock_bh(&sky2->phy_lock);
- pci_free_consistent(hw->pdev, RX_LE_BYTES,
- sky2->rx_le, sky2->rx_le_map);
- kfree(sky2->rx_ring);
+ sky2_tx_reset(hw, port);
- pci_free_consistent(hw->pdev,
- TX_RING_SIZE * sizeof(struct sky2_tx_le),
- sky2->tx_le, sky2->tx_le_map);
- kfree(sky2->tx_ring);
+ /* Free any pending frames stuck in HW queue */
+ sky2_tx_complete(sky2, sky2->tx_prod);
- sky2->tx_le = NULL;
- sky2->rx_le = NULL;
+ sky2_rx_clean(sky2);
- sky2->rx_ring = NULL;
- sky2->tx_ring = NULL;
+ sky2_free_buffers(sky2);
return 0;
}
printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
sky2->netdev->name, istatus, phystat);
- if (sky2->autoneg == AUTONEG_ENABLE && (istatus & PHY_M_IS_AN_COMPL)) {
+ if (istatus & PHY_M_IS_AN_COMPL) {
if (sky2_autoneg_done(sky2, phystat) == 0)
sky2_link_up(sky2);
goto out;
synchronize_irq(hw->pdev->irq);
- if (sky2_read8(hw, B2_E_0) == 0)
+ if (!(hw->flags & SKY2_HW_RAM_BUFFER))
sky2_set_tx_stfwd(hw, port);
ctl = gma_read16(hw, port, GM_GP_CTRL);
prefetch(skb->data);
re->skb = nskb;
- sky2_rx_map_skb(sky2->hw->pdev, re, hdr_space);
+ if (sky2_rx_map_skb(sky2->hw->pdev, re, hdr_space)) {
+ dev_kfree_skb(nskb);
+ re->skb = skb;
+ return NULL;
+ }
if (skb_shinfo(skb)->nr_frags)
skb_put_frags(skb, hdr_space, length);
goto resubmit;
}
-/* Transmit complete */
-static inline void sky2_tx_done(struct net_device *dev, u16 last)
+/* Transmit complete */
+static inline void sky2_tx_done(struct net_device *dev, u16 last)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ if (netif_running(dev))
+ sky2_tx_complete(sky2, last);
+}
+
+static inline void sky2_skb_rx(const struct sky2_port *sky2,
+ u32 status, struct sk_buff *skb)
+{
+#ifdef SKY2_VLAN_TAG_USED
+ u16 vlan_tag = be16_to_cpu(sky2->rx_tag);
+ if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
+ if (skb->ip_summed == CHECKSUM_NONE)
+ vlan_hwaccel_receive_skb(skb, sky2->vlgrp, vlan_tag);
+ else
+ vlan_gro_receive(&sky2->hw->napi, sky2->vlgrp,
+ vlan_tag, skb);
+ return;
+ }
+#endif
+ if (skb->ip_summed == CHECKSUM_NONE)
+ netif_receive_skb(skb);
+ else
+ napi_gro_receive(&sky2->hw->napi, skb);
+}
+
+static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port,
+ unsigned packets, unsigned bytes)
{
- struct sky2_port *sky2 = netdev_priv(dev);
+ if (packets) {
+ struct net_device *dev = hw->dev[port];
- if (netif_running(dev)) {
- netif_tx_lock(dev);
- sky2_tx_complete(sky2, last);
- netif_tx_unlock(dev);
+ dev->stats.rx_packets += packets;
+ dev->stats.rx_bytes += bytes;
+ dev->last_rx = jiffies;
+ sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
}
}
static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
{
int work_done = 0;
- unsigned rx[2] = { 0, 0 };
+ unsigned int total_bytes[2] = { 0 };
+ unsigned int total_packets[2] = { 0 };
rmb();
do {
le->opcode = 0;
switch (opcode & ~HW_OWNER) {
case OP_RXSTAT:
- ++rx[port];
+ total_packets[port]++;
+ total_bytes[port] += length;
skb = sky2_receive(dev, length, status);
if (unlikely(!skb)) {
dev->stats.rx_dropped++;
/* This chip reports checksum status differently */
if (hw->flags & SKY2_HW_NEW_LE) {
- if (sky2->rx_csum &&
+ if ((sky2->flags & SKY2_FLAG_RX_CHECKSUM) &&
(le->css & (CSS_ISIPV4 | CSS_ISIPV6)) &&
(le->css & CSS_TCPUDPCSOK))
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
skb->protocol = eth_type_trans(skb, dev);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
- dev->last_rx = jiffies;
-#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);
+ sky2_skb_rx(sky2, status, skb);
/* Stop after net poll weight */
if (++work_done >= to_do)
/* fall through */
#endif
case OP_RXCHKS:
- if (!sky2->rx_csum)
+ if (!(sky2->flags & SKY2_FLAG_RX_CHECKSUM))
break;
/* If this happens then driver assuming wrong format */
if (likely(status >> 16 == (status & 0xffff))) {
skb = sky2->rx_ring[sky2->rx_next].skb;
skb->ip_summed = CHECKSUM_COMPLETE;
- skb->csum = status & 0xffff;
+ skb->csum = le16_to_cpu(status);
} else {
printk(KERN_NOTICE PFX "%s: hardware receive "
"checksum problem (status = %#x)\n",
dev->name, status);
- sky2->rx_csum = 0;
+ sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
+
sky2_write32(sky2->hw,
Q_ADDR(rxqaddr[port], Q_CSR),
BMU_DIS_RX_CHKSUM);
case OP_TXINDEXLE:
/* TX index reports status for both ports */
- BUILD_BUG_ON(TX_RING_SIZE > 0x1000);
sky2_tx_done(hw->dev[0], status & 0xfff);
if (hw->dev[1])
sky2_tx_done(hw->dev[1],
sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
exit_loop:
- if (rx[0])
- sky2_rx_update(netdev_priv(hw->dev[0]), Q_R1);
-
- if (rx[1])
- sky2_rx_update(netdev_priv(hw->dev[1]), Q_R2);
+ sky2_rx_done(hw, 0, total_packets[0], total_bytes[0]);
+ sky2_rx_done(hw, 1, total_packets[1], total_bytes[1]);
return work_done;
}
if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) {
u16 pci_err;
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
pci_err = sky2_pci_read16(hw, PCI_STATUS);
if (net_ratelimit())
dev_err(&pdev->dev, "PCI hardware error (0x%x)\n",
sky2_pci_write16(hw, 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 occurred */
u32 err;
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
err = sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
0xfffffffful);
dev_err(&pdev->dev, "PCI Express error (0x%x)\n", err);
sky2_read32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
}
if (status & Y2_HWE_L1_MASK)
}
/* This should never happen it is a bug. */
-static void sky2_le_error(struct sky2_hw *hw, unsigned port,
- u16 q, unsigned ring_size)
+static void sky2_le_error(struct sky2_hw *hw, unsigned port, u16 q)
{
struct net_device *dev = hw->dev[port];
- struct sky2_port *sky2 = netdev_priv(dev);
- unsigned idx;
- const u64 *le = (q == Q_R1 || q == Q_R2)
- ? (u64 *) sky2->rx_le : (u64 *) sky2->tx_le;
+ u16 idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
- idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
- printk(KERN_ERR PFX "%s: descriptor error q=%#x get=%u [%llx] put=%u\n",
- dev->name, (unsigned) q, idx, (unsigned long long) le[idx],
- (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
+ dev_err(&hw->pdev->dev, PFX
+ "%s: descriptor error q=%#x get=%u put=%u\n",
+ dev->name, (unsigned) q, (unsigned) idx,
+ (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
}
++active;
/* For chips with Rx FIFO, check if stuck */
- if ((hw->flags & SKY2_HW_FIFO_HANG_CHECK) &&
+ if ((hw->flags & SKY2_HW_RAM_BUFFER) &&
sky2_rx_hung(dev)) {
pr_info(PFX "%s: receiver hang detected\n",
dev->name);
sky2_mac_intr(hw, 1);
if (status & Y2_IS_CHK_RX1)
- sky2_le_error(hw, 0, Q_R1, RX_LE_SIZE);
+ sky2_le_error(hw, 0, Q_R1);
if (status & Y2_IS_CHK_RX2)
- sky2_le_error(hw, 1, Q_R2, RX_LE_SIZE);
+ sky2_le_error(hw, 1, Q_R2);
if (status & Y2_IS_CHK_TXA1)
- sky2_le_error(hw, 0, Q_XA1, TX_RING_SIZE);
+ sky2_le_error(hw, 0, Q_XA1);
if (status & Y2_IS_CHK_TXA2)
- sky2_le_error(hw, 1, Q_XA2, TX_RING_SIZE);
+ sky2_le_error(hw, 1, Q_XA2);
}
static int sky2_poll(struct napi_struct *napi, int work_limit)
goto done;
}
- /* Bug/Errata workaround?
- * Need to kick the TX irq moderation timer.
- */
- if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_START) {
- sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
- sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
- }
napi_complete(napi);
sky2_read32(hw, B0_Y2_SP_LISR);
done:
case CHIP_ID_YUKON_EC_U:
case CHIP_ID_YUKON_EX:
case CHIP_ID_YUKON_SUPR:
+ case CHIP_ID_YUKON_UL_2:
return 125;
case CHIP_ID_YUKON_FE:
switch(hw->chip_id) {
case CHIP_ID_YUKON_XL:
- hw->flags = SKY2_HW_GIGABIT
- | SKY2_HW_NEWER_PHY;
- if (hw->chip_rev < 3)
- hw->flags |= SKY2_HW_FIFO_HANG_CHECK;
-
+ hw->flags = SKY2_HW_GIGABIT | SKY2_HW_NEWER_PHY;
break;
case CHIP_ID_YUKON_EC_U:
dev_err(&hw->pdev->dev, "unsupported revision Yukon-EC rev A1\n");
return -EOPNOTSUPP;
}
- hw->flags = SKY2_HW_GIGABIT | SKY2_HW_FIFO_HANG_CHECK;
+ hw->flags = SKY2_HW_GIGABIT;
break;
case CHIP_ID_YUKON_FE:
| SKY2_HW_ADV_POWER_CTL;
break;
+ case CHIP_ID_YUKON_UL_2:
+ hw->flags = SKY2_HW_GIGABIT
+ | SKY2_HW_ADV_POWER_CTL;
+ break;
+
default:
dev_err(&hw->pdev->dev, "unsupported chip type 0x%x\n",
hw->chip_id);
if (hw->pmd_type == 'L' || hw->pmd_type == 'S' || hw->pmd_type == 'P')
hw->flags |= SKY2_HW_FIBRE_PHY;
-
hw->ports = 1;
t8 = sky2_read8(hw, B2_Y2_HW_RES);
if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) {
++hw->ports;
}
+ if (sky2_read8(hw, B2_E_0))
+ hw->flags |= SKY2_HW_RAM_BUFFER;
+
return 0;
}
}
sky2_power_on(hw);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
for (i = 0; i < hw->ports; i++) {
sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
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 off descriptor polling */
sky2_write32(hw, B28_DPT_CTRL, DPT_STOP);
sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
}
+/* Take device down (offline).
+ * Equivalent to doing dev_stop() but this does not
+ * inform upper layers of the transistion.
+ */
+static void sky2_detach(struct net_device *dev)
+{
+ if (netif_running(dev)) {
+ netif_device_detach(dev); /* stop txq */
+ sky2_down(dev);
+ }
+}
+
+/* Bring device back after doing sky2_detach */
+static int sky2_reattach(struct net_device *dev)
+{
+ int err = 0;
+
+ if (netif_running(dev)) {
+ err = sky2_up(dev);
+ if (err) {
+ printk(KERN_INFO PFX "%s: could not restart %d\n",
+ dev->name, err);
+ dev_close(dev);
+ } else {
+ netif_device_attach(dev);
+ sky2_set_multicast(dev);
+ }
+ }
+
+ return err;
+}
+
static void sky2_restart(struct work_struct *work)
{
struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work);
- struct net_device *dev;
- int i, err;
+ int i;
rtnl_lock();
- for (i = 0; i < hw->ports; i++) {
- dev = hw->dev[i];
- if (netif_running(dev))
- sky2_down(dev);
- }
+ for (i = 0; i < hw->ports; i++)
+ sky2_detach(hw->dev[i]);
napi_disable(&hw->napi);
sky2_write32(hw, B0_IMSK, 0);
sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
napi_enable(&hw->napi);
- for (i = 0; i < hw->ports; i++) {
- dev = hw->dev[i];
- if (netif_running(dev)) {
- err = sky2_up(dev);
- if (err) {
- printk(KERN_INFO PFX "%s: could not restart %d\n",
- dev->name, err);
- dev_close(dev);
- }
- }
- }
+ for (i = 0; i < hw->ports; i++)
+ sky2_reattach(hw->dev[i]);
rtnl_unlock();
}
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
- if (wol->wolopts & ~sky2_wol_supported(sky2->hw))
+ if ((wol->wolopts & ~sky2_wol_supported(sky2->hw))
+ || !device_can_wakeup(&hw->pdev->dev))
return -EOPNOTSUPP;
sky2->wol = wol->wolopts;
sky2_write32(hw, B0_CTST, sky2->wol
? Y2_HW_WOL_ON : Y2_HW_WOL_OFF);
+ device_set_wakeup_enable(&hw->pdev->dev, sky2->wol);
+
if (!netif_running(dev))
sky2_wol_init(sky2);
return 0;
}
ecmd->advertising = sky2->advertising;
- ecmd->autoneg = sky2->autoneg;
+ ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_SPEED)
+ ? AUTONEG_ENABLE : AUTONEG_DISABLE;
ecmd->duplex = sky2->duplex;
return 0;
}
u32 supported = sky2_supported_modes(hw);
if (ecmd->autoneg == AUTONEG_ENABLE) {
+ sky2->flags |= SKY2_FLAG_AUTO_SPEED;
ecmd->advertising = supported;
sky2->duplex = -1;
sky2->speed = -1;
sky2->speed = ecmd->speed;
sky2->duplex = ecmd->duplex;
+ sky2->flags &= ~SKY2_FLAG_AUTO_SPEED;
}
- sky2->autoneg = ecmd->autoneg;
sky2->advertising = ecmd->advertising;
if (netif_running(dev)) {
{
struct sky2_port *sky2 = netdev_priv(dev);
- return sky2->rx_csum;
+ return !!(sky2->flags & SKY2_FLAG_RX_CHECKSUM);
}
static int sky2_set_rx_csum(struct net_device *dev, u32 data)
{
struct sky2_port *sky2 = netdev_priv(dev);
- sky2->rx_csum = data;
+ if (data)
+ sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
+ else
+ sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
{
struct sky2_port *sky2 = netdev_priv(dev);
- if (!netif_running(dev) || sky2->autoneg != AUTONEG_ENABLE)
+ if (!netif_running(dev) || !(sky2->flags & SKY2_FLAG_AUTO_SPEED))
return -EINVAL;
sky2_phy_reinit(sky2);
/* Can have one global because blinking is controlled by
* ethtool and that is always under RTNL mutex
*/
-static void sky2_led(struct sky2_hw *hw, unsigned port, int on)
+static void sky2_led(struct sky2_port *sky2, enum led_mode mode)
{
- u16 pg;
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
- switch (hw->chip_id) {
- case CHIP_ID_YUKON_XL:
+ spin_lock_bh(&sky2->phy_lock);
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U ||
+ hw->chip_id == CHIP_ID_YUKON_EX ||
+ hw->chip_id == CHIP_ID_YUKON_SUPR) {
+ u16 pg;
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);
+
+ switch (mode) {
+ case MO_LED_OFF:
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+ PHY_M_LEDC_LOS_CTRL(8) |
+ PHY_M_LEDC_INIT_CTRL(8) |
+ PHY_M_LEDC_STA1_CTRL(8) |
+ PHY_M_LEDC_STA0_CTRL(8));
+ break;
+ case MO_LED_ON:
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+ PHY_M_LEDC_LOS_CTRL(9) |
+ PHY_M_LEDC_INIT_CTRL(9) |
+ PHY_M_LEDC_STA1_CTRL(9) |
+ PHY_M_LEDC_STA0_CTRL(9));
+ break;
+ case MO_LED_BLINK:
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+ PHY_M_LEDC_LOS_CTRL(0xa) |
+ PHY_M_LEDC_INIT_CTRL(0xa) |
+ PHY_M_LEDC_STA1_CTRL(0xa) |
+ PHY_M_LEDC_STA0_CTRL(0xa));
+ break;
+ case MO_LED_NORM:
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+ PHY_M_LEDC_LOS_CTRL(1) |
+ PHY_M_LEDC_INIT_CTRL(8) |
+ PHY_M_LEDC_STA1_CTRL(7) |
+ PHY_M_LEDC_STA0_CTRL(7));
+ }
gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
- break;
+ } else
+ gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+ PHY_M_LED_MO_DUP(mode) |
+ PHY_M_LED_MO_10(mode) |
+ PHY_M_LED_MO_100(mode) |
+ PHY_M_LED_MO_1000(mode) |
+ PHY_M_LED_MO_RX(mode) |
+ PHY_M_LED_MO_TX(mode));
- default:
- gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
- gm_phy_write(hw, port, PHY_MARV_LED_OVER,
- on ? PHY_M_LED_ALL : 0);
- }
+ spin_unlock_bh(&sky2->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 = 0;
- long ms;
- int interrupted;
- 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(&sky2->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);
- 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);
- }
-
- interrupted = 0;
- while (!interrupted && ms > 0) {
- sky2_led(hw, port, onoff);
- onoff = !onoff;
-
- spin_unlock_bh(&sky2->phy_lock);
- interrupted = msleep_interruptible(250);
- spin_lock_bh(&sky2->phy_lock);
+ unsigned int i;
- ms -= 250;
- }
+ if (data == 0)
+ data = UINT_MAX;
- /* resume regularly scheduled programming */
- 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);
+ for (i = 0; i < data; i++) {
+ sky2_led(sky2, MO_LED_ON);
+ if (msleep_interruptible(500))
+ break;
+ sky2_led(sky2, MO_LED_OFF);
+ if (msleep_interruptible(500))
+ break;
}
- spin_unlock_bh(&sky2->phy_lock);
+ sky2_led(sky2, MO_LED_NORM);
return 0;
}
ecmd->tx_pause = ecmd->rx_pause = 1;
}
- ecmd->autoneg = sky2->autoneg;
+ ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_PAUSE)
+ ? AUTONEG_ENABLE : AUTONEG_DISABLE;
}
static int sky2_set_pauseparam(struct net_device *dev,
{
struct sky2_port *sky2 = netdev_priv(dev);
- sky2->autoneg = ecmd->autoneg;
+ if (ecmd->autoneg == AUTONEG_ENABLE)
+ sky2->flags |= SKY2_FLAG_AUTO_PAUSE;
+ else
+ sky2->flags &= ~SKY2_FLAG_AUTO_PAUSE;
+
sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause);
if (netif_running(dev))
ecmd->rx_coalesce_usecs_irq > tmax)
return -EINVAL;
- if (ecmd->tx_max_coalesced_frames >= TX_RING_SIZE-1)
+ if (ecmd->tx_max_coalesced_frames >= sky2->tx_ring_size-1)
return -EINVAL;
if (ecmd->rx_max_coalesced_frames > RX_MAX_PENDING)
return -EINVAL;
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->tx_max_pending = TX_MAX_PENDING;
ering->rx_pending = sky2->rx_pending;
ering->rx_mini_pending = 0;
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)
+ ering->tx_pending < TX_MIN_PENDING ||
+ ering->tx_pending > TX_MAX_PENDING)
return -EINVAL;
- if (netif_running(dev))
- sky2_down(dev);
+ sky2_detach(dev);
sky2->rx_pending = ering->rx_pending;
sky2->tx_pending = ering->tx_pending;
+ sky2->tx_ring_size = roundup_pow_of_two(sky2->tx_pending+1);
- if (netif_running(dev)) {
- err = sky2_up(dev);
- if (err)
- dev_close(dev);
- else
- sky2_set_multicast(dev);
- }
-
- return err;
+ return sky2_reattach(dev);
}
static int sky2_get_regs_len(struct net_device *dev)
return 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
}
-static u32 sky2_vpd_read(struct sky2_hw *hw, int cap, u16 offset)
+static int sky2_vpd_wait(const struct sky2_hw *hw, int cap, u16 busy)
{
- u32 val;
+ unsigned long start = jiffies;
- sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset);
+ while ( (sky2_pci_read16(hw, cap + PCI_VPD_ADDR) & PCI_VPD_ADDR_F) == busy) {
+ /* Can take up to 10.6 ms for write */
+ if (time_after(jiffies, start + HZ/4)) {
+ dev_err(&hw->pdev->dev, PFX "VPD cycle timed out");
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ }
- do {
- offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR);
- } while (!(offset & PCI_VPD_ADDR_F));
+ return 0;
+}
+
+static int sky2_vpd_read(struct sky2_hw *hw, int cap, void *data,
+ u16 offset, size_t length)
+{
+ int rc = 0;
+
+ while (length > 0) {
+ u32 val;
+
+ sky2_pci_write16(hw, cap + PCI_VPD_ADDR, offset);
+ rc = sky2_vpd_wait(hw, cap, 0);
+ if (rc)
+ break;
+
+ val = sky2_pci_read32(hw, cap + PCI_VPD_DATA);
+
+ memcpy(data, &val, min(sizeof(val), length));
+ offset += sizeof(u32);
+ data += sizeof(u32);
+ length -= sizeof(u32);
+ }
- val = sky2_pci_read32(hw, cap + PCI_VPD_DATA);
- return val;
+ return rc;
}
-static void sky2_vpd_write(struct sky2_hw *hw, int cap, u16 offset, u32 val)
+static int sky2_vpd_write(struct sky2_hw *hw, int cap, const void *data,
+ u16 offset, unsigned int length)
{
- sky2_pci_write16(hw, cap + PCI_VPD_DATA, val);
- sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F);
- do {
- offset = sky2_pci_read16(hw, cap + PCI_VPD_ADDR);
- } while (offset & PCI_VPD_ADDR_F);
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < length; i += sizeof(u32)) {
+ u32 val = *(u32 *)(data + i);
+
+ sky2_pci_write32(hw, cap + PCI_VPD_DATA, val);
+ sky2_pci_write32(hw, cap + PCI_VPD_ADDR, offset | PCI_VPD_ADDR_F);
+
+ rc = sky2_vpd_wait(hw, cap, PCI_VPD_ADDR_F);
+ if (rc)
+ break;
+ }
+ return rc;
}
static int sky2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
{
struct sky2_port *sky2 = netdev_priv(dev);
int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
- int length = eeprom->len;
- u16 offset = eeprom->offset;
if (!cap)
return -EINVAL;
eeprom->magic = SKY2_EEPROM_MAGIC;
- while (length > 0) {
- u32 val = sky2_vpd_read(sky2->hw, cap, offset);
- int n = min_t(int, length, sizeof(val));
-
- memcpy(data, &val, n);
- length -= n;
- data += n;
- offset += n;
- }
- return 0;
+ return sky2_vpd_read(sky2->hw, cap, data, eeprom->offset, eeprom->len);
}
static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
{
struct sky2_port *sky2 = netdev_priv(dev);
int cap = pci_find_capability(sky2->hw->pdev, PCI_CAP_ID_VPD);
- int length = eeprom->len;
- u16 offset = eeprom->offset;
if (!cap)
return -EINVAL;
if (eeprom->magic != SKY2_EEPROM_MAGIC)
return -EINVAL;
- while (length > 0) {
- u32 val;
- int n = min_t(int, length, sizeof(val));
-
- if (n < sizeof(val))
- val = sky2_vpd_read(sky2->hw, cap, offset);
- memcpy(&val, data, n);
-
- sky2_vpd_write(sky2->hw, cap, offset, val);
+ /* Partial writes not supported */
+ if ((eeprom->offset & 3) || (eeprom->len & 3))
+ return -EINVAL;
- length -= n;
- data += n;
- offset += n;
- }
- return 0;
+ return sky2_vpd_write(sky2->hw, cap, data, eeprom->offset, eeprom->len);
}
static struct dentry *sky2_debug;
+
+/*
+ * Read and parse the first part of Vital Product Data
+ */
+#define VPD_SIZE 128
+#define VPD_MAGIC 0x82
+
+static const struct vpd_tag {
+ char tag[2];
+ char *label;
+} vpd_tags[] = {
+ { "PN", "Part Number" },
+ { "EC", "Engineering Level" },
+ { "MN", "Manufacturer" },
+ { "SN", "Serial Number" },
+ { "YA", "Asset Tag" },
+ { "VL", "First Error Log Message" },
+ { "VF", "Second Error Log Message" },
+ { "VB", "Boot Agent ROM Configuration" },
+ { "VE", "EFI UNDI Configuration" },
+};
+
+static void sky2_show_vpd(struct seq_file *seq, struct sky2_hw *hw)
+{
+ size_t vpd_size;
+ loff_t offs;
+ u8 len;
+ unsigned char *buf;
+ u16 reg2;
+
+ reg2 = sky2_pci_read16(hw, PCI_DEV_REG2);
+ vpd_size = 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+
+ seq_printf(seq, "%s Product Data\n", pci_name(hw->pdev));
+ buf = kmalloc(vpd_size, GFP_KERNEL);
+ if (!buf) {
+ seq_puts(seq, "no memory!\n");
+ return;
+ }
+
+ if (pci_read_vpd(hw->pdev, 0, vpd_size, buf) < 0) {
+ seq_puts(seq, "VPD read failed\n");
+ goto out;
+ }
+
+ if (buf[0] != VPD_MAGIC) {
+ seq_printf(seq, "VPD tag mismatch: %#x\n", buf[0]);
+ goto out;
+ }
+ len = buf[1];
+ if (len == 0 || len > vpd_size - 4) {
+ seq_printf(seq, "Invalid id length: %d\n", len);
+ goto out;
+ }
+
+ seq_printf(seq, "%.*s\n", len, buf + 3);
+ offs = len + 3;
+
+ while (offs < vpd_size - 4) {
+ int i;
+
+ if (!memcmp("RW", buf + offs, 2)) /* end marker */
+ break;
+ len = buf[offs + 2];
+ if (offs + len + 3 >= vpd_size)
+ break;
+
+ for (i = 0; i < ARRAY_SIZE(vpd_tags); i++) {
+ if (!memcmp(vpd_tags[i].tag, buf + offs, 2)) {
+ seq_printf(seq, " %s: %.*s\n",
+ vpd_tags[i].label, len, buf + offs + 3);
+ break;
+ }
+ }
+ offs += len + 3;
+ }
+out:
+ kfree(buf);
+}
+
static int sky2_debug_show(struct seq_file *seq, void *v)
{
struct net_device *dev = seq->private;
unsigned idx, last;
int sop;
- if (!netif_running(dev))
- return -ENETDOWN;
+ sky2_show_vpd(seq, hw);
- seq_printf(seq, "IRQ src=%x mask=%x control=%x\n",
+ seq_printf(seq, "\nIRQ src=%x mask=%x control=%x\n",
sky2_read32(hw, B0_ISRC),
sky2_read32(hw, B0_IMSK),
sky2_read32(hw, B0_Y2_SP_ICR));
+ if (!netif_running(dev)) {
+ seq_printf(seq, "network not running\n");
+ return 0;
+ }
+
napi_disable(&hw->napi);
last = sky2_read16(hw, STAT_PUT_IDX);
/* Dump contents of tx ring */
sop = 1;
- for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < TX_RING_SIZE;
- idx = RING_NEXT(idx, TX_RING_SIZE)) {
+ for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < sky2->tx_ring_size;
+ idx = RING_NEXT(idx, sky2->tx_ring_size)) {
const struct sky2_tx_le *le = sky2->tx_le + idx;
u32 a = le32_to_cpu(le->addr);
seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
- last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
+ sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
sky2_read32(hw, B0_Y2_SP_LISR);
struct net_device *dev = ptr;
struct sky2_port *sky2 = netdev_priv(dev);
- if (dev->open != sky2_up || !sky2_debug)
+ if (dev->netdev_ops->ndo_open != sky2_up || !sky2_debug)
return NOTIFY_DONE;
switch(event) {
#define sky2_debug_cleanup()
#endif
+/* Two copies of network device operations to handle special case of
+ not allowing netpoll on second port */
+static const struct net_device_ops sky2_netdev_ops[2] = {
+ {
+ .ndo_open = sky2_up,
+ .ndo_stop = sky2_down,
+ .ndo_start_xmit = sky2_xmit_frame,
+ .ndo_do_ioctl = sky2_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = sky2_set_mac_address,
+ .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_change_mtu = sky2_change_mtu,
+ .ndo_tx_timeout = sky2_tx_timeout,
+#ifdef SKY2_VLAN_TAG_USED
+ .ndo_vlan_rx_register = sky2_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = sky2_netpoll,
+#endif
+ },
+ {
+ .ndo_open = sky2_up,
+ .ndo_stop = sky2_down,
+ .ndo_start_xmit = sky2_xmit_frame,
+ .ndo_do_ioctl = sky2_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = sky2_set_mac_address,
+ .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_change_mtu = sky2_change_mtu,
+ .ndo_tx_timeout = sky2_tx_timeout,
+#ifdef SKY2_VLAN_TAG_USED
+ .ndo_vlan_rx_register = sky2_vlan_rx_register,
+#endif
+ },
+};
/* Initialize network device */
static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
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->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;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- if (port == 0)
- dev->poll_controller = sky2_netpoll;
-#endif
+ dev->netdev_ops = &sky2_netdev_ops[port];
sky2 = netdev_priv(dev);
sky2->netdev = dev;
sky2->msg_enable = netif_msg_init(debug, default_msg);
/* Auto speed and flow control */
- sky2->autoneg = AUTONEG_ENABLE;
+ sky2->flags = SKY2_FLAG_AUTO_SPEED | SKY2_FLAG_AUTO_PAUSE;
+ if (hw->chip_id != CHIP_ID_YUKON_XL)
+ sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
+
sky2->flow_mode = FC_BOTH;
sky2->duplex = -1;
sky2->speed = -1;
sky2->advertising = sky2_supported_modes(hw);
- sky2->rx_csum = (hw->chip_id != CHIP_ID_YUKON_XL);
sky2->wol = wol;
spin_lock_init(&sky2->phy_lock);
+
sky2->tx_pending = TX_DEF_PENDING;
+ sky2->tx_ring_size = roundup_pow_of_two(TX_DEF_PENDING+1);
sky2->rx_pending = RX_DEF_PENDING;
hw->dev[port] = dev;
if (!(sky2->hw->chip_id == CHIP_ID_YUKON_FE_P &&
sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0)) {
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = sky2_vlan_rx_register;
}
#endif
static void __devinit sky2_show_addr(struct net_device *dev)
{
const struct sky2_port *sky2 = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
if (netif_msg_probe(sky2))
- printk(KERN_INFO PFX "%s: addr %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO PFX "%s: addr %pM\n",
+ dev->name, dev->dev_addr);
}
/* Handle software interrupt used during MSI test */
return err;
}
-static int __devinit pci_wake_enabled(struct pci_dev *dev)
+/* This driver supports yukon2 chipset only */
+static const char *sky2_name(u8 chipid, char *buf, int sz)
{
- int pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- u16 value;
+ const char *name[] = {
+ "XL", /* 0xb3 */
+ "EC Ultra", /* 0xb4 */
+ "Extreme", /* 0xb5 */
+ "EC", /* 0xb6 */
+ "FE", /* 0xb7 */
+ "FE+", /* 0xb8 */
+ "Supreme", /* 0xb9 */
+ "UL 2", /* 0xba */
+ };
- if (!pm)
- return 0;
- if (pci_read_config_word(dev, pm + PCI_PM_CTRL, &value))
- return 0;
- return value & PCI_PM_CTRL_PME_ENABLE;
+ if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2)
+ strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
+ else
+ snprintf(buf, sz, "(chip %#x)", chipid);
+ return buf;
}
static int __devinit sky2_probe(struct pci_dev *pdev,
struct net_device *dev;
struct sky2_hw *hw;
int err, using_dac = 0, wol_default;
+ u32 reg;
+ char buf1[16];
err = pci_enable_device(pdev);
if (err) {
goto err_out;
}
+ /* Get configuration information
+ * Note: only regular PCI config access once to test for HW issues
+ * other PCI access through shared memory for speed and to
+ * avoid MMCONFIG problems.
+ */
+ err = pci_read_config_dword(pdev, PCI_DEV_REG2, ®);
+ if (err) {
+ dev_err(&pdev->dev, "PCI read config failed\n");
+ goto err_out;
+ }
+
+ if (~reg == 0) {
+ dev_err(&pdev->dev, "PCI configuration read error\n");
+ goto err_out;
+ }
+
err = pci_request_regions(pdev, DRV_NAME);
if (err) {
dev_err(&pdev->dev, "cannot obtain PCI resources\n");
pci_set_master(pdev);
if (sizeof(dma_addr_t) > sizeof(u32) &&
- !(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) {
+ !(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))) {
using_dac = 1;
- err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
if (err < 0) {
dev_err(&pdev->dev, "unable to obtain 64 bit DMA "
"for consistent allocations\n");
goto err_out_free_regions;
}
} else {
- err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pdev->dev, "no usable DMA configuration\n");
goto err_out_free_regions;
}
}
- wol_default = pci_wake_enabled(pdev) ? WAKE_MAGIC : 0;
+
+#ifdef __BIG_ENDIAN
+ /* The sk98lin vendor driver uses hardware byte swapping but
+ * this driver uses software swapping.
+ */
+ reg &= ~PCI_REV_DESC;
+ err = pci_write_config_dword(pdev,PCI_DEV_REG2, reg);
+ if (err) {
+ dev_err(&pdev->dev, "PCI write config failed\n");
+ goto err_out_free_regions;
+ }
+#endif
+
+ wol_default = device_may_wakeup(&pdev->dev) ? WAKE_MAGIC : 0;
err = -ENOMEM;
- hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+
+ hw = kzalloc(sizeof(*hw) + strlen(DRV_NAME "@pci:")
+ + strlen(pci_name(pdev)) + 1, GFP_KERNEL);
if (!hw) {
dev_err(&pdev->dev, "cannot allocate hardware struct\n");
goto err_out_free_regions;
}
hw->pdev = pdev;
+ sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev));
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
if (!hw->regs) {
goto err_out_free_hw;
}
-#ifdef __BIG_ENDIAN
- /* The sk98lin vendor driver uses hardware byte swapping but
- * this driver uses software swapping.
- */
- {
- u32 reg;
- reg = sky2_pci_read32(hw, PCI_DEV_REG2);
- reg &= ~PCI_REV_DESC;
- sky2_pci_write32(hw, PCI_DEV_REG2, reg);
- }
-#endif
-
/* ring for status responses */
hw->st_le = pci_alloc_consistent(pdev, STATUS_LE_BYTES, &hw->st_dma);
if (!hw->st_le)
if (err)
goto err_out_iounmap;
- dev_info(&pdev->dev, "v%s addr 0x%llx irq %d Yukon-%s (0x%x) rev %d\n",
- DRV_VERSION, (unsigned long long)pci_resource_start(pdev, 0),
- pdev->irq, yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL],
- hw->chip_id, hw->chip_rev);
+ dev_info(&pdev->dev, "Yukon-2 %s chip revision %d\n",
+ sky2_name(hw->chip_id, buf1, sizeof(buf1)), hw->chip_rev);
sky2_reset(hw);
err = request_irq(pdev->irq, sky2_intr,
(hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
- dev->name, hw);
+ hw->irq_name, hw);
if (err) {
dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
goto err_out_unregister;
if (hw->ports > 1) {
struct net_device *dev1;
+ err = -ENOMEM;
dev1 = sky2_init_netdev(hw, 1, using_dac, wol_default);
- if (!dev1)
- dev_warn(&pdev->dev, "allocation for second device failed\n");
- else if ((err = register_netdev(dev1))) {
+ if (dev1 && (err = register_netdev(dev1)) == 0)
+ sky2_show_addr(dev1);
+ else {
dev_warn(&pdev->dev,
"register of second port failed (%d)\n", err);
hw->dev[1] = NULL;
- free_netdev(dev1);
- } else
- sky2_show_addr(dev1);
+ hw->ports = 1;
+ if (dev1)
+ free_netdev(dev1);
+ }
}
setup_timer(&hw->watchdog_timer, sky2_watchdog, (unsigned long) hw);
sky2_power_aux(hw);
- sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
sky2_write8(hw, B0_CTST, CS_RST_SET);
sky2_read8(hw, B0_CTST);
if (!hw)
return 0;
+ del_timer_sync(&hw->watchdog_timer);
+ cancel_work_sync(&hw->restart_work);
+
+ rtnl_lock();
for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
struct sky2_port *sky2 = netdev_priv(dev);
- if (netif_running(dev))
- sky2_down(dev);
+ sky2_detach(dev);
if (sky2->wol)
sky2_wol_init(sky2);
sky2_write32(hw, B0_IMSK, 0);
napi_disable(&hw->napi);
sky2_power_aux(hw);
+ rtnl_unlock();
pci_save_state(pdev);
pci_enable_wake(pdev, pci_choose_state(pdev, state), wol);
sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
napi_enable(&hw->napi);
+ rtnl_lock();
for (i = 0; i < hw->ports; i++) {
- struct net_device *dev = hw->dev[i];
- if (netif_running(dev)) {
- err = sky2_up(dev);
- if (err) {
- printk(KERN_ERR PFX "%s: could not up: %d\n",
- dev->name, err);
- dev_close(dev);
- goto out;
- }
-
- sky2_set_multicast(dev);
- }
+ err = sky2_reattach(hw->dev[i]);
+ if (err)
+ goto out;
}
+ rtnl_unlock();
return 0;
out:
+ rtnl_unlock();
+
dev_err(&pdev->dev, "resume failed (%d)\n", err);
pci_disable_device(pdev);
return err;
if (!hw)
return;
+ rtnl_lock();
del_timer_sync(&hw->watchdog_timer);
for (i = 0; i < hw->ports; i++) {
if (wol)
sky2_power_aux(hw);
+ rtnl_unlock();
pci_enable_wake(pdev, PCI_D3hot, wol);
pci_enable_wake(pdev, PCI_D3cold, wol);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
-
}
static struct pci_driver sky2_driver = {
static int __init sky2_init_module(void)
{
+ pr_info(PFX "driver version " DRV_VERSION "\n");
+
sky2_debug_init();
return pci_register_driver(&sky2_driver);
}