#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/in.h>
+#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/etherdevice.h>
/*
* Provides access to local SMI interface.
*/
- struct mii_bus smi_bus;
+ struct mii_bus *smi_bus;
/*
* If we have access to the error interrupt pin (which is
u8 work_rx_refill;
u8 work_rx_oom;
+ int skb_size;
+ struct sk_buff_head rx_recycle;
+
/*
* RX state.
*/
static int rxq_refill(struct rx_queue *rxq, int budget)
{
struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
- int skb_size;
int refilled;
- /*
- * Reserve 2+14 bytes for an ethernet header (the hardware
- * automatically prepends 2 bytes of dummy data to each
- * received packet), 16 bytes for up to four VLAN tags, and
- * 4 bytes for the trailing FCS -- 36 bytes total.
- */
- skb_size = rxq_to_mp(rxq)->dev->mtu + 36;
-
- /*
- * Make sure that the skb size is a multiple of 8 bytes, as
- * the lower three bits of the receive descriptor's buffer
- * size field are ignored by the hardware.
- */
- skb_size = (skb_size + 7) & ~7;
-
refilled = 0;
while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) {
struct sk_buff *skb;
int unaligned;
int rx;
- skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1);
+ skb = __skb_dequeue(&mp->rx_recycle);
+ if (skb == NULL)
+ skb = dev_alloc_skb(mp->skb_size +
+ dma_get_cache_alignment() - 1);
+
if (skb == NULL) {
mp->work_rx_oom |= 1 << rxq->index;
goto oom;
rxq->rx_used_desc = 0;
rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data,
- skb_size, DMA_FROM_DEVICE);
- rxq->rx_desc_area[rx].buf_size = skb_size;
+ mp->skb_size, DMA_FROM_DEVICE);
+ rxq->rx_desc_area[rx].buf_size = mp->skb_size;
rxq->rx_skb[rx] = skb;
wmb();
rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA |
desc->byte_cnt, DMA_TO_DEVICE);
}
- if (skb)
- dev_kfree_skb(skb);
+ if (skb != NULL) {
+ if (skb_queue_len(&mp->rx_recycle) <
+ mp->default_rx_ring_size &&
+ skb_recycle_check(skb, mp->skb_size))
+ __skb_queue_head(&mp->rx_recycle, skb);
+ else
+ dev_kfree_skb(skb);
+ }
}
__netif_tx_unlock(nq);
wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4);
}
+static void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp)
+{
+ int skb_size;
+
+ /*
+ * Reserve 2+14 bytes for an ethernet header (the hardware
+ * automatically prepends 2 bytes of dummy data to each
+ * received packet), 16 bytes for up to four VLAN tags, and
+ * 4 bytes for the trailing FCS -- 36 bytes total.
+ */
+ skb_size = mp->dev->mtu + 36;
+
+ /*
+ * Make sure that the skb size is a multiple of 8 bytes, as
+ * the lower three bits of the receive descriptor's buffer
+ * size field are ignored by the hardware.
+ */
+ mp->skb_size = (skb_size + 7) & ~7;
+}
+
static int mv643xx_eth_open(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
init_mac_tables(mp);
+ mv643xx_eth_recalc_skb_size(mp);
+
napi_enable(&mp->napi);
+ skb_queue_head_init(&mp->rx_recycle);
+
for (i = 0; i < mp->rxq_count; i++) {
err = rxq_init(mp, i);
if (err) {
mv643xx_eth_get_stats(dev);
mib_counters_update(mp);
+ skb_queue_purge(&mp->rx_recycle);
+
for (i = 0; i < mp->rxq_count; i++)
rxq_deinit(mp->rxq + i);
for (i = 0; i < mp->txq_count; i++)
return -EINVAL;
dev->mtu = new_mtu;
+ mv643xx_eth_recalc_skb_size(mp);
tx_set_rate(mp, 1000000000, 16777216);
if (!netif_running(dev))
* Set up and register SMI bus.
*/
if (pd == NULL || pd->shared_smi == NULL) {
- msp->smi_bus.priv = msp;
- msp->smi_bus.name = "mv643xx_eth smi";
- msp->smi_bus.read = smi_bus_read;
- msp->smi_bus.write = smi_bus_write,
- snprintf(msp->smi_bus.id, MII_BUS_ID_SIZE, "%d", pdev->id);
- msp->smi_bus.dev = &pdev->dev;
- msp->smi_bus.phy_mask = 0xffffffff;
- if (mdiobus_register(&msp->smi_bus) < 0)
+ msp->smi_bus = mdiobus_alloc();
+ if (msp->smi_bus == NULL)
goto out_unmap;
+
+ msp->smi_bus->priv = msp;
+ msp->smi_bus->name = "mv643xx_eth smi";
+ msp->smi_bus->read = smi_bus_read;
+ msp->smi_bus->write = smi_bus_write,
+ snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
+ msp->smi_bus->parent = &pdev->dev;
+ msp->smi_bus->phy_mask = 0xffffffff;
+ if (mdiobus_register(msp->smi_bus) < 0)
+ goto out_free_mii_bus;
msp->smi = msp;
} else {
msp->smi = platform_get_drvdata(pd->shared_smi);
return 0;
+out_free_mii_bus:
+ mdiobus_free(msp->smi_bus);
out_unmap:
iounmap(msp->base);
out_free:
struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
- if (pd == NULL || pd->shared_smi == NULL)
- mdiobus_unregister(&msp->smi_bus);
+ if (pd == NULL || pd->shared_smi == NULL) {
+ mdiobus_free(msp->smi_bus);
+ mdiobus_unregister(msp->smi_bus);
+ }
if (msp->err_interrupt != NO_IRQ)
free_irq(msp->err_interrupt, msp);
iounmap(msp->base);
static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
int phy_addr)
{
- struct mii_bus *bus = &mp->shared->smi->smi_bus;
+ struct mii_bus *bus = mp->shared->smi->smi_bus;
struct phy_device *phydev;
int start;
int num;