patch-2.4.20 linux-2.4.20/drivers/net/8139cp.c
Next file: linux-2.4.20/drivers/net/8139too.c
Previous file: linux-2.4.20/drivers/net/7990.c
Back to the patch index
Back to the overall index
- Lines: 1096
- Date:
Thu Nov 28 15:53:13 2002
- Orig file:
linux-2.4.19/drivers/net/8139cp.c
- Orig date:
Fri Aug 2 17:39:44 2002
diff -urN linux-2.4.19/drivers/net/8139cp.c linux-2.4.20/drivers/net/8139cp.c
@@ -18,21 +18,21 @@
See the file COPYING in this distribution for more information.
+ Contributors:
+
+ Wake-on-LAN support - Felipe Damasio <felipewd@terra.com.br>
+ PCI suspend/resume - Felipe Damasio <felipewd@terra.com.br>
+ LinkChg interrupt - Felipe Damasio <felipewd@terra.com.br>
+
TODO, in rough priority order:
+ * Test Tx checksumming thoroughly
* dev->tx_timeout
- * LinkChg interrupt
- * Support forcing media type with a module parameter,
- like dl2k.c/sundance.c
- * Implement PCI suspend/resume
* Constants (module parms?) for Rx work limit
- * support 64-bit PCI DMA
* Complete reset on PciErr
* Consider Rx interrupt mitigation using TimerIntr
* Implement 8139C+ statistics dump; maybe not...
h/w stats can be reset only by software reset
- * 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
* Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error
@@ -41,12 +41,14 @@
Tx descriptor bit
* The real minimum of CP_MIN_MTU is 4 bytes. However,
for this to be supported, one must(?) turn on packet padding.
+ * Support 8169 GMII
+ * Support external MII transceivers
*/
#define DRV_NAME "8139cp"
-#define DRV_VERSION "0.0.7"
-#define DRV_RELDATE "Feb 27, 2002"
+#define DRV_VERSION "0.3.0"
+#define DRV_RELDATE "Sep 29, 2002"
#include <linux/config.h>
@@ -62,9 +64,17 @@
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
#include <asm/io.h>
#include <asm/uaccess.h>
+/* experimental TX checksumming feature enable/disable */
+#undef CP_TX_CHECKSUM
+
+/* VLAN tagging feature enable/disable */
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define CP_VLAN_TAG_USED 1
#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
@@ -77,7 +87,7 @@
/* 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";
+KERN_INFO DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";
MODULE_AUTHOR("Jeff Garzik <jgarzik@mandrakesoft.com>");
MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver");
@@ -85,25 +95,34 @@
static int debug = -1;
MODULE_PARM (debug, "i");
-MODULE_PARM_DESC (debug, "8139cp bitmapped message enable number");
+MODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number");
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
The RTL chips use a 64 element hash table based on the Ethernet CRC. */
static int multicast_filter_limit = 32;
MODULE_PARM (multicast_filter_limit, "i");
-MODULE_PARM_DESC (multicast_filter_limit, "8139cp maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses");
#define PFX DRV_NAME ": "
+#ifndef TRUE
+#define FALSE 0
+#define TRUE (!FALSE)
+#endif
+
#define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
NETIF_MSG_PROBE | \
NETIF_MSG_LINK)
+#define CP_NUM_STATS 14 /* struct cp_dma_stats, plus one */
+#define CP_STATS_SIZE 64 /* size in bytes of DMA stats block */
#define CP_REGS_SIZE (0xff + 1)
+#define CP_REGS_VER 1 /* version 1 */
#define CP_RX_RING_SIZE 64
#define CP_TX_RING_SIZE 64
#define CP_RING_BYTES \
((sizeof(struct cp_desc) * CP_RX_RING_SIZE) + \
- (sizeof(struct cp_desc) * CP_TX_RING_SIZE))
+ (sizeof(struct cp_desc) * CP_TX_RING_SIZE) + \
+ CP_STATS_SIZE)
#define NEXT_TX(N) (((N) + 1) & (CP_TX_RING_SIZE - 1))
#define NEXT_RX(N) (((N) + 1) & (CP_RX_RING_SIZE - 1))
#define TX_BUFFS_AVAIL(CP) \
@@ -132,6 +151,7 @@
/* NIC register offsets */
MAC0 = 0x00, /* Ethernet hardware address. */
MAR0 = 0x08, /* Multicast filter. */
+ StatsAddr = 0x10, /* 64-bit start addr of 64-byte DMA stats blk */
TxRingAddr = 0x20, /* 64-bit start addr of Tx ring */
HiTxRingAddr = 0x28, /* 64-bit start addr of high priority Tx ring */
Cmd = 0x37, /* Command register */
@@ -152,7 +172,9 @@
NWayExpansion = 0x6A, /* MII Expansion */
Config5 = 0xD8, /* Config5 */
TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */
+ RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */
CpCmd = 0xE0, /* C+ Command register (C+ mode only) */
+ IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */
RxRingAddr = 0xE4, /* 64-bit start addr of Rx ring */
TxThresh = 0xEC, /* Early Tx threshold */
OldRxBufAddr = 0x30, /* DMA address of Rx ring buffer (C mode) */
@@ -176,8 +198,8 @@
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,
+ RxProtoTCP = 1,
+ RxProtoUDP = 2,
RxProtoIP = 3,
TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */
TxOWC = (1 << 22), /* Tx Out-of-window collision */
@@ -192,6 +214,9 @@
RxErrLong = (1 << 21), /* Rx error, packet > 4096 bytes */
RxErrFIFO = (1 << 22), /* Rx error, FIFO overflowed, pkt bad */
+ /* StatsAddr register */
+ DumpStats = (1 << 3), /* Begin stats dump */
+
/* RxConfig register */
RxCfgFIFOShift = 13, /* Shift, to get Rx FIFO thresh value */
RxCfgDMAShift = 8, /* Shift, to get Rx Max DMA value */
@@ -230,6 +255,7 @@
/* C+ mode command register */
RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */
RxChkSum = (1 << 5), /* Rx checksum offload enable */
+ PCIDAC = (1 << 4), /* PCI Dual Address Cycle (64-bit PCI) */
PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */
CpRxOn = (1 << 1), /* Rx mode enable */
CpTxOn = (1 << 0), /* Tx mode enable */
@@ -248,12 +274,23 @@
/* Config1 register */
DriverLoaded = (1 << 5), /* Software marker, driver is loaded */
+ LWACT = (1 << 4), /* LWAKE active mode */
PMEnable = (1 << 0), /* Enable various PM features of chip */
/* Config3 register */
PARMEnable = (1 << 6), /* Enable auto-loading of PHY parms */
+ MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */
+ LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */
+
+ /* Config4 register */
+ LWPTN = (1 << 1), /* LWAKE Pattern */
+ LWPME = (1 << 4), /* LANWAKE vs PMEB */
/* Config5 register */
+ BWF = (1 << 6), /* Accept Broadcast wakeup frame */
+ MWF = (1 << 5), /* Accept Multicast wakeup frame */
+ UWF = (1 << 4), /* Accept Unicast wakeup frame */
+ LANWake = (1 << 1), /* Enable LANWake signal */
PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */
};
@@ -269,8 +306,7 @@
struct cp_desc {
u32 opts1;
u32 opts2;
- u32 addr_lo;
- u32 addr_hi;
+ u64 addr;
};
struct ring_info {
@@ -279,6 +315,22 @@
unsigned frag;
};
+struct cp_dma_stats {
+ u64 tx_ok;
+ u64 rx_ok;
+ u64 tx_err;
+ u32 rx_err;
+ u16 rx_fifo;
+ u16 frame_align;
+ u32 tx_ok_1col;
+ u32 tx_ok_mcol;
+ u64 rx_ok_phys;
+ u64 rx_ok_bcast;
+ u32 rx_ok_mcast;
+ u16 tx_abort;
+ u16 tx_underrun;
+} __attribute__((packed));
+
struct cp_extra_stats {
unsigned long rx_frags;
};
@@ -307,12 +359,19 @@
struct net_device_stats net_stats;
struct cp_extra_stats cp_stats;
+ struct cp_dma_stats *nic_stats;
+ dma_addr_t nic_stats_dma;
struct pci_dev *pdev;
u32 rx_config;
struct sk_buff *frag_skb;
unsigned dropping_frag : 1;
+ unsigned pci_using_dac : 1;
+ unsigned int board_type;
+
+ unsigned int wol_enabled : 1; /* Is Wake-on-LAN enabled? */
+ u32 power_state[16];
struct mii_if_info mii_if;
};
@@ -341,14 +400,52 @@
static void cp_tx (struct cp_private *cp);
static void cp_clean_rings (struct cp_private *cp);
+enum board_type {
+ RTL8139Cp,
+ RTL8169,
+};
+
+static struct cp_board_info {
+ const char *name;
+} cp_board_tbl[] __devinitdata = {
+ /* RTL8139Cp */
+ { "RTL-8139C+" },
+
+ /* RTL8169 */
+ { "RTL-8169" },
+};
static struct pci_device_id cp_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139Cp },
+#if 0
+ { PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8169,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8169 },
+#endif
{ },
};
MODULE_DEVICE_TABLE(pci, cp_pci_tbl);
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "tx_ok" },
+ { "rx_ok" },
+ { "tx_err" },
+ { "rx_err" },
+ { "rx_fifo" },
+ { "frame_align" },
+ { "tx_ok_1col" },
+ { "tx_ok_mcol" },
+ { "rx_ok_phys" },
+ { "rx_ok_bcast" },
+ { "rx_ok_mcast" },
+ { "tx_abort" },
+ { "tx_underrun" },
+ { "rx_frags" },
+};
+
+
static inline void cp_set_rxbufsize (struct cp_private *cp)
{
unsigned int mtu = cp->dev->mtu;
@@ -545,13 +642,13 @@
cp_rx_skb(cp, skb, desc);
rx_next:
+ cp->rx_ring[rx_tail].opts2 = 0;
+ cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping);
if (rx_tail == (CP_RX_RING_SIZE - 1))
desc->opts1 = cpu_to_le32(DescOwn | RingEnd |
cp->rx_buf_sz);
else
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);
}
@@ -575,14 +672,16 @@
printk(KERN_DEBUG "%s: intr, status %04x cmd %02x cpcmd %04x\n",
dev->name, status, cpr8(Cmd), cpr16(CpCmd));
+ cpw16_f(IntrStatus, status);
+
spin_lock(&cp->lock);
if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr))
cp_rx(cp);
if (status & (TxOK | TxErr | TxEmpty | SWInt))
cp_tx(cp);
-
- cpw16_f(IntrStatus, status);
+ if (status & LinkChg)
+ mii_check_media(&cp->mii_if, netif_msg_link(cp), FALSE);
if (status & PciErr) {
u16 pci_status;
@@ -682,22 +781,32 @@
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
if (skb_shinfo(skb)->nr_frags == 0) {
struct cp_desc *txd = &cp->tx_ring[entry];
- u32 mapping, len;
+ u32 len;
+ dma_addr_t mapping;
len = skb->len;
mapping = pci_map_single(cp->pdev, skb->data, len, PCI_DMA_TODEVICE);
- eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
CP_VLAN_TX_TAG(txd, vlan_tag);
- txd->addr_lo = cpu_to_le32(mapping);
+ txd->addr = cpu_to_le64(mapping);
wmb();
#ifdef CP_TX_CHECKSUM
- txd->opts1 = cpu_to_le32(eor | len | DescOwn | FirstFrag |
- LastFrag | IPCS | UDPCS | TCPCS);
-#else
- txd->opts1 = cpu_to_le32(eor | len | DescOwn | FirstFrag |
- LastFrag);
+ if (skb->ip_summed == CHECKSUM_HW) {
+ const struct iphdr *ip = skb->nh.iph;
+ if (ip->protocol == IPPROTO_TCP)
+ txd->opts1 = cpu_to_le32(eor | len | DescOwn |
+ FirstFrag | LastFrag |
+ IPCS | TCPCS);
+ else if (ip->protocol == IPPROTO_UDP)
+ txd->opts1 = cpu_to_le32(eor | len | DescOwn |
+ FirstFrag | LastFrag |
+ IPCS | UDPCS);
+ else
+ BUG();
+ } else
#endif
+ txd->opts1 = cpu_to_le32(eor | len | DescOwn |
+ FirstFrag | LastFrag);
wmb();
cp->tx_skb[entry].skb = skb;
@@ -706,12 +815,17 @@
entry = NEXT_TX(entry);
} else {
struct cp_desc *txd;
- u32 first_len, first_mapping;
+ u32 first_len, first_eor;
+ dma_addr_t first_mapping;
int frag, first_entry = entry;
+#ifdef CP_TX_CHECKSUM
+ const struct iphdr *ip = skb->nh.iph;
+#endif
/* We must give this initial chunk to the device last.
* Otherwise we could race with the device.
*/
+ first_eor = eor;
first_len = skb->len - skb->data_len;
first_mapping = pci_map_single(cp->pdev, skb->data,
first_len, PCI_DMA_TODEVICE);
@@ -722,8 +836,9 @@
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
- u32 len, mapping;
+ u32 len;
u32 ctrl;
+ dma_addr_t mapping;
len = this_frag->size;
mapping = pci_map_single(cp->pdev,
@@ -732,16 +847,24 @@
len, PCI_DMA_TODEVICE);
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
#ifdef CP_TX_CHECKSUM
- ctrl = eor | len | DescOwn | IPCS | UDPCS | TCPCS;
-#else
- ctrl = eor | len | DescOwn;
+ if (skb->ip_summed == CHECKSUM_HW) {
+ ctrl = eor | len | DescOwn | IPCS;
+ if (ip->protocol == IPPROTO_TCP)
+ ctrl |= TCPCS;
+ else if (ip->protocol == IPPROTO_UDP)
+ ctrl |= UDPCS;
+ else
+ BUG();
+ } else
#endif
+ ctrl = eor | len | DescOwn;
+
if (frag == skb_shinfo(skb)->nr_frags - 1)
ctrl |= LastFrag;
txd = &cp->tx_ring[entry];
CP_VLAN_TX_TAG(txd, vlan_tag);
- txd->addr_lo = cpu_to_le32(mapping);
+ txd->addr = cpu_to_le64(mapping);
wmb();
txd->opts1 = cpu_to_le32(ctrl);
@@ -755,14 +878,25 @@
txd = &cp->tx_ring[first_entry];
CP_VLAN_TX_TAG(txd, vlan_tag);
- txd->addr_lo = cpu_to_le32(first_mapping);
+ txd->addr = cpu_to_le64(first_mapping);
wmb();
#ifdef CP_TX_CHECKSUM
- txd->opts1 = cpu_to_le32(first_len | FirstFrag | DescOwn | IPCS | UDPCS | TCPCS);
-#else
- txd->opts1 = cpu_to_le32(first_len | FirstFrag | DescOwn);
+ if (skb->ip_summed == CHECKSUM_HW) {
+ if (ip->protocol == IPPROTO_TCP)
+ txd->opts1 = cpu_to_le32(first_eor | first_len |
+ FirstFrag | DescOwn |
+ IPCS | TCPCS);
+ else if (ip->protocol == IPPROTO_UDP)
+ txd->opts1 = cpu_to_le32(first_eor | first_len |
+ FirstFrag | DescOwn |
+ IPCS | UDPCS);
+ else
+ BUG();
+ } else
#endif
+ txd->opts1 = cpu_to_le32(first_eor | first_len |
+ FirstFrag | DescOwn);
wmb();
}
cp->tx_head = entry;
@@ -889,8 +1023,12 @@
static inline void cp_start_hw (struct cp_private *cp)
{
+ u16 pci_dac = cp->pci_using_dac ? PCIDAC : 0;
+ if (cp->board_type == RTL8169)
+ cpw16(CpCmd, pci_dac | PCIMulRW | RxChkSum);
+ else
+ cpw16(CpCmd, pci_dac | PCIMulRW | RxChkSum | CpRxOn | CpTxOn);
cpw8(Cmd, RxOn | TxOn);
- cpw16(CpCmd, PCIMulRW | RxChkSum | CpRxOn | CpTxOn);
}
static void cp_init_hw (struct cp_private *cp)
@@ -912,27 +1050,28 @@
cpw32_f (TxConfig, IFG | (TX_DMA_BURST << TxDMAShift));
cpw8(Config1, cpr8(Config1) | DriverLoaded | PMEnable);
- cpw8(Config3, PARMEnable); /* disables magic packet and WOL */
- cpw8(Config5, cpr8(Config5) & PMEStatus); /* disables more WOL stuff */
+ /* Disable Wake-on-LAN. Can be turned on with ETHTOOL_SWOL */
+ if (cp->board_type == RTL8139Cp) {
+ cpw8(Config3, PARMEnable);
+ cp->wol_enabled = 0;
+ }
+ cpw8(Config5, cpr8(Config5) & PMEStatus);
+ if (cp->board_type == RTL8169)
+ cpw16(RxMaxSize, cp->rx_buf_sz);
cpw32_f(HiTxRingAddr, 0);
cpw32_f(HiTxRingAddr + 4, 0);
- cpw32_f(OldRxBufAddr, 0);
- cpw32_f(OldTSD0, 0);
- cpw32_f(OldTSD0 + 4, 0);
- cpw32_f(OldTSD0 + 8, 0);
- cpw32_f(OldTSD0 + 12, 0);
cpw32_f(RxRingAddr, cp->ring_dma);
- cpw32_f(RxRingAddr + 4, 0);
+ cpw32_f(RxRingAddr + 4, 0); /* FIXME: 64-bit PCI */
cpw32_f(TxRingAddr, cp->ring_dma + (sizeof(struct cp_desc) * CP_RX_RING_SIZE));
- cpw32_f(TxRingAddr + 4, 0);
+ cpw32_f(TxRingAddr + 4, 0); /* FIXME: 64-bit PCI */
cpw16(MultiIntr, 0);
- cpw16(IntrMask, cp_intr_mask);
+ cpw16_f(IntrMask, cp_intr_mask);
- cpw8_f (Cfg9346, Cfg9346_Lock);
+ cpw8_f(Cfg9346, Cfg9346_Lock);
}
static int cp_refill_rx (struct cp_private *cp)
@@ -954,15 +1093,14 @@
cp->rx_skb[i].skb = skb;
cp->rx_skb[i].frag = 0;
+ cp->rx_ring[i].opts2 = 0;
+ cp->rx_ring[i].addr = cpu_to_le64(cp->rx_skb[i].mapping);
if (i == (CP_RX_RING_SIZE - 1))
cp->rx_ring[i].opts1 =
cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz);
else
cp->rx_ring[i].opts1 =
cpu_to_le32(DescOwn | cp->rx_buf_sz);
- cp->rx_ring[i].opts2 = 0;
- cp->rx_ring[i].addr_lo = cpu_to_le32(cp->rx_skb[i].mapping);
- cp->rx_ring[i].addr_hi = 0;
}
return 0;
@@ -985,10 +1123,19 @@
static int cp_alloc_rings (struct cp_private *cp)
{
- cp->rx_ring = pci_alloc_consistent(cp->pdev, CP_RING_BYTES, &cp->ring_dma);
- if (!cp->rx_ring)
+ void *mem;
+
+ mem = pci_alloc_consistent(cp->pdev, CP_RING_BYTES, &cp->ring_dma);
+ if (!mem)
return -ENOMEM;
+
+ cp->rx_ring = mem;
cp->tx_ring = &cp->rx_ring[CP_RX_RING_SIZE];
+
+ mem += (CP_RING_BYTES - CP_STATS_SIZE);
+ cp->nic_stats = mem;
+ cp->nic_stats_dma = cp->ring_dma + (CP_RING_BYTES - CP_STATS_SIZE);
+
return cp_init_rings(cp);
}
@@ -1027,6 +1174,7 @@
pci_free_consistent(cp->pdev, CP_RING_BYTES, cp->rx_ring, cp->ring_dma);
cp->rx_ring = NULL;
cp->tx_ring = NULL;
+ cp->nic_stats = NULL;
}
static int cp_open (struct net_device *dev)
@@ -1047,6 +1195,8 @@
if (rc)
goto err_out_hw;
+ netif_carrier_off(dev);
+ mii_check_media(&cp->mii_if, netif_msg_link(cp), TRUE);
netif_start_queue(dev);
return 0;
@@ -1065,12 +1215,18 @@
printk(KERN_DEBUG "%s: disabling interface\n", dev->name);
netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ spin_lock_irq(&cp->lock);
cp_stop_hw(cp);
+ spin_unlock_irq(&cp->lock);
+
free_irq(dev->irq, dev);
cp_free_rings(cp);
return 0;
}
+#ifdef BROKEN
static int cp_change_mtu(struct net_device *dev, int new_mtu)
{
struct cp_private *cp = dev->priv;
@@ -1094,6 +1250,8 @@
dev->mtu = new_mtu;
cp_set_rxbufsize(cp); /* set new rx buf size */
+ if (cp->board_type == RTL8169)
+ cpw16(RxMaxSize, cp->rx_buf_sz);
rc = cp_init_rings(cp); /* realloc and restart h/w */
cp_start_hw(cp);
@@ -1102,6 +1260,7 @@
return rc;
}
+#endif /* BROKEN */
static char mii_2_8139_map[8] = {
BasicModeCtrl,
@@ -1136,6 +1295,60 @@
cpw16(mii_2_8139_map[location], value);
}
+/* Set the ethtool Wake-on-LAN settings */
+static void netdev_set_wol (struct cp_private *cp,
+ const struct ethtool_wolinfo *wol)
+{
+ u8 options;
+
+ options = cpr8 (Config3) & ~(LinkUp | MagicPacket);
+ /* If WOL is being disabled, no need for complexity */
+ if (wol->wolopts) {
+ if (wol->wolopts & WAKE_PHY) options |= LinkUp;
+ if (wol->wolopts & WAKE_MAGIC) options |= MagicPacket;
+ }
+
+ cpw8 (Cfg9346, Cfg9346_Unlock);
+ cpw8 (Config3, options);
+ cpw8 (Cfg9346, Cfg9346_Lock);
+
+ options = 0; /* Paranoia setting */
+ options = cpr8 (Config5) & ~(UWF | MWF | BWF);
+ /* If WOL is being disabled, no need for complexity */
+ if (wol->wolopts) {
+ if (wol->wolopts & WAKE_UCAST) options |= UWF;
+ if (wol->wolopts & WAKE_BCAST) options |= BWF;
+ if (wol->wolopts & WAKE_MCAST) options |= MWF;
+ }
+
+ cpw8 (Config5, options);
+
+ cp->wol_enabled = (wol->wolopts) ? 1 : 0;
+}
+
+/* Get the ethtool Wake-on-LAN settings */
+static void netdev_get_wol (struct cp_private *cp,
+ struct ethtool_wolinfo *wol)
+{
+ u8 options;
+
+ wol->wolopts = 0; /* Start from scratch */
+ wol->supported = WAKE_PHY | WAKE_BCAST | WAKE_MAGIC |
+ WAKE_MCAST | WAKE_UCAST;
+ /* We don't need to go on if WOL is disabled */
+ if (!cp->wol_enabled) return;
+
+ options = cpr8 (Config3);
+ if (options & LinkUp) wol->wolopts |= WAKE_PHY;
+ if (options & MagicPacket) wol->wolopts |= WAKE_MAGIC;
+
+ options = 0; /* Paranoia setting */
+ options = cpr8 (Config5);
+ if (options & UWF) wol->wolopts |= WAKE_UCAST;
+ if (options & BWF) wol->wolopts |= WAKE_BCAST;
+ if (options & MWF) wol->wolopts |= WAKE_MCAST;
+}
+
static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr)
{
u32 ethcmd;
@@ -1153,6 +1366,8 @@
strcpy (info.driver, DRV_NAME);
strcpy (info.version, DRV_VERSION);
strcpy (info.bus_info, cp->pdev->slot_name);
+ info.regdump_len = CP_REGS_SIZE;
+ info.n_stats = CP_NUM_STATS;
if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT;
return 0;
@@ -1209,6 +1424,226 @@
return 0;
}
+ /* NIC register dump */
+ case ETHTOOL_GREGS: {
+ struct ethtool_regs regs;
+ u8 *regbuf = kmalloc(CP_REGS_SIZE, GFP_KERNEL);
+ int rc;
+
+ if (!regbuf)
+ return -ENOMEM;
+ memset(regbuf, 0, CP_REGS_SIZE);
+
+ rc = copy_from_user(®s, useraddr, sizeof(regs));
+ if (rc) {
+ rc = -EFAULT;
+ goto err_out_gregs;
+ }
+
+ if (regs.len > CP_REGS_SIZE)
+ regs.len = CP_REGS_SIZE;
+ if (regs.len < CP_REGS_SIZE) {
+ rc = -EINVAL;
+ goto err_out_gregs;
+ }
+
+ regs.version = CP_REGS_VER;
+ rc = copy_to_user(useraddr, ®s, sizeof(regs));
+ if (rc) {
+ rc = -EFAULT;
+ goto err_out_gregs;
+ }
+
+ useraddr += offsetof(struct ethtool_regs, data);
+
+ spin_lock_irq(&cp->lock);
+ memcpy_fromio(regbuf, cp->regs, CP_REGS_SIZE);
+ spin_unlock_irq(&cp->lock);
+
+ if (copy_to_user(useraddr, regbuf, regs.len))
+ rc = -EFAULT;
+
+err_out_gregs:
+ kfree(regbuf);
+ return rc;
+ }
+
+ /* get/set RX checksumming */
+ case ETHTOOL_GRXCSUM: {
+ struct ethtool_value edata = { ETHTOOL_GRXCSUM };
+ u16 cmd = cpr16(CpCmd) & RxChkSum;
+
+ edata.data = cmd ? 1 : 0;
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ case ETHTOOL_SRXCSUM: {
+ struct ethtool_value edata;
+ u16 cmd = cpr16(CpCmd), newcmd;
+
+ newcmd = cmd;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ if (edata.data)
+ newcmd |= RxChkSum;
+ else
+ newcmd &= ~RxChkSum;
+
+ if (newcmd == cmd)
+ return 0;
+
+ spin_lock_irq(&cp->lock);
+ cpw16_f(CpCmd, newcmd);
+ spin_unlock_irq(&cp->lock);
+ }
+
+ /* get/set TX checksumming */
+ case ETHTOOL_GTXCSUM: {
+ struct ethtool_value edata = { ETHTOOL_GTXCSUM };
+
+ edata.data = (cp->dev->features & NETIF_F_IP_CSUM) != 0;
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ case ETHTOOL_STXCSUM: {
+ struct ethtool_value edata;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ if (edata.data)
+ cp->dev->features |= NETIF_F_IP_CSUM;
+ else
+ cp->dev->features &= ~NETIF_F_IP_CSUM;
+
+ return 0;
+ }
+
+ /* get/set scatter-gather */
+ case ETHTOOL_GSG: {
+ struct ethtool_value edata = { ETHTOOL_GSG };
+
+ edata.data = (cp->dev->features & NETIF_F_SG) != 0;
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ case ETHTOOL_SSG: {
+ struct ethtool_value edata;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ if (edata.data)
+ cp->dev->features |= NETIF_F_SG;
+ else
+ cp->dev->features &= ~NETIF_F_SG;
+
+ return 0;
+ }
+
+ /* get string list(s) */
+ case ETHTOOL_GSTRINGS: {
+ struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS };
+
+ if (copy_from_user(&estr, useraddr, sizeof(estr)))
+ return -EFAULT;
+ if (estr.string_set != ETH_SS_STATS)
+ return -EINVAL;
+
+ estr.len = CP_NUM_STATS;
+ if (copy_to_user(useraddr, &estr, sizeof(estr)))
+ return -EFAULT;
+ if (copy_to_user(useraddr + sizeof(estr),
+ ðtool_stats_keys,
+ sizeof(ethtool_stats_keys)))
+ return -EFAULT;
+ return 0;
+ }
+
+ /* get NIC-specific statistics */
+ case ETHTOOL_GSTATS: {
+ struct ethtool_stats estats = { ETHTOOL_GSTATS };
+ u64 *tmp_stats;
+ unsigned int work = 100;
+ const unsigned int sz = sizeof(u64) * CP_NUM_STATS;
+ int i;
+
+ /* begin NIC statistics dump */
+ cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */
+ cpw32(StatsAddr, cp->nic_stats_dma | DumpStats);
+ cpr32(StatsAddr);
+
+ estats.n_stats = CP_NUM_STATS;
+ if (copy_to_user(useraddr, &estats, sizeof(estats)))
+ return -EFAULT;
+
+ while (work-- > 0) {
+ if ((cpr32(StatsAddr) & DumpStats) == 0)
+ break;
+ cpu_relax();
+ }
+
+ if (cpr32(StatsAddr) & DumpStats)
+ return -EIO;
+
+ tmp_stats = kmalloc(sz, GFP_KERNEL);
+ if (!tmp_stats)
+ return -ENOMEM;
+ memset(tmp_stats, 0, sz);
+
+ i = 0;
+ tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok);
+ tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok);
+ tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err);
+ tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err);
+ tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo);
+ tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align);
+ tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col);
+ tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol);
+ tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys);
+ tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast);
+ tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast);
+ tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort);
+ tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun);
+ tmp_stats[i++] = cp->cp_stats.rx_frags;
+ if (i != CP_NUM_STATS)
+ BUG();
+
+ i = copy_to_user(useraddr + sizeof(estats),
+ tmp_stats, sz);
+ kfree(tmp_stats);
+
+ if (i)
+ return -EFAULT;
+ return 0;
+ }
+
+ /* get/set Wake-on-LAN settings */
+ case ETHTOOL_GWOL: {
+ struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
+
+ spin_lock_irq (&cp->lock);
+ netdev_get_wol (cp, &wol);
+ spin_unlock_irq (&cp->lock);
+ return ((copy_to_user (useraddr, &wol, sizeof (wol)))? -EFAULT : 0);
+ }
+
+ case ETHTOOL_SWOL: {
+ struct ethtool_wolinfo wol;
+
+ if (copy_from_user (&wol, useraddr, sizeof (wol)))
+ return -EFAULT;
+ spin_lock_irq (&cp->lock);
+ netdev_set_wol (cp, &wol);
+ spin_unlock_irq (&cp->lock);
+ return 0;
+ }
+
default:
break;
}
@@ -1220,20 +1655,18 @@
static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
struct cp_private *cp = dev->priv;
- int rc = 0;
+ struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &rq->ifr_data;
+ int rc;
if (!netif_running(dev))
return -EINVAL;
- switch (cmd) {
- case SIOCETHTOOL:
+ if (cmd == SIOCETHTOOL)
return cp_ethtool_ioctl(cp, (void *) rq->ifr_data);
- default:
- rc = -EOPNOTSUPP;
- break;
- }
-
+ spin_lock_irq(&cp->lock);
+ rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL);
+ spin_unlock_irq(&cp->lock);
return rc;
}
@@ -1321,6 +1754,13 @@
return retval;
}
+/* Put the board into D3cold state and wait for WakeUp signal */
+static void cp_set_d3_state (struct cp_private *cp)
+{
+ pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */
+ pci_set_power_state (cp->pdev, 3);
+}
+
static int __devinit cp_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -1329,9 +1769,10 @@
int rc;
void *regs;
long pciaddr;
- unsigned addr_len, i;
+ unsigned int addr_len, i;
u8 pci_rev, cache_size;
u16 pci_command;
+ unsigned int board_type = (unsigned int) ent->driver_data;
#ifndef MODULE
static int version_printed;
@@ -1355,6 +1796,7 @@
SET_MODULE_OWNER(dev);
cp = dev->priv;
cp->pdev = pdev;
+ cp->board_type = board_type;
cp->dev = dev;
cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug);
spin_lock_init (&cp->lock);
@@ -1362,6 +1804,8 @@
cp->mii_if.mdio_read = mdio_read;
cp->mii_if.mdio_write = mdio_write;
cp->mii_if.phy_id = CP_INTERNAL_PHY;
+ cp->mii_if.phy_id_mask = 0x1f;
+ cp->mii_if.reg_num_mask = 0x1f;
cp_set_rxbufsize(cp);
rc = pci_enable_device(pdev);
@@ -1392,6 +1836,19 @@
goto err_out_res;
}
+ /* Configure DMA attributes. */
+ if (!pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) {
+ cp->pci_using_dac = 1;
+ } else {
+ rc = pci_set_dma_mask(pdev, (u64) 0xffffffff);
+ if (rc) {
+ printk(KERN_ERR PFX "No usable DMA configuration, "
+ "aborting.\n");
+ goto err_out_res;
+ }
+ cp->pci_using_dac = 0;
+ }
+
regs = ioremap_nocache(pciaddr, CP_REGS_SIZE);
if (!regs) {
rc = -EIO;
@@ -1416,7 +1873,9 @@
dev->hard_start_xmit = cp_start_xmit;
dev->get_stats = cp_get_stats;
dev->do_ioctl = cp_ioctl;
+#ifdef BROKEN
dev->change_mtu = cp_change_mtu;
+#endif
#if 0
dev->tx_timeout = cp_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
@@ -1440,7 +1899,7 @@
"%02x:%02x:%02x:%02x:%02x:%02x, "
"IRQ %d\n",
dev->name,
- "RTL-8139C+",
+ cp_board_tbl[board_type].name,
dev->base_addr,
dev->dev_addr[0], dev->dev_addr[1],
dev->dev_addr[2], dev->dev_addr[3],
@@ -1477,6 +1936,8 @@
}
pci_set_master(pdev);
+ if (cp->wol_enabled) cp_set_d3_state (cp);
+
return 0;
err_out_iomap:
@@ -1499,17 +1960,75 @@
BUG();
unregister_netdev(dev);
iounmap(cp->regs);
+ if (cp->wol_enabled) pci_set_power_state (pdev, 0);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
kfree(dev);
}
+#ifdef CONFIG_PM
+static int cp_suspend (struct pci_dev *pdev, u32 state)
+{
+ struct net_device *dev;
+ struct cp_private *cp;
+ unsigned long flags;
+
+ dev = pci_get_drvdata (pdev);
+ cp = dev->priv;
+
+ if (!dev || !netif_running (dev)) return 0;
+
+ netif_device_detach (dev);
+ netif_stop_queue (dev);
+
+ spin_lock_irqsave (&cp->lock, flags);
+
+ /* Disable Rx and Tx */
+ cpw16 (IntrMask, 0);
+ cpw8 (Cmd, cpr8 (Cmd) & (~RxOn | ~TxOn));
+
+ spin_unlock_irqrestore (&cp->lock, flags);
+
+ if (cp->pdev && cp->wol_enabled) {
+ pci_save_state (cp->pdev, cp->power_state);
+ cp_set_d3_state (cp);
+ }
+
+ return 0;
+}
+
+static int cp_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev;
+ struct cp_private *cp;
+
+ dev = pci_get_drvdata (pdev);
+ cp = dev->priv;
+
+ netif_device_attach (dev);
+
+ if (cp->pdev && cp->wol_enabled) {
+ pci_set_power_state (cp->pdev, 0);
+ pci_restore_state (cp->pdev, cp->power_state);
+ }
+
+ cp_init_hw (cp);
+ netif_start_queue (dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
static struct pci_driver cp_driver = {
- name: DRV_NAME,
- id_table: cp_pci_tbl,
- probe: cp_init_one,
- remove: __devexit_p(cp_remove_one),
+ .name = DRV_NAME,
+ .id_table = cp_pci_tbl,
+ .probe = cp_init_one,
+ .remove = __devexit_p(cp_remove_one),
+#ifdef CONFIG_PM
+ .resume = cp_resume,
+ .suspend = cp_suspend,
+#endif
};
static int __init cp_init (void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)