#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/clk.h>
+#include <linux/crc32.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
                if (i == queue->tx_head) {
                        ctrl |= MACB_BF(TX_LSO, lso_ctrl);
                        ctrl |= MACB_BF(TX_TCP_SEQ_SRC, seq_ctrl);
+                       if ((bp->dev->features & NETIF_F_HW_CSUM) &&
+                           skb->ip_summed != CHECKSUM_PARTIAL && !lso_ctrl)
+                               ctrl |= MACB_BIT(TX_NOCRC);
                } else
                        /* Only set MSS/MFS on payload descriptors
                         * (second or later descriptor)
        return 0;
 }
 
+static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
+{
+       bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb);
+       int padlen = ETH_ZLEN - (*skb)->len;
+       int headroom = skb_headroom(*skb);
+       int tailroom = skb_tailroom(*skb);
+       struct sk_buff *nskb;
+       u32 fcs;
+
+       if (!(ndev->features & NETIF_F_HW_CSUM) ||
+           !((*skb)->ip_summed != CHECKSUM_PARTIAL) ||
+           skb_shinfo(*skb)->gso_size) /* Not available for GSO */
+               return 0;
+
+       if (padlen <= 0) {
+               /* FCS could be appeded to tailroom. */
+               if (tailroom >= ETH_FCS_LEN)
+                       goto add_fcs;
+               /* FCS could be appeded by moving data to headroom. */
+               else if (!cloned && headroom + tailroom >= ETH_FCS_LEN)
+                       padlen = 0;
+               /* No room for FCS, need to reallocate skb. */
+               else
+                       padlen = ETH_FCS_LEN - tailroom;
+       } else {
+               /* Add room for FCS. */
+               padlen += ETH_FCS_LEN;
+       }
+
+       if (!cloned && headroom + tailroom >= padlen) {
+               (*skb)->data = memmove((*skb)->head, (*skb)->data, (*skb)->len);
+               skb_set_tail_pointer(*skb, (*skb)->len);
+       } else {
+               nskb = skb_copy_expand(*skb, 0, padlen, GFP_ATOMIC);
+               if (!nskb)
+                       return -ENOMEM;
+
+               dev_kfree_skb_any(*skb);
+               *skb = nskb;
+       }
+
+       if (padlen) {
+               if (padlen >= ETH_FCS_LEN)
+                       skb_put_zero(*skb, padlen - ETH_FCS_LEN);
+               else
+                       skb_trim(*skb, ETH_FCS_LEN - padlen);
+       }
+
+add_fcs:
+       /* set FCS to packet */
+       fcs = crc32_le(~0, (*skb)->data, (*skb)->len);
+       fcs = ~fcs;
+
+       skb_put_u8(*skb, fcs            & 0xff);
+       skb_put_u8(*skb, (fcs >> 8)     & 0xff);
+       skb_put_u8(*skb, (fcs >> 16)    & 0xff);
+       skb_put_u8(*skb, (fcs >> 24)    & 0xff);
+
+       return 0;
+}
+
 static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        u16 queue_index = skb_get_queue_mapping(skb);
                return ret;
        }
 
+       if (macb_pad_and_fcs(&skb, dev)) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
        is_lso = (skb_shinfo(skb)->gso_size != 0);
 
        if (is_lso) {