summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c')
-rw-r--r--target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c72
1 files changed, 64 insertions, 8 deletions
diff --git a/target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c b/target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c
index 3dd170d68..a72149250 100644
--- a/target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c
+++ b/target/linux/brcm63xx/files/drivers/net/bcm63xx_enet.c
@@ -27,6 +27,7 @@
#include <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
+#include <linux/if_vlan.h>
#include <bcm63xx_dev_enet.h>
#include "bcm63xx_enet.h"
@@ -189,18 +190,18 @@ static int bcm_enet_refill_rx(struct net_device *dev)
desc = &priv->rx_desc_cpu[desc_idx];
if (!priv->rx_skb[desc_idx]) {
- skb = netdev_alloc_skb(dev, BCMENET_MAX_RX_SIZE);
+ skb = netdev_alloc_skb(dev, priv->rx_skb_size);
if (!skb)
break;
priv->rx_skb[desc_idx] = skb;
p = dma_map_single(&priv->pdev->dev, skb->data,
- BCMENET_MAX_RX_SIZE,
+ priv->rx_skb_size,
DMA_FROM_DEVICE);
desc->address = p;
}
- len_stat = BCMENET_MAX_RX_SIZE << DMADESC_LENGTH_SHIFT;
+ len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT;
len_stat |= DMADESC_OWNER_MASK;
if (priv->rx_dirty_desc == priv->rx_ring_size - 1) {
len_stat |= DMADESC_WRAP_MASK;
@@ -337,7 +338,7 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
skb = nskb;
} else {
dma_unmap_single(&priv->pdev->dev, desc->address,
- BCMENET_MAX_RX_SIZE, DMA_FROM_DEVICE);
+ priv->rx_skb_size, DMA_FROM_DEVICE);
priv->rx_skb[desc_idx] = NULL;
}
@@ -949,8 +950,8 @@ static int bcm_enet_open(struct net_device *dev)
enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->tx_chan));
/* set max rx/tx length */
- enet_writel(priv, BCMENET_MAX_RX_SIZE, ENET_RXMAXLEN_REG);
- enet_writel(priv, BCMENET_MAX_TX_SIZE, ENET_TXMAXLEN_REG);
+ enet_writel(priv, priv->hw_mtu, ENET_RXMAXLEN_REG);
+ enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG);
/* set dma maximum burst len */
enet_dma_writel(priv, BCMENET_DMA_MAXBURST,
@@ -1016,7 +1017,7 @@ out:
continue;
desc = &priv->rx_desc_cpu[i];
- dma_unmap_single(kdev, desc->address, BCMENET_MAX_RX_SIZE,
+ dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
DMA_FROM_DEVICE);
kfree_skb(priv->rx_skb[i]);
}
@@ -1116,7 +1117,7 @@ static int bcm_enet_stop(struct net_device *dev)
continue;
desc = &priv->rx_desc_cpu[i];
- dma_unmap_single(kdev, desc->address, BCMENET_MAX_RX_SIZE,
+ dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
DMA_FROM_DEVICE);
kfree_skb(priv->rx_skb[i]);
}
@@ -1506,6 +1507,55 @@ static int bcm_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
/*
+ * calculate actual hardware mtu
+ */
+static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu)
+{
+ int actual_mtu;
+
+ actual_mtu = mtu;
+
+ /* add ethernet header + vlan tag size */
+ actual_mtu += VLAN_ETH_HLEN;
+
+ if (actual_mtu < 64 || actual_mtu > BCMENET_MAX_MTU)
+ return -EINVAL;
+
+ /*
+ * setup maximum size before we get overflow mark in
+ * descriptor, note that this will not prevent reception of
+ * big frames, they will be split into multiple buffers
+ * anyway
+ */
+ priv->hw_mtu = actual_mtu;
+
+ /*
+ * align rx buffer size to dma burst len, account FCS since
+ * it's appended
+ */
+ priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN,
+ BCMENET_DMA_MAXBURST * 4);
+ return 0;
+}
+
+/*
+ * adjust mtu, can't be called while device is running
+ */
+static int bcm_enet_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int ret;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ret = compute_hw_mtu(netdev_priv(dev), new_mtu);
+ if (ret)
+ return ret;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+/*
* preinit hardware to allow mii operation while device is down
*/
static void bcm_enet_hw_preinit(struct bcm_enet_priv *priv)
@@ -1582,6 +1632,10 @@ static int __devinit bcm_enet_probe(struct platform_device *pdev)
priv = netdev_priv(dev);
memset(priv, 0, sizeof(*priv));
+ ret = compute_hw_mtu(priv, dev->mtu);
+ if (ret)
+ goto out;
+
iomem_size = res_mem->end - res_mem->start + 1;
if (!request_mem_region(res_mem->start, iomem_size, "bcm63xx_enet")) {
ret = -EBUSY;
@@ -1721,6 +1775,7 @@ static int __devinit bcm_enet_probe(struct platform_device *pdev)
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = bcm_enet_netpoll;
#endif
+ dev->change_mtu = bcm_enet_change_mtu;
SET_ETHTOOL_OPS(dev, &bcm_enet_ethtool_ops);
@@ -1754,6 +1809,7 @@ err:
enet_writel(priv, 0, ENET_MIISC_REG);
iounmap(priv->base);
}
+out:
free_netdev(dev);
return ret;
}