#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.23"
+#define DRV_VERSION "1.24"
#define PFX DRV_NAME " "
/*
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? */
/* 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(MO_LED_ON);
}
}
/* 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);
/* setup General Purpose Control Register */
gma_write16(hw, port, GM_GP_CTRL,
- GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS);
+ 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) {
* 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);
le = get_tx_le(sky2, &sky2->tx_prod);
le->addr = 0;
le->opcode = OP_ADDR64 | HW_OWNER;
+ sky2->tx_last_upper = 0;
}
static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
}
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;
}
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);
}
/*
sky2_set_vlan_mode(hw, port, sky2->vlgrp != NULL);
#endif
- sky2->restarting = 0;
-
err = sky2_rx_start(sky2);
if (err)
goto err_out;
sky2_write32(hw, B0_IMSK, imask);
sky2_read32(hw, B0_IMSK);
- sky2_set_multicast(dev);
-
- /* wake queue incase we are restarting */
- netif_wake_queue(dev);
-
if (netif_msg_ifup(sky2))
printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
+
return 0;
err_out:
/* Number of list elements available for next tx */
static inline int tx_avail(const struct sky2_port *sky2)
{
- if (unlikely(sky2->restarting))
- return 0;
return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod);
}
struct sky2_tx_le *le = NULL;
struct tx_ring_info *re;
unsigned i, len;
- u16 slot;
dma_addr_t mapping;
+ u32 upper;
+ u16 slot;
u16 mss;
u8 ctrl;
dev->name, slot, skb->len);
/* Send high bits if needed */
- if (sizeof(dma_addr_t) > sizeof(u32)) {
+ upper = upper_32_bits(mapping);
+ if (upper != sky2->tx_last_upper) {
le = get_tx_le(sky2, &slot);
- le->addr = cpu_to_le32(upper_32_bits(mapping));
+ le->addr = cpu_to_le32(upper);
+ sky2->tx_last_upper = upper;
le->opcode = OP_ADDR64 | HW_OWNER;
}
}
le = get_tx_le(sky2, &slot);
- le->addr = cpu_to_le32((u32) mapping);
+ 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);
if (pci_dma_mapping_error(hw->pdev, mapping))
goto mapping_unwind;
- if (sizeof(dma_addr_t) > sizeof(u32)) {
+ upper = upper_32_bits(mapping);
+ if (upper != sky2->tx_last_upper) {
le = get_tx_le(sky2, &slot);
- le->addr = cpu_to_le32(upper_32_bits(mapping));
- le->ctrl = 0;
+ le->addr = cpu_to_le32(upper);
+ sky2->tx_last_upper = upper;
le->opcode = OP_ADDR64 | HW_OWNER;
}
le = get_tx_le(sky2, &slot);
- le->addr = cpu_to_le32((u32) mapping);
+ 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;
/*
* 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)
{
netif_wake_queue(dev);
}
-/* Cleanup all untransmitted buffers, assume transmitter not running */
-static void sky2_tx_clean(struct net_device *dev)
-{
- struct sky2_port *sky2 = netdev_priv(dev);
-
- netif_tx_lock_bh(dev);
- sky2_tx_complete(sky2, sky2->tx_prod);
- netif_tx_unlock_bh(dev);
-}
-
static void sky2_tx_reset(struct sky2_hw *hw, unsigned port)
{
/* Disable Force Sync bit and Enable Alloc bit */
if (netif_msg_ifdown(sky2))
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
- /* explicitly shut off tx incase we're restarting */
- sky2->restarting = 1;
- netif_tx_disable(dev);
-
/* Force flow control off */
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
sky2_tx_reset(hw, port);
- sky2_tx_clean(dev);
+ /* Free any pending frames stuck in HW queue */
+ sky2_tx_complete(sky2, sky2->tx_prod);
+
sky2_rx_clean(sky2);
pci_free_consistent(hw->pdev, RX_LE_BYTES,
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;
{
struct sky2_port *sky2 = netdev_priv(dev);
- if (likely(netif_running(dev) && !sky2->restarting)) {
- netif_tx_lock(dev);
+ if (netif_running(dev))
sky2_tx_complete(sky2, last);
- netif_tx_unlock(dev);
- }
}
static inline void sky2_skb_rx(const struct sky2_port *sky2,
/* 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;
/* 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 */
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);
}
/* 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);
}
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)
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();
}
}
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);
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))
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 > TX_RING_SIZE - 1)
return -EINVAL;
- if (netif_running(dev))
- sky2_down(dev);
+ sky2_detach(dev);
sky2->rx_pending = ering->rx_pending;
sky2->tx_pending = ering->tx_pending;
- if (netif_running(dev)) {
- err = sky2_up(dev);
- if (err)
- dev_close(dev);
- }
-
- return err;
+ return sky2_reattach(dev);
}
static int sky2_get_regs_len(struct net_device *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->rx_pending = RX_DEF_PENDING;
- sky2->restarting = 0;
hw->dev[port] = dev;
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);
- netif_device_detach(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];
-
- netif_device_attach(dev);
- if (netif_running(dev)) {
- err = sky2_up(dev);
- if (err) {
- printk(KERN_ERR PFX "%s: could not up: %d\n",
- dev->name, err);
- rtnl_lock();
- dev_close(dev);
- rtnl_unlock();
- goto out;
- }
- }
+ 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);