diff options
author | Jim Baxter <jim_baxter@mentor.com> | 2013-04-19 04:10:49 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-25 03:58:26 -0400 |
commit | 4c09eed9dc422e980fabdb25434ef68e599b704c (patch) | |
tree | a00da8b76aed9cc916649eafdaf9504e27038b03 /drivers/net/ethernet/freescale/fec_main.c | |
parent | cf62cb72d63944f4dcc7376efd84959afc9366cb (diff) |
net: fec: Enable imx6 enet checksum acceleration.
Enables hardware generation of IP header and
protocol specific checksums for transmitted
packets.
Enabled hardware discarding of received packets with
invalid IP header or protocol specific checksums.
The feature is enabled by default but can be
enabled/disabled by ethtool.
Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_main.c')
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 100 |
1 files changed, 97 insertions, 3 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 2451ab1b5a83..b9748f14ea78 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -34,6 +34,12 @@ | |||
34 | #include <linux/netdevice.h> | 34 | #include <linux/netdevice.h> |
35 | #include <linux/etherdevice.h> | 35 | #include <linux/etherdevice.h> |
36 | #include <linux/skbuff.h> | 36 | #include <linux/skbuff.h> |
37 | #include <linux/in.h> | ||
38 | #include <linux/ip.h> | ||
39 | #include <net/ip.h> | ||
40 | #include <linux/tcp.h> | ||
41 | #include <linux/udp.h> | ||
42 | #include <linux/icmp.h> | ||
37 | #include <linux/spinlock.h> | 43 | #include <linux/spinlock.h> |
38 | #include <linux/workqueue.h> | 44 | #include <linux/workqueue.h> |
39 | #include <linux/bitops.h> | 45 | #include <linux/bitops.h> |
@@ -176,6 +182,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
176 | #define PKT_MINBUF_SIZE 64 | 182 | #define PKT_MINBUF_SIZE 64 |
177 | #define PKT_MAXBLR_SIZE 1520 | 183 | #define PKT_MAXBLR_SIZE 1520 |
178 | 184 | ||
185 | /* FEC receive acceleration */ | ||
186 | #define FEC_RACC_IPDIS (1 << 1) | ||
187 | #define FEC_RACC_PRODIS (1 << 2) | ||
188 | #define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) | ||
189 | |||
179 | /* | 190 | /* |
180 | * The 5270/5271/5280/5282/532x RX control register also contains maximum frame | 191 | * The 5270/5271/5280/5282/532x RX control register also contains maximum frame |
181 | * size bits. Other FEC hardware does not, so we need to take that into | 192 | * size bits. Other FEC hardware does not, so we need to take that into |
@@ -236,6 +247,21 @@ static void *swap_buffer(void *bufaddr, int len) | |||
236 | return bufaddr; | 247 | return bufaddr; |
237 | } | 248 | } |
238 | 249 | ||
250 | static int | ||
251 | fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) | ||
252 | { | ||
253 | /* Only run for packets requiring a checksum. */ | ||
254 | if (skb->ip_summed != CHECKSUM_PARTIAL) | ||
255 | return 0; | ||
256 | |||
257 | if (unlikely(skb_cow_head(skb, 0))) | ||
258 | return -1; | ||
259 | |||
260 | *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
239 | static netdev_tx_t | 265 | static netdev_tx_t |
240 | fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | 266 | fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) |
241 | { | 267 | { |
@@ -248,7 +274,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
248 | unsigned int index; | 274 | unsigned int index; |
249 | 275 | ||
250 | if (!fep->link) { | 276 | if (!fep->link) { |
251 | /* Link is down or autonegotiation is in progress. */ | 277 | /* Link is down or auto-negotiation is in progress. */ |
252 | return NETDEV_TX_BUSY; | 278 | return NETDEV_TX_BUSY; |
253 | } | 279 | } |
254 | 280 | ||
@@ -265,6 +291,12 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
265 | return NETDEV_TX_BUSY; | 291 | return NETDEV_TX_BUSY; |
266 | } | 292 | } |
267 | 293 | ||
294 | /* Protocol checksum off-load for TCP and UDP. */ | ||
295 | if (fec_enet_clear_csum(skb, ndev)) { | ||
296 | kfree_skb(skb); | ||
297 | return NETDEV_TX_OK; | ||
298 | } | ||
299 | |||
268 | /* Clear all of the status flags */ | 300 | /* Clear all of the status flags */ |
269 | status &= ~BD_ENET_TX_STATS; | 301 | status &= ~BD_ENET_TX_STATS; |
270 | 302 | ||
@@ -321,8 +353,14 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |||
321 | ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT); | 353 | ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT); |
322 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; | 354 | skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; |
323 | } else { | 355 | } else { |
324 | |||
325 | ebdp->cbd_esc = BD_ENET_TX_INT; | 356 | ebdp->cbd_esc = BD_ENET_TX_INT; |
357 | |||
358 | /* Enable protocol checksum flags | ||
359 | * We do not bother with the IP Checksum bits as they | ||
360 | * are done by the kernel | ||
361 | */ | ||
362 | if (skb->ip_summed == CHECKSUM_PARTIAL) | ||
363 | ebdp->cbd_esc |= BD_ENET_TX_PINS; | ||
326 | } | 364 | } |
327 | } | 365 | } |
328 | /* If this was the last BD in the ring, start at the beginning again. */ | 366 | /* If this was the last BD in the ring, start at the beginning again. */ |
@@ -402,6 +440,7 @@ fec_restart(struct net_device *ndev, int duplex) | |||
402 | const struct platform_device_id *id_entry = | 440 | const struct platform_device_id *id_entry = |
403 | platform_get_device_id(fep->pdev); | 441 | platform_get_device_id(fep->pdev); |
404 | int i; | 442 | int i; |
443 | u32 val; | ||
405 | u32 temp_mac[2]; | 444 | u32 temp_mac[2]; |
406 | u32 rcntl = OPT_FRAME_SIZE | 0x04; | 445 | u32 rcntl = OPT_FRAME_SIZE | 0x04; |
407 | u32 ecntl = 0x2; /* ETHEREN */ | 446 | u32 ecntl = 0x2; /* ETHEREN */ |
@@ -468,6 +507,14 @@ fec_restart(struct net_device *ndev, int duplex) | |||
468 | /* Set MII speed */ | 507 | /* Set MII speed */ |
469 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | 508 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
470 | 509 | ||
510 | /* set RX checksum */ | ||
511 | val = readl(fep->hwp + FEC_RACC); | ||
512 | if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) | ||
513 | val |= FEC_RACC_OPTIONS; | ||
514 | else | ||
515 | val &= ~FEC_RACC_OPTIONS; | ||
516 | writel(val, fep->hwp + FEC_RACC); | ||
517 | |||
471 | /* | 518 | /* |
472 | * The phy interface and speed need to get configured | 519 | * The phy interface and speed need to get configured |
473 | * differently on enet-mac. | 520 | * differently on enet-mac. |
@@ -525,7 +572,7 @@ fec_restart(struct net_device *ndev, int duplex) | |||
525 | fep->phy_dev && fep->phy_dev->pause)) { | 572 | fep->phy_dev && fep->phy_dev->pause)) { |
526 | rcntl |= FEC_ENET_FCE; | 573 | rcntl |= FEC_ENET_FCE; |
527 | 574 | ||
528 | /* set FIFO thresh hold parameter to reduce overrun */ | 575 | /* set FIFO threshold parameter to reduce overrun */ |
529 | writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); | 576 | writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); |
530 | writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); | 577 | writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); |
531 | writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); | 578 | writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); |
@@ -813,6 +860,18 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
813 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); | 860 | spin_unlock_irqrestore(&fep->tmreg_lock, flags); |
814 | } | 861 | } |
815 | 862 | ||
863 | if (fep->bufdesc_ex && | ||
864 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { | ||
865 | struct bufdesc_ex *ebdp = | ||
866 | (struct bufdesc_ex *)bdp; | ||
867 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { | ||
868 | /* don't check it */ | ||
869 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
870 | } else { | ||
871 | skb_checksum_none_assert(skb); | ||
872 | } | ||
873 | } | ||
874 | |||
816 | if (!skb_defer_rx_timestamp(skb)) | 875 | if (!skb_defer_rx_timestamp(skb)) |
817 | napi_gro_receive(&fep->napi, skb); | 876 | napi_gro_receive(&fep->napi, skb); |
818 | } | 877 | } |
@@ -1614,6 +1673,33 @@ static void fec_poll_controller(struct net_device *dev) | |||
1614 | } | 1673 | } |
1615 | #endif | 1674 | #endif |
1616 | 1675 | ||
1676 | static int fec_set_features(struct net_device *netdev, | ||
1677 | netdev_features_t features) | ||
1678 | { | ||
1679 | struct fec_enet_private *fep = netdev_priv(netdev); | ||
1680 | netdev_features_t changed = features ^ netdev->features; | ||
1681 | |||
1682 | netdev->features = features; | ||
1683 | |||
1684 | /* Receive checksum has been changed */ | ||
1685 | if (changed & NETIF_F_RXCSUM) { | ||
1686 | if (features & NETIF_F_RXCSUM) | ||
1687 | fep->csum_flags |= FLAG_RX_CSUM_ENABLED; | ||
1688 | else | ||
1689 | fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; | ||
1690 | |||
1691 | if (netif_running(netdev)) { | ||
1692 | fec_stop(netdev); | ||
1693 | fec_restart(netdev, fep->phy_dev->duplex); | ||
1694 | netif_wake_queue(netdev); | ||
1695 | } else { | ||
1696 | fec_restart(netdev, fep->phy_dev->duplex); | ||
1697 | } | ||
1698 | } | ||
1699 | |||
1700 | return 0; | ||
1701 | } | ||
1702 | |||
1617 | static const struct net_device_ops fec_netdev_ops = { | 1703 | static const struct net_device_ops fec_netdev_ops = { |
1618 | .ndo_open = fec_enet_open, | 1704 | .ndo_open = fec_enet_open, |
1619 | .ndo_stop = fec_enet_close, | 1705 | .ndo_stop = fec_enet_close, |
@@ -1627,6 +1713,7 @@ static const struct net_device_ops fec_netdev_ops = { | |||
1627 | #ifdef CONFIG_NET_POLL_CONTROLLER | 1713 | #ifdef CONFIG_NET_POLL_CONTROLLER |
1628 | .ndo_poll_controller = fec_poll_controller, | 1714 | .ndo_poll_controller = fec_poll_controller, |
1629 | #endif | 1715 | #endif |
1716 | .ndo_set_features = fec_set_features, | ||
1630 | }; | 1717 | }; |
1631 | 1718 | ||
1632 | /* | 1719 | /* |
@@ -1668,6 +1755,13 @@ static int fec_enet_init(struct net_device *ndev) | |||
1668 | writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); | 1755 | writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); |
1669 | netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); | 1756 | netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); |
1670 | 1757 | ||
1758 | /* enable hw accelerator */ | ||
1759 | ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ||
1760 | | NETIF_F_RXCSUM); | ||
1761 | ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ||
1762 | | NETIF_F_RXCSUM); | ||
1763 | fep->csum_flags |= FLAG_RX_CSUM_ENABLED; | ||
1764 | |||
1671 | fec_restart(ndev, 0); | 1765 | fec_restart(ndev, 0); |
1672 | 1766 | ||
1673 | return 0; | 1767 | return 0; |