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>
This commit is contained in:
Jim Baxter 2013-07-02 22:52:56 +01:00 committed by David S. Miller
parent bc2bebe8de
commit cdffcf1bc7
2 changed files with 55 additions and 10 deletions

View file

@ -203,6 +203,9 @@ struct bufdesc_ex {
#define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_CL ((ushort)0x0001)
#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
/* Enhanced buffer descriptor control/status used by Ethernet receive */
#define BD_ENET_RX_VLAN 0x00000004
/* Buffer descriptor control/status used by Ethernet transmit. /* Buffer descriptor control/status used by Ethernet transmit.
*/ */
#define BD_ENET_TX_READY ((ushort)0x8000) #define BD_ENET_TX_READY ((ushort)0x8000)

View file

@ -54,6 +54,7 @@
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/if_vlan.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
@ -90,6 +91,8 @@ static void set_multicast_list(struct net_device *ndev);
#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4)
/* Controller has hardware checksum support */ /* Controller has hardware checksum support */
#define FEC_QUIRK_HAS_CSUM (1 << 5) #define FEC_QUIRK_HAS_CSUM (1 << 5)
/* Controller has hardware vlan support */
#define FEC_QUIRK_HAS_VLAN (1 << 6)
static struct platform_device_id fec_devtype[] = { static struct platform_device_id fec_devtype[] = {
{ {
@ -108,7 +111,8 @@ static struct platform_device_id fec_devtype[] = {
}, { }, {
.name = "imx6q-fec", .name = "imx6q-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM, FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
FEC_QUIRK_HAS_VLAN,
}, { }, {
.name = "mvf600-fec", .name = "mvf600-fec",
.driver_data = FEC_QUIRK_ENET_MAC, .driver_data = FEC_QUIRK_ENET_MAC,
@ -179,11 +183,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
/* The FEC stores dest/src/type, data, and checksum for receive packets. /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
*/ */
#define PKT_MAXBUF_SIZE 1518 #define PKT_MAXBUF_SIZE 1522
#define PKT_MINBUF_SIZE 64 #define PKT_MINBUF_SIZE 64
#define PKT_MAXBLR_SIZE 1520 #define PKT_MAXBLR_SIZE 1536
/* FEC receive acceleration */ /* FEC receive acceleration */
#define FEC_RACC_IPDIS (1 << 1) #define FEC_RACC_IPDIS (1 << 1)
@ -806,6 +810,9 @@ fec_enet_rx(struct net_device *ndev, int budget)
ushort pkt_len; ushort pkt_len;
__u8 *data; __u8 *data;
int pkt_received = 0; int pkt_received = 0;
struct bufdesc_ex *ebdp = NULL;
bool vlan_packet_rcvd = false;
u16 vlan_tag;
#ifdef CONFIG_M532x #ifdef CONFIG_M532x
flush_cache_all(); flush_cache_all();
@ -869,6 +876,24 @@ fec_enet_rx(struct net_device *ndev, int budget)
if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
swap_buffer(data, pkt_len); swap_buffer(data, pkt_len);
/* Extract the enhanced buffer descriptor */
ebdp = NULL;
if (fep->bufdesc_ex)
ebdp = (struct bufdesc_ex *)bdp;
/* If this is a VLAN packet remove the VLAN Tag */
vlan_packet_rcvd = false;
if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) {
/* Push and remove the vlan tag */
struct vlan_hdr *vlan_header =
(struct vlan_hdr *) (data + ETH_HLEN);
vlan_tag = ntohs(vlan_header->h_vlan_TCI);
pkt_len -= VLAN_HLEN;
vlan_packet_rcvd = true;
}
/* This does 16 byte alignment, exactly what we need. /* This does 16 byte alignment, exactly what we need.
* The packet length includes FCS, but we don't want to * The packet length includes FCS, but we don't want to
* include that when passing upstream as it messes up * include that when passing upstream as it messes up
@ -879,9 +904,18 @@ fec_enet_rx(struct net_device *ndev, int budget)
if (unlikely(!skb)) { if (unlikely(!skb)) {
ndev->stats.rx_dropped++; ndev->stats.rx_dropped++;
} else { } else {
int payload_offset = (2 * ETH_ALEN);
skb_reserve(skb, NET_IP_ALIGN); skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, pkt_len - 4); /* Make room */ skb_put(skb, pkt_len - 4); /* Make room */
skb_copy_to_linear_data(skb, data, pkt_len - 4);
/* Extract the frame data without the VLAN header. */
skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN));
if (vlan_packet_rcvd)
payload_offset = (2 * ETH_ALEN) + VLAN_HLEN;
skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN),
data + payload_offset,
pkt_len - 4 - (2 * ETH_ALEN));
skb->protocol = eth_type_trans(skb, ndev); skb->protocol = eth_type_trans(skb, ndev);
/* Get receive timestamp from the skb */ /* Get receive timestamp from the skb */
@ -889,8 +923,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
struct skb_shared_hwtstamps *shhwtstamps = struct skb_shared_hwtstamps *shhwtstamps =
skb_hwtstamps(skb); skb_hwtstamps(skb);
unsigned long flags; unsigned long flags;
struct bufdesc_ex *ebdp =
(struct bufdesc_ex *)bdp;
memset(shhwtstamps, 0, sizeof(*shhwtstamps)); memset(shhwtstamps, 0, sizeof(*shhwtstamps));
@ -901,9 +933,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
} }
if (fep->bufdesc_ex && if (fep->bufdesc_ex &&
(fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
struct bufdesc_ex *ebdp =
(struct bufdesc_ex *)bdp;
if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
/* don't check it */ /* don't check it */
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
@ -912,6 +942,12 @@ fec_enet_rx(struct net_device *ndev, int budget)
} }
} }
/* Handle received VLAN packets */
if (vlan_packet_rcvd)
__vlan_hwaccel_put_tag(skb,
htons(ETH_P_8021Q),
vlan_tag);
if (!skb_defer_rx_timestamp(skb)) if (!skb_defer_rx_timestamp(skb))
napi_gro_receive(&fep->napi, skb); napi_gro_receive(&fep->napi, skb);
} }
@ -1924,6 +1960,12 @@ static int fec_enet_init(struct net_device *ndev)
writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
/* enable hw VLAN support */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
}
if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
/* enable hw accelerator */ /* enable hw accelerator */
ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM