TLFRCR  = 0x0758,
        RFCR    = 0x0760,
        MAFCR   = 0x0778,
-       CSR0    = 0x0800,       /* RZ/G2L only */
+
+       /* TOE registers (RZ/G2L only) */
+       CSR0    = 0x0800,
+       CSR2    = 0x0808,
 };
 
 
        CSR0_RPE        = 0x00000020,
 };
 
+enum CSR2_BIT {
+       CSR2_RIP4       = 0x00000001,
+       CSR2_RTCP4      = 0x00000010,
+       CSR2_RUDP4      = 0x00000020,
+       CSR2_RICMP4     = 0x00000040,
+       CSR2_RTCP6      = 0x00100000,
+       CSR2_RUDP6      = 0x00200000,
+       CSR2_RICMP6     = 0x00400000,
+       CSR2_RHOP       = 0x01000000,
+       CSR2_RROUT      = 0x02000000,
+       CSR2_RAHD       = 0x04000000,
+       CSR2_RDHD       = 0x08000000,
+};
+
 #define DBAT_ENTRY_NUM 22
 #define RX_QUEUE_OFFSET        4
 #define NUM_RX_QUEUE   2
 
        return -ENOMEM;
 }
 
+static void ravb_csum_init_gbeth(struct net_device *ndev)
+{
+       if (!(ndev->features & NETIF_F_RXCSUM))
+               goto done;
+
+       ravb_write(ndev, 0, CSR0);
+       if (ravb_wait(ndev, CSR0, CSR0_RPE, 0)) {
+               netdev_err(ndev, "Timeout enabling hardware checksum\n");
+               ndev->features &= ~NETIF_F_RXCSUM;
+       } else {
+               ravb_write(ndev, CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4,
+                          CSR2);
+       }
+
+done:
+       ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0);
+}
+
 static void ravb_emac_init_gbeth(struct net_device *ndev)
 {
        struct ravb_private *priv = netdev_priv(ndev);
 
        /* E-MAC status register clear */
        ravb_write(ndev, ECSR_ICD | ECSR_LCHNG | ECSR_PFRI, ECSR);
-       ravb_write(ndev, CSR0_TPE | CSR0_RPE, CSR0);
+
+       ravb_csum_init_gbeth(ndev);
 
        /* E-MAC interrupt enable register */
        ravb_write(ndev, ECSIPR_ICDIP, ECSIPR);
        }
 }
 
+static void ravb_rx_csum_gbeth(struct sk_buff *skb)
+{
+       __wsum csum_ip_hdr, csum_proto;
+       u8 *hw_csum;
+
+       /* The hardware checksum status is contained in sizeof(__sum16) * 2 = 4
+        * bytes appended to packet data. First 2 bytes is ip header checksum
+        * and last 2 bytes is protocol checksum.
+        */
+       if (unlikely(skb->len < sizeof(__sum16) * 2))
+               return;
+
+       hw_csum = skb_tail_pointer(skb) - sizeof(__sum16);
+       csum_proto = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum));
+
+       hw_csum -= sizeof(__sum16);
+       csum_ip_hdr = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum));
+       skb_trim(skb, skb->len - 2 * sizeof(__sum16));
+
+       /* TODO: IPV6 Rx checksum */
+       if (skb->protocol == htons(ETH_P_IP) && !csum_ip_hdr && !csum_proto)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
 static void ravb_rx_csum(struct sk_buff *skb)
 {
        u8 *hw_csum;
                                skb = ravb_get_skb_gbeth(ndev, entry, desc);
                                skb_put(skb, pkt_len);
                                skb->protocol = eth_type_trans(skb, ndev);
+                               if (ndev->features & NETIF_F_RXCSUM)
+                                       ravb_rx_csum_gbeth(skb);
                                napi_gro_receive(&priv->napi[q], skb);
                                stats->rx_packets++;
                                stats->rx_bytes += pkt_len;
                                dev_kfree_skb(skb);
                                priv->rx_1st_skb->protocol =
                                        eth_type_trans(priv->rx_1st_skb, ndev);
+                               if (ndev->features & NETIF_F_RXCSUM)
+                                       ravb_rx_csum_gbeth(skb);
                                napi_gro_receive(&priv->napi[q],
                                                 priv->rx_1st_skb);
                                stats->rx_packets++;
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
+static int ravb_endisable_csum_gbeth(struct net_device *ndev, enum ravb_reg reg,
+                                    u32 val, u32 mask)
+{
+       u32 csr0 = CSR0_TPE | CSR0_RPE;
+       int ret;
+
+       ravb_write(ndev, csr0 & ~mask, CSR0);
+       ret = ravb_wait(ndev, CSR0, mask, 0);
+       if (!ret)
+               ravb_write(ndev, val, reg);
+
+       ravb_write(ndev, csr0, CSR0);
+
+       return ret;
+}
+
 static int ravb_set_features_gbeth(struct net_device *ndev,
                                   netdev_features_t features)
 {
-       /* Place holder */
-       return 0;
+       netdev_features_t changed = ndev->features ^ features;
+       struct ravb_private *priv = netdev_priv(ndev);
+       unsigned long flags;
+       int ret = 0;
+       u32 val;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (changed & NETIF_F_RXCSUM) {
+               if (features & NETIF_F_RXCSUM)
+                       val = CSR2_RIP4 | CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4;
+               else
+                       val = 0;
+
+               ret = ravb_endisable_csum_gbeth(ndev, CSR2, val, CSR0_RPE);
+               if (ret)
+                       goto done;
+       }
+
+       ndev->features = features;
+done:
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return ret;
 }
 
 static int ravb_set_features_rcar(struct net_device *ndev,
        .emac_init = ravb_emac_init_gbeth,
        .gstrings_stats = ravb_gstrings_stats_gbeth,
        .gstrings_size = sizeof(ravb_gstrings_stats_gbeth),
+       .net_hw_features = NETIF_F_RXCSUM,
+       .net_features = NETIF_F_RXCSUM,
        .stats_len = ARRAY_SIZE(ravb_gstrings_stats_gbeth),
        .max_rx_len = ALIGN(GBETH_RX_BUFF_MAX, RAVB_ALIGN),
        .tccr_mask = TCCR_TSRQ0,