Commit 1caf73f0 authored by Jeff Garzik's avatar Jeff Garzik

Update 8139cp net driver for the following:

* Merge VLAN defines and support from vger, ifdef'd out until
  API appears in main tree.
* Support RX checksumming.
* Correct CP_MAX_MTU.
* Clarify CP_MIN_MTU issues.
parent 03789917
......@@ -2,6 +2,7 @@
/*
Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com>
Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c]
Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c]
Copyright 2001 Manfred Spraul [natsemi.c]
Copyright 1999-2001 by Donald Becker. [natsemi.c]
......@@ -29,8 +30,8 @@
* Consider Rx interrupt mitigation using TimerIntr
* Implement 8139C+ statistics dump; maybe not...
h/w stats can be reset only by software reset
* Rx checksumming
* Tx checksumming
* Handle netif_rx return value
* ETHTOOL_GREGS, ETHTOOL_[GS]WOL,
* Investigate using skb->priority with h/w VLAN priority
* Investigate using High Priority Tx Queue with skb->priority
......@@ -38,8 +39,8 @@
* Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error
* Implement Tx software interrupt mitigation via
Tx descriptor bit
* Determine correct value for CP_{MIN,MAX}_MTU, instead of
using conservative guesses.
* The real minimum of CP_MIN_MTU is 4 bytes. However,
for this to be supported, one must(?) turn on packet padding.
*/
......@@ -48,8 +49,10 @@
#define DRV_RELDATE "Feb 27, 2002"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
......@@ -57,9 +60,15 @@
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#define CP_VLAN_TAG_USED 0
#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
do { (tx_desc)->opts2 = 0; } while (0)
/* These identify the driver base version and may not be removed. */
static char version[] __devinitdata =
KERN_INFO DRV_NAME " 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";
......@@ -110,8 +119,8 @@ MODULE_PARM_DESC (multicast_filter_limit, "8139cp maximum number of filtered mul
#define TX_TIMEOUT (6*HZ)
/* hardware minimum and maximum for a single frame's data payload */
#define CP_MIN_MTU 60 /* FIXME: this is a guess */
#define CP_MAX_MTU 1500 /* FIXME: this is a guess */
#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */
#define CP_MAX_MTU 4096
enum {
/* NIC register offsets */
......@@ -153,12 +162,17 @@ enum {
IPCS = (1 << 18), /* Calculate IP checksum */
UDPCS = (1 << 17), /* Calculate UDP/IP checksum */
TCPCS = (1 << 16), /* Calculate TCP/IP checksum */
TxVlanTag = (1 << 17), /* Add VLAN tag */
RxVlanTagged = (1 << 16), /* Rx VLAN tag available */
IPFail = (1 << 15), /* IP checksum failed */
UDPFail = (1 << 14), /* UDP/IP checksum failed */
TCPFail = (1 << 13), /* TCP/IP checksum failed */
NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */
PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */
PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */
RxProtoTCP = 2,
RxProtoUDP = 1,
RxProtoIP = 3,
TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */
TxOWC = (1 << 22), /* Tx Out-of-window collision */
TxLinkFail = (1 << 21), /* Link failed during Tx of packet */
......@@ -208,6 +222,7 @@ enum {
TxOn = (1 << 2), /* Tx mode enable */
/* C+ mode command register */
RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */
RxChkSum = (1 << 5), /* Rx checksum offload enable */
PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */
CpRxOn = (1 << 1), /* Rx mode enable */
......@@ -278,6 +293,10 @@ struct cp_private {
unsigned rx_buf_sz;
dma_addr_t ring_dma;
#if CP_VLAN_TAG_USED
struct vlan_group *vlgrp;
#endif
u32 msg_enable;
struct net_device_stats net_stats;
......@@ -335,14 +354,21 @@ static inline void cp_set_rxbufsize (struct cp_private *cp)
cp->rx_buf_sz = PKT_BUF_SZ;
}
static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb)
static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
struct cp_desc *desc)
{
skb->protocol = eth_type_trans (skb, cp->dev);
cp->net_stats.rx_packets++;
cp->net_stats.rx_bytes += skb->len;
cp->dev->last_rx = jiffies;
netif_rx (skb);
#if CP_VLAN_TAG_USED
if (cp->vlgrp && (desc->opts2 & RxVlanTagged)) {
vlan_hwaccel_rx(skb, cp->vlgrp, desc->opts2 & 0xffff);
} else
#endif
netif_rx(skb);
}
static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
......@@ -425,13 +451,26 @@ static void cp_rx_frag (struct cp_private *cp, unsigned rx_tail,
cp_rx_err_acct(cp, rx_tail, status, len);
dev_kfree_skb_irq(copy_skb);
} else
cp_rx_skb(cp, copy_skb);
cp_rx_skb(cp, copy_skb, &cp->rx_ring[rx_tail]);
cp->frag_skb = NULL;
} else {
cp->frag_skb = copy_skb;
}
}
static inline unsigned int cp_rx_csum_ok (u32 status)
{
unsigned int protocol = (status >> 16) & 0x3;
if (likely((protocol == RxProtoTCP) && (!(status & TCPFail))))
return 1;
else if ((protocol == RxProtoUDP) && (!(status & UDPFail)))
return 1;
else if ((protocol == RxProtoIP) && (!(status & IPFail)))
return 1;
return 0;
}
static void cp_rx (struct cp_private *cp)
{
unsigned rx_tail = cp->rx_tail;
......@@ -441,13 +480,15 @@ static void cp_rx (struct cp_private *cp)
u32 status, len;
dma_addr_t mapping;
struct sk_buff *skb, *new_skb;
struct cp_desc *desc;
unsigned buflen;
skb = cp->rx_skb[rx_tail].skb;
if (!skb)
BUG();
rmb();
status = le32_to_cpu(cp->rx_ring[rx_tail].opts1);
desc = &cp->rx_ring[rx_tail];
status = le32_to_cpu(desc->opts1);
if (status & DescOwn)
break;
......@@ -480,7 +521,13 @@ static void cp_rx (struct cp_private *cp)
pci_unmap_single(cp->pdev, mapping,
buflen, PCI_DMA_FROMDEVICE);
skb->ip_summed = CHECKSUM_NONE;
/* Handle checksum offloading for incoming packets. */
if (cp_rx_csum_ok(status))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
skb_put(skb, len);
mapping =
......@@ -489,15 +536,14 @@ static void cp_rx (struct cp_private *cp)
buflen, PCI_DMA_FROMDEVICE);
cp->rx_skb[rx_tail].skb = new_skb;
cp_rx_skb(cp, skb);
cp_rx_skb(cp, skb, desc);
rx_next:
if (rx_tail == (CP_RX_RING_SIZE - 1))
cp->rx_ring[rx_tail].opts1 =
cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz);
desc->opts1 = cpu_to_le32(DescOwn | RingEnd |
cp->rx_buf_sz);
else
cp->rx_ring[rx_tail].opts1 =
cpu_to_le32(DescOwn | cp->rx_buf_sz);
desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz);
cp->rx_ring[rx_tail].opts2 = 0;
cp->rx_ring[rx_tail].addr_lo = cpu_to_le32(mapping);
rx_tail = NEXT_RX(rx_tail);
......@@ -606,6 +652,9 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
struct cp_private *cp = dev->priv;
unsigned entry;
u32 eor;
#if CP_VLAN_TAG_USED
u32 vlan_tag = 0;
#endif
spin_lock_irq(&cp->lock);
......@@ -615,6 +664,11 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
return 1;
}
#if CP_VLAN_TAG_USED
if (cp->vlgrp && vlan_tx_tag_present(skb))
vlan_tag = TxVlanTag | vlan_tx_tag_get(skb);
#endif
entry = cp->tx_head;
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
if (skb_shinfo(skb)->nr_frags == 0) {
......@@ -624,7 +678,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
len = skb->len;
mapping = pci_map_single(cp->pdev, skb->data, len, PCI_DMA_TODEVICE);
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
txd->opts2 = 0;
CP_VLAN_TX_TAG(txd, vlan_tag);
txd->addr_lo = cpu_to_le32(mapping);
wmb();
......@@ -677,7 +731,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
ctrl |= LastFrag;
txd = &cp->tx_ring[entry];
txd->opts2 = 0;
CP_VLAN_TX_TAG(txd, vlan_tag);
txd->addr_lo = cpu_to_le32(mapping);
wmb();
......@@ -691,7 +745,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev)
}
txd = &cp->tx_ring[first_entry];
txd->opts2 = 0;
CP_VLAN_TX_TAG(txd, vlan_tag);
txd->addr_lo = cpu_to_le32(first_mapping);
wmb();
......@@ -826,7 +880,7 @@ static void cp_reset_hw (struct cp_private *cp)
static inline void cp_start_hw (struct cp_private *cp)
{
cpw8(Cmd, RxOn | TxOn);
cpw16(CpCmd, PCIMulRW | CpRxOn | CpTxOn);
cpw16(CpCmd, PCIMulRW | RxChkSum | CpRxOn | CpTxOn);
}
static void cp_init_hw (struct cp_private *cp)
......@@ -1171,7 +1225,30 @@ static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
return rc;
}
#if CP_VLAN_TAG_USED
static int cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
struct cp_private *cp = dev->priv;
spin_lock_irq(&cp->lock);
cp->vlgrp = grp;
cpw16(CpCmd, cpr16(CpCmd) | RxVlanOn);
spin_unlock_irq(&cp->lock);
return 0;
}
static void cp_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct cp_private *cp = dev->priv;
spin_lock_irq(&cp->lock);
cpw16(CpCmd, cpr16(CpCmd) & ~RxVlanOn);
if (cp->vlgrp)
cp->vlgrp->vlan_devices[vid] = NULL;
spin_unlock_irq(&cp->lock);
}
#endif
/* Serial EEPROM section. */
......@@ -1337,6 +1414,11 @@ static int __devinit cp_init_one (struct pci_dev *pdev,
#ifdef CP_TX_CHECKSUM
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
#endif
#if CP_VLAN_TAG_USED
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->vlan_rx_register = cp_vlan_rx_register;
dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid;
#endif
dev->irq = pdev->irq;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment