diff options
author | Jim Baxter <jim_baxter@mentor.com> | 2013-07-02 17:52:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-07-03 19:45:34 -0400 |
commit | cdffcf1bc721032d261a7a9da353b41f0d830410 (patch) | |
tree | 4746033333eae7eb588f2eca7eb7770eb38f563f /drivers/net | |
parent | bc2bebe8de8ed4ba6482c9cc370b0dd72ffe8cd2 (diff) |
net: fec: Add VLAN receive HW support.
This enables the driver to take advantage of the FEC VLAN
indicator to improve performance.
Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/freescale/fec.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 62 |
2 files changed, 55 insertions, 10 deletions
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 8362a0399afb..2b0a0ea4f8e7 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h | |||
@@ -203,6 +203,9 @@ struct bufdesc_ex { | |||
203 | #define BD_ENET_RX_CL ((ushort)0x0001) | 203 | #define BD_ENET_RX_CL ((ushort)0x0001) |
204 | #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ | 204 | #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ |
205 | 205 | ||
206 | /* Enhanced buffer descriptor control/status used by Ethernet receive */ | ||
207 | #define BD_ENET_RX_VLAN 0x00000004 | ||
208 | |||
206 | /* Buffer descriptor control/status used by Ethernet transmit. | 209 | /* Buffer descriptor control/status used by Ethernet transmit. |
207 | */ | 210 | */ |
208 | #define BD_ENET_TX_READY ((ushort)0x8000) | 211 | #define BD_ENET_TX_READY ((ushort)0x8000) |
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 1f7ff2268bd0..d3ad5ea711d3 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #include <linux/of_gpio.h> | 54 | #include <linux/of_gpio.h> |
55 | #include <linux/of_net.h> | 55 | #include <linux/of_net.h> |
56 | #include <linux/regulator/consumer.h> | 56 | #include <linux/regulator/consumer.h> |
57 | #include <linux/if_vlan.h> | ||
57 | 58 | ||
58 | #include <asm/cacheflush.h> | 59 | #include <asm/cacheflush.h> |
59 | 60 | ||
@@ -90,6 +91,8 @@ static void set_multicast_list(struct net_device *ndev); | |||
90 | #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) | 91 | #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) |
91 | /* Controller has hardware checksum support */ | 92 | /* Controller has hardware checksum support */ |
92 | #define FEC_QUIRK_HAS_CSUM (1 << 5) | 93 | #define FEC_QUIRK_HAS_CSUM (1 << 5) |
94 | /* Controller has hardware vlan support */ | ||
95 | #define FEC_QUIRK_HAS_VLAN (1 << 6) | ||
93 | 96 | ||
94 | static struct platform_device_id fec_devtype[] = { | 97 | static struct platform_device_id fec_devtype[] = { |
95 | { | 98 | { |
@@ -108,7 +111,8 @@ static struct platform_device_id fec_devtype[] = { | |||
108 | }, { | 111 | }, { |
109 | .name = "imx6q-fec", | 112 | .name = "imx6q-fec", |
110 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | | 113 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | |
111 | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM, | 114 | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | |
115 | FEC_QUIRK_HAS_VLAN, | ||
112 | }, { | 116 | }, { |
113 | .name = "mvf600-fec", | 117 | .name = "mvf600-fec", |
114 | .driver_data = FEC_QUIRK_ENET_MAC, | 118 | .driver_data = FEC_QUIRK_ENET_MAC, |
@@ -179,11 +183,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
179 | #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) | 183 | #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) |
180 | #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) | 184 | #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) |
181 | 185 | ||
182 | /* The FEC stores dest/src/type, data, and checksum for receive packets. | 186 | /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. |
183 | */ | 187 | */ |
184 | #define PKT_MAXBUF_SIZE 1518 | 188 | #define PKT_MAXBUF_SIZE 1522 |
185 | #define PKT_MINBUF_SIZE 64 | 189 | #define PKT_MINBUF_SIZE 64 |
186 | #define PKT_MAXBLR_SIZE 1520 | 190 | #define PKT_MAXBLR_SIZE 1536 |
187 | 191 | ||
188 | /* FEC receive acceleration */ | 192 | /* FEC receive acceleration */ |
189 | #define FEC_RACC_IPDIS (1 << 1) | 193 | #define FEC_RACC_IPDIS (1 << 1) |
@@ -806,6 +810,9 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
806 | ushort pkt_len; | 810 | ushort pkt_len; |
807 | __u8 *data; | 811 | __u8 *data; |
808 | int pkt_received = 0; | 812 | int pkt_received = 0; |
813 | struct bufdesc_ex *ebdp = NULL; | ||
814 | bool vlan_packet_rcvd = false; | ||
815 | u16 vlan_tag; | ||
809 | 816 | ||
810 | #ifdef CONFIG_M532x | 817 | #ifdef CONFIG_M532x |
811 | flush_cache_all(); | 818 | flush_cache_all(); |
@@ -869,6 +876,24 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
869 | if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) | 876 | if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) |
870 | swap_buffer(data, pkt_len); | 877 | swap_buffer(data, pkt_len); |
871 | 878 | ||
879 | /* Extract the enhanced buffer descriptor */ | ||
880 | ebdp = NULL; | ||
881 | if (fep->bufdesc_ex) | ||
882 | ebdp = (struct bufdesc_ex *)bdp; | ||
883 | |||
884 | /* If this is a VLAN packet remove the VLAN Tag */ | ||
885 | vlan_packet_rcvd = false; | ||
886 | if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && | ||
887 | fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { | ||
888 | /* Push and remove the vlan tag */ | ||
889 | struct vlan_hdr *vlan_header = | ||
890 | (struct vlan_hdr *) (data + ETH_HLEN); | ||
891 | vlan_tag = ntohs(vlan_header->h_vlan_TCI); | ||
892 | pkt_len -= VLAN_HLEN; | ||
893 | |||
894 | vlan_packet_rcvd = true; | ||
895 | } | ||
896 | |||
872 | /* This does 16 byte alignment, exactly what we need. | 897 | /* This does 16 byte alignment, exactly what we need. |
873 | * The packet length includes FCS, but we don't want to | 898 | * The packet length includes FCS, but we don't want to |
874 | * include that when passing upstream as it messes up | 899 | * include that when passing upstream as it messes up |
@@ -879,9 +904,18 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
879 | if (unlikely(!skb)) { | 904 | if (unlikely(!skb)) { |
880 | ndev->stats.rx_dropped++; | 905 | ndev->stats.rx_dropped++; |
881 | } else { | 906 | } else { |
907 | int payload_offset = (2 * ETH_ALEN); | ||
882 | skb_reserve(skb, NET_IP_ALIGN); | 908 | skb_reserve(skb, NET_IP_ALIGN); |
883 | skb_put(skb, pkt_len - 4); /* Make room */ | 909 | skb_put(skb, pkt_len - 4); /* Make room */ |
884 | skb_copy_to_linear_data(skb, data, pkt_len - 4); | 910 | |
911 | /* Extract the frame data without the VLAN header. */ | ||
912 | skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); | ||
913 | if (vlan_packet_rcvd) | ||
914 | payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; | ||
915 | skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), | ||
916 | data + payload_offset, | ||
917 | pkt_len - 4 - (2 * ETH_ALEN)); | ||
918 | |||
885 | skb->protocol = eth_type_trans(skb, ndev); | 919 | skb->protocol = eth_type_trans(skb, ndev); |
886 | 920 | ||
887 | /* Get receive timestamp from the skb */ | 921 | /* Get receive timestamp from the skb */ |
@@ -889,8 +923,6 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
889 | struct skb_shared_hwtstamps *shhwtstamps = | 923 | struct skb_shared_hwtstamps *shhwtstamps = |
890 | skb_hwtstamps(skb); | 924 | skb_hwtstamps(skb); |
891 | unsigned long flags; | 925 | unsigned long flags; |
892 | struct bufdesc_ex *ebdp = | ||
893 | (struct bufdesc_ex *)bdp; | ||
894 | 926 | ||
895 | memset(shhwtstamps, 0, sizeof(*shhwtstamps)); | 927 | memset(shhwtstamps, 0, sizeof(*shhwtstamps)); |
896 | 928 | ||
@@ -901,9 +933,7 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
901 | } | 933 | } |
902 | 934 | ||
903 | if (fep->bufdesc_ex && | 935 | if (fep->bufdesc_ex && |
904 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { | 936 | (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { |
905 | struct bufdesc_ex *ebdp = | ||
906 | (struct bufdesc_ex *)bdp; | ||
907 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { | 937 | if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { |
908 | /* don't check it */ | 938 | /* don't check it */ |
909 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 939 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
@@ -912,6 +942,12 @@ fec_enet_rx(struct net_device *ndev, int budget) | |||
912 | } | 942 | } |
913 | } | 943 | } |
914 | 944 | ||
945 | /* Handle received VLAN packets */ | ||
946 | if (vlan_packet_rcvd) | ||
947 | __vlan_hwaccel_put_tag(skb, | ||
948 | htons(ETH_P_8021Q), | ||
949 | vlan_tag); | ||
950 | |||
915 | if (!skb_defer_rx_timestamp(skb)) | 951 | if (!skb_defer_rx_timestamp(skb)) |
916 | napi_gro_receive(&fep->napi, skb); | 952 | napi_gro_receive(&fep->napi, skb); |
917 | } | 953 | } |
@@ -1924,6 +1960,12 @@ static int fec_enet_init(struct net_device *ndev) | |||
1924 | writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); | 1960 | writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); |
1925 | netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); | 1961 | netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); |
1926 | 1962 | ||
1963 | if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) { | ||
1964 | /* enable hw VLAN support */ | ||
1965 | ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; | ||
1966 | ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; | ||
1967 | } | ||
1968 | |||
1927 | if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { | 1969 | if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { |
1928 | /* enable hw accelerator */ | 1970 | /* enable hw accelerator */ |
1929 | ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 1971 | ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |