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) {
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);
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 */
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);
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 */
/* 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);
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);
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;
}
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));
+ }
}
/*
break;
case OP_TXINDEXLE:
- sky2_tx_complete(sky2,
- tx_index(sky2->port, status, length));
+ /* pick up transmit status later */
break;
default:
}
exit_loop:
+ sky2_tx_check(hw, 0);
+ sky2_tx_check(hw, 1);
mmiowb();
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)