patch-2.4.22 linux-2.4.22/drivers/net/amd8111e.c
Next file: linux-2.4.22/drivers/net/amd8111e.h
Previous file: linux-2.4.22/drivers/net/acenic.c
Back to the patch index
Back to the overall index
- Lines: 1597
- Date:
2003-08-25 04:44:42.000000000 -0700
- Orig file:
linux-2.4.21/drivers/net/amd8111e.c
- Orig date:
2003-06-13 07:51:34.000000000 -0700
diff -urN linux-2.4.21/drivers/net/amd8111e.c linux-2.4.22/drivers/net/amd8111e.c
@@ -1,6 +1,6 @@
/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver
- * Copyright (C) 2002 Advanced Micro Devices
+ * Copyright (C) 2003 Advanced Micro Devices
*
*
* Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ]
@@ -41,6 +41,20 @@
Kernel Mode
Revision History:
+ 3.0.0
+ Initial Revision.
+ 3.0.1
+ 1. Dynamic interrupt coalescing.
+ 2. Removed prev_stats.
+ 3. MII support.
+ 4. Dynamic IPG support
+ 3.0.2 05/29/2003
+ 1. Bug fix: Fixed failure to send jumbo packets larger than 4k.
+ 2. Bug fix: Fixed VLAN support failure.
+ 3. Bug fix: Fixed receive interrupt coalescing bug.
+ 4. Dynamic IPG support is disabled by default.
+ 3.0.3 06/05/2003
+ 1. Bug fix: Fixed failure to close the interface if SMP is enabled.
*/
@@ -77,13 +91,16 @@
#include "amd8111e.h"
#define MODULE_NAME "amd8111e"
-#define MODULE_VERSION "3.0.0"
+#define MODULE_VERSION "3.0.3"
MODULE_AUTHOR("Advanced Micro Devices, Inc.");
-MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version 3.0.0");
+MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version 3.0.3");
MODULE_LICENSE("GPL");
-
MODULE_PARM(speed_duplex, "1-" __MODULE_STRING (MAX_UNITS) "i");
MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotitate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex");
+MODULE_PARM(coalesce, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM_DESC(coalesce, "Enable or Disable interrupt coalescing, 1: Enable, 0: Disable");
+MODULE_PARM(dynamic_ipg, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM_DESC(dynamic_ipg, "Enable or Disable dynamic IPG, 1: Enable, 0: Disable");
static struct pci_device_id amd8111e_pci_tbl[] __devinitdata = {
@@ -92,6 +109,88 @@
{ 0, }
};
+/*
+This function will read the PHY registers.
+*/
+static int amd8111e_read_phy(struct amd8111e_priv* lp, int phy_id, int reg, u32* val)
+{
+ void * mmio = lp->mmio;
+ unsigned int reg_val;
+ unsigned int repeat= REPEAT_CNT;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl( mmio + PHY_ACCESS );
+
+ writel( PHY_RD_CMD | ((phy_id & 0x1f) << 21) |
+ ((reg & 0x1f) << 16), mmio +PHY_ACCESS);
+ do{
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write data */
+ } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+ if(reg_val & PHY_RD_ERR)
+ goto err_phy_read;
+
+ *val = reg_val & 0xffff;
+ return 0;
+err_phy_read:
+ *val = 0;
+ return -EINVAL;
+
+}
+
+/*
+This function will write into PHY registers.
+*/
+static int amd8111e_write_phy(struct amd8111e_priv* lp,int phy_id, int reg, u32 val)
+{
+ unsigned int repeat = REPEAT_CNT
+ void * mmio = lp->mmio;
+ unsigned int reg_val;
+
+ reg_val = readl(mmio + PHY_ACCESS);
+ while (reg_val & PHY_CMD_ACTIVE)
+ reg_val = readl( mmio + PHY_ACCESS );
+
+ writel( PHY_WR_CMD | ((phy_id & 0x1f) << 21) |
+ ((reg & 0x1f) << 16)|val, mmio + PHY_ACCESS);
+
+ do{
+ reg_val = readl(mmio + PHY_ACCESS);
+ udelay(30); /* It takes 30 us to read/write the data */
+ } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+
+ if(reg_val & PHY_RD_ERR)
+ goto err_phy_write;
+
+ return 0;
+
+err_phy_write:
+ return -EINVAL;
+
+}
+/*
+This is the mii register read function provided to the mii interface.
+*/
+static int amd8111e_mdio_read(struct net_device * dev, int phy_id, int reg_num)
+{
+ struct amd8111e_priv* lp = dev->priv;
+ unsigned int reg_val;
+
+ amd8111e_read_phy(lp,phy_id,reg_num,®_val);
+ return reg_val;
+
+}
+
+/*
+This is the mii register write function provided to the mii interface.
+*/
+static void amd8111e_mdio_write(struct net_device * dev, int phy_id, int reg_num, int val)
+{
+ struct amd8111e_priv* lp = dev->priv;
+
+ amd8111e_write_phy(lp, phy_id, reg_num, val);
+}
/*
This function will set PHY speed. During initialization sets the original speed to 100 full.
@@ -99,26 +198,39 @@
static void amd8111e_set_ext_phy(struct net_device *dev)
{
struct amd8111e_priv *lp = (struct amd8111e_priv *)dev->priv;
- unsigned long reg_val = 0;
- void * mmio = lp->mmio;
- struct amd8111e_link_config *link_config = &lp->link_config;
+ u32 bmcr,advert,tmp;
- if(!lp->opened){
- /* Initializing SPEED_100 and DUPLEX_FULL as original values */
- link_config->orig_speed = SPEED_100;
- link_config->orig_duplex = DUPLEX_FULL;
- link_config->orig_phy_option = XPHYSP |XPHYFD;
- }
- reg_val = lp->ext_phy_option;
-
- /* Disable port manager */
- writel((u32) EN_PMGR, mmio + CMD3 );
+ /* Determine mii register values to set the speed */
+ advert = amd8111e_mdio_read(dev, PHY_ID, MII_ADVERTISE);
+ tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ switch (lp->ext_phy_option){
+
+ default:
+ case SPEED_AUTONEG: /* advertise all values */
+ tmp |= ( ADVERTISE_10HALF|ADVERTISE_10FULL|
+ ADVERTISE_100HALF|ADVERTISE_100FULL) ;
+ break;
+ case SPEED10_HALF:
+ tmp |= ADVERTISE_10HALF;
+ break;
+ case SPEED10_FULL:
+ tmp |= ADVERTISE_10FULL;
+ break;
+ case SPEED100_HALF:
+ tmp |= ADVERTISE_100HALF;
+ break;
+ case SPEED100_FULL:
+ tmp |= ADVERTISE_100FULL;
+ break;
+ }
+
+ if(advert != tmp)
+ amd8111e_mdio_write(dev, PHY_ID, MII_ADVERTISE, tmp);
+ /* Restart auto negotiation */
+ bmcr = amd8111e_mdio_read(dev, PHY_ID, MII_BMCR);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ amd8111e_mdio_write(dev, PHY_ID, MII_BMCR, bmcr);
- /* Reset PHY */
- writel((u32)XPHYRST | lp->ext_phy_option, mmio + CTRL2);
-
- /* Enable port manager */
- writel((u32)VAL1 | EN_PMGR, mmio + CMD3 );
}
/*
@@ -156,7 +268,7 @@
}
/*
- This will set the receive buffer length corresponding to the mtu size of network interface.
+This will set the receive buffer length corresponding to the mtu size of networkinterface.
*/
static inline void amd8111e_set_rx_buff_len(struct net_device* dev)
{
@@ -226,13 +338,13 @@
lp->rx_ring[i].buff_phy_addr = cpu_to_le32(lp->rx_dma_addr[i]);
lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len);
- lp->rx_ring[i].rx_dr_offset10 = cpu_to_le16(OWN_BIT);
+ lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT);
}
/* Initializing transmit descriptors */
for (i = 0; i < NUM_TX_RING_DR; i++) {
lp->tx_ring[i].buff_phy_addr = 0;
- lp->tx_ring[i].tx_dr_offset2 = 0;
+ lp->tx_ring[i].tx_flags = 0;
lp->tx_ring[i].buff_count = 0;
}
@@ -253,6 +365,65 @@
err_no_mem:
return -ENOMEM;
}
+/* This function will set the interrupt coalescing according to the input arguments */
+static int amd8111e_set_coalesce(struct net_device * dev, enum coal_mode cmod)
+{
+ unsigned int timeout;
+ unsigned int event_count;
+
+ struct amd8111e_priv *lp = dev->priv;
+ void* mmio = lp->mmio;
+ struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf;
+
+
+ switch(cmod)
+ {
+ case RX_INTR_COAL :
+ timeout = coal_conf->rx_timeout;
+ event_count = coal_conf->rx_event_count;
+ if( timeout > MAX_TIMEOUT ||
+ event_count > MAX_EVENT_COUNT )
+ return -EINVAL;
+
+ timeout = timeout * DELAY_TIMER_CONV;
+ writel(VAL0|STINTEN, mmio+INTEN0);
+ writel((u32)DLY_INT_A_R0|( event_count<< 16 )|timeout,
+ mmio+DLY_INT_A);
+ break;
+
+ case TX_INTR_COAL :
+ timeout = coal_conf->tx_timeout;
+ event_count = coal_conf->tx_event_count;
+ if( timeout > MAX_TIMEOUT ||
+ event_count > MAX_EVENT_COUNT )
+ return -EINVAL;
+
+
+ timeout = timeout * DELAY_TIMER_CONV;
+ writel(VAL0|STINTEN,mmio+INTEN0);
+ writel((u32)DLY_INT_B_T0|( event_count<< 16 )|timeout,
+ mmio+DLY_INT_B);
+ break;
+
+ case DISABLE_COAL:
+ writel(0,mmio+STVAL);
+ writel(STINTEN, mmio+INTEN0);
+ writel(0, mmio +DLY_INT_B);
+ writel(0, mmio+DLY_INT_A);
+ break;
+ case ENABLE_COAL:
+ /* Start the timer */
+ writel((u32)SOFT_TIMER_FREQ, mmio+STVAL); /* 0.5 sec */
+ writel(VAL0|STINTEN, mmio+INTEN0);
+ break;
+ default:
+ break;
+
+ }
+ return 0;
+
+}
+
/*
This function initializes the device registers and starts the device.
*/
@@ -267,13 +438,17 @@
if(amd8111e_init_ring(dev))
return -ENOMEM;
+
+ /* enable the port manager and set auto negotiation always */
+ writel((u32) VAL1|EN_PMGR, mmio + CMD3 );
+ writel((u32)XPHYANE|XPHYRST , mmio + CTRL2);
amd8111e_set_ext_phy(dev);
/* set control registers */
reg_val = readl(mmio + CTRL1);
-
- writel( reg_val| XMTSP_128 | CACHE_ALIGN | B1_MASK, mmio + CTRL1 );
+ reg_val &= ~XMTSP_MASK;
+ writel( reg_val| XMTSP_128 | CACHE_ALIGN, mmio + CTRL1 );
/* enable interrupt */
writel( APINT5EN | APINT4EN | APINT3EN | APINT2EN | APINT1EN |
@@ -288,15 +463,21 @@
writew((u32)NUM_TX_RING_DR, mmio + XMT_RING_LEN0);
writew((u16)NUM_RX_RING_DR, mmio + RCV_RING_LEN0);
+
+ /* set default IPG to 96 */
+ writew((u32)DEFAULT_IPG,mmio+IPG);
+ writew((u32)(DEFAULT_IPG-IFS1_DELTA), mmio + IFS1);
if(lp->options & OPTION_JUMBO_ENABLE){
writel((u32)VAL2|JUMBO, mmio + CMD3);
/* Reset REX_UFLO */
writel( REX_UFLO, mmio + CMD2);
/* Should not set REX_UFLO for jumbo frames */
- writel( VAL0 | APAD_XMT | REX_RTRY, mmio + CMD2);
- }else
+ writel( VAL0 | APAD_XMT|REX_RTRY , mmio + CMD2);
+ }else{
writel( VAL0 | APAD_XMT | REX_RTRY|REX_UFLO, mmio + CMD2);
+ writel((u32)JUMBO, mmio + CMD3);
+ }
#if AMD8111E_VLAN_TAG_USED
writel((u32) VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3);
@@ -306,11 +487,20 @@
/* Setting the MAC address to the device */
for(i = 0; i < ETH_ADDR_LEN; i++)
writeb( dev->dev_addr[i], mmio + PADR + i );
+
+ /* Enable interrupt coalesce */
+ if(lp->options & OPTION_INTR_COAL_ENABLE){
+ printk(KERN_INFO "%s: Interrupt Coalescing Enabled.\n",
+ dev->name);
+ amd8111e_set_coalesce(dev,ENABLE_COAL);
+ }
/* set RUN bit to start the chip */
writel(VAL2 | RDMD0, mmio + CMD0);
writel(VAL0 | INTREN | RUN, mmio + CMD0);
+ /* To avoid PCI posting bug */
+ readl(mmio+CMD0);
return 0;
}
/*
@@ -383,7 +573,7 @@
writew(MIB_CLEAR, mmio + MIB_ADDR);
/* Clear LARF */
- AMD8111E_WRITE_REG64(mmio, LADRF,logic_filter);
+ amd8111e_writeq(*(u64*)logic_filter,mmio+LADRF);
/* SRAM_SIZE register */
reg_val = readl(mmio + SRAM_SIZE);
@@ -393,8 +583,11 @@
#if AMD8111E_VLAN_TAG_USED
writel(VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3 );
#endif
- /* CMD2 register */
- reg_val = readl(mmio + CMD2);
+ /* Set default value to CTRL1 Register */
+ writel(CTRL1_DEFAULT, mmio + CTRL1);
+
+ /* To avoid PCI posting bug */
+ readl(mmio + CMD2);
}
@@ -412,6 +605,9 @@
/* Clear INT0 */
intr0 = readl(lp->mmio + INT0);
writel(intr0, lp->mmio + INT0);
+
+ /* To avoid PCI posting bug */
+ readl(lp->mmio + INT0);
}
@@ -421,6 +617,9 @@
static void amd8111e_stop_chip(struct amd8111e_priv* lp)
{
writel(RUN, lp->mmio + CMD0);
+
+ /* To avoid PCI posting bug */
+ readl(lp->mmio + CMD0);
}
/*
@@ -467,11 +666,10 @@
struct amd8111e_priv* lp = dev->priv;
int tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
int status;
-
/* Complete all the transmit packet */
while (lp->tx_complete_idx != lp->tx_idx){
tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
- status = le16_to_cpu(lp->tx_ring[tx_index].tx_dr_offset2);
+ status = le16_to_cpu(lp->tx_ring[tx_index].tx_flags);
if(status & OWN_BIT)
break; /* It still hasn't been Txed */
@@ -487,11 +685,15 @@
lp->tx_skbuff[tx_index] = 0;
lp->tx_dma_addr[tx_index] = 0;
}
- lp->tx_complete_idx++;
+ lp->tx_complete_idx++;
+ /*COAL update tx coalescing parameters */
+ lp->coal_conf.tx_packets++;
+ lp->coal_conf.tx_bytes += lp->tx_ring[tx_index].buff_count;
if (netif_queue_stopped(dev) &&
lp->tx_complete_idx > lp->tx_idx - NUM_TX_BUFFERS +2){
/* The ring is no longer full, clear tbusy. */
+ /* lp->tx_full = 0; */
netif_wake_queue (dev);
}
}
@@ -516,33 +718,31 @@
/* If we own the next entry, it's a new packet. Send it up. */
while(++num_rx_pkt <= max_rx_pkt){
- if(lp->rx_ring[rx_index].rx_dr_offset10 & OWN_BIT)
+ if(lp->rx_ring[rx_index].rx_flags & OWN_BIT)
return 0;
/* check if err summary bit is set */
- if(le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10) & ERR_BIT){
+ if(le16_to_cpu(lp->rx_ring[rx_index].rx_flags) & ERR_BIT){
/*
* There is a tricky error noted by John Murphy,
* <murf@perftech.com> to Russ Nelson: Even with full-sized
* buffers it's possible for a jabber packet to use two
* buffers, with only the last correctly noting the error. */
/* reseting flags */
- lp->rx_ring[rx_index].rx_dr_offset10 &=
- cpu_to_le16(RESET_RX_FLAGS);
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
goto err_next_pkt;
}
/* check for STP and ENP */
- status = le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10);
+ status = le16_to_cpu(lp->rx_ring[rx_index].rx_flags);
if(!((status & STP_BIT) && (status & ENP_BIT))){
/* reseting flags */
- lp->rx_ring[rx_index].rx_dr_offset10 &=
- cpu_to_le16(RESET_RX_FLAGS);
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
goto err_next_pkt;
}
pkt_len = le16_to_cpu(lp->rx_ring[rx_index].msg_count) - 4;
#if AMD8111E_VLAN_TAG_USED
- vtag = le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10) & TT_MASK;
+ vtag = le16_to_cpu(lp->rx_ring[rx_index].rx_flags) & TT_MASK;
/*MAC will strip vlan tag*/
if(lp->vlgrp != NULL && vtag !=0)
min_pkt_len =MIN_PKT_LEN - 4;
@@ -551,16 +751,14 @@
min_pkt_len =MIN_PKT_LEN;
if (pkt_len < min_pkt_len) {
- lp->rx_ring[rx_index].rx_dr_offset10 &=
- cpu_to_le16(RESET_RX_FLAGS);
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
lp->stats.rx_errors++;
goto err_next_pkt;
}
if(!(new_skb = dev_alloc_skb(lp->rx_buff_len))){
/* if allocation fail,
ignore that pkt and go to next one */
- lp->rx_ring[rx_index].rx_dr_offset10 &=
- cpu_to_le16(RESET_RX_FLAGS);
+ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS;
lp->stats.rx_errors++;
goto err_next_pkt;
}
@@ -580,22 +778,26 @@
#if AMD8111E_VLAN_TAG_USED
- vtag = lp->rx_ring[rx_index].rx_dr_offset10 & TT_MASK;
+ vtag = lp->rx_ring[rx_index].rx_flags & TT_MASK;
if(lp->vlgrp != NULL && (vtag == TT_VLAN_TAGGED)){
amd8111e_vlan_rx(lp, skb,
lp->rx_ring[rx_index].tag_ctrl_info);
} else
#endif
- dev->last_rx = jiffies;
netif_rx (skb);
+ /*COAL update rx coalescing parameters*/
+ lp->coal_conf.rx_packets++;
+ lp->coal_conf.rx_bytes += pkt_len;
+
+ dev->last_rx = jiffies;
err_next_pkt:
lp->rx_ring[rx_index].buff_phy_addr
= cpu_to_le32(lp->rx_dma_addr[rx_index]);
lp->rx_ring[rx_index].buff_count =
cpu_to_le16(lp->rx_buff_len-2);
- lp->rx_ring[rx_index].rx_dr_offset10 |= cpu_to_le16(OWN_BIT);
+ lp->rx_ring[rx_index].rx_flags |= cpu_to_le16(OWN_BIT);
rx_index = (++lp->rx_idx) & RX_RING_DR_MOD_MASK;
}
@@ -603,8 +805,8 @@
}
/*
-This function will store the original speed to restore later, if autoneg is turned on. This speed will be set later when the autoneg is turned off. If the link status indicates that link is down, that will be indicated to the kernel */
-
+This function will indicate the link status to the kernel.
+*/
static int amd8111e_link_change(struct net_device* dev)
{
struct amd8111e_priv *lp = dev->priv;
@@ -614,21 +816,11 @@
status0 = readl(lp->mmio + STAT0);
if(status0 & LINK_STATS){
- if(status0 & AUTONEG_COMPLETE){
- /* keeping the original speeds */
- if((lp->link_config.speed != SPEED_INVALID)&&
- (lp->link_config.duplex != DUPLEX_INVALID)){
- lp->link_config.orig_speed = lp->link_config.speed;
- lp->link_config.orig_duplex = lp->link_config.duplex;
- lp->link_config.orig_phy_option = lp->ext_phy_option;
- }
-
- lp->link_config.speed = SPEED_INVALID;
- lp->link_config.duplex = DUPLEX_INVALID;
+ if(status0 & AUTONEG_COMPLETE)
lp->link_config.autoneg = AUTONEG_ENABLE;
- netif_carrier_on(dev);
- return 0;
- }
+ else
+ lp->link_config.autoneg = AUTONEG_DISABLE;
+
if(status0 & FULL_DPLX)
lp->link_config.duplex = DUPLEX_FULL;
else
@@ -638,13 +830,17 @@
lp->link_config.speed = SPEED_10;
else if(speed == PHY_SPEED_100)
lp->link_config.speed = SPEED_100;
- lp->link_config.autoneg = AUTONEG_DISABLE;
+
+ printk(KERN_INFO "%s: Link is Up. Speed is %s Mbps %s Duplex\n", dev->name,
+ (lp->link_config.speed == SPEED_100) ? "100": "10",
+ (lp->link_config.duplex == DUPLEX_FULL)? "Full": "Half");
netif_carrier_on(dev);
}
else{
lp->link_config.speed = SPEED_INVALID;
lp->link_config.duplex = DUPLEX_INVALID;
lp->link_config.autoneg = AUTONEG_INVALID;
+ printk(KERN_INFO "%s: Link is Down.\n",dev->name);
netif_carrier_off(dev);
}
@@ -671,114 +867,236 @@
}
/*
-This function retuurns the reads the mib registers and returns the hardware statistics. It adds the previous statistics with new values.*/
+This function reads the mib registers and returns the hardware statistics. It updates previous internal driver statistics with new values.
+*/
static struct net_device_stats *amd8111e_get_stats(struct net_device * dev)
{
struct amd8111e_priv *lp = dev->priv;
void * mmio = lp->mmio;
unsigned long flags;
- struct net_device_stats *prev_stats = &lp->prev_stats;
+ /* struct net_device_stats *prev_stats = &lp->prev_stats; */
struct net_device_stats* new_stats = &lp->stats;
if(!lp->opened)
- return prev_stats;
+ return &lp->stats;
spin_lock_irqsave (&lp->lock, flags);
/* stats.rx_packets */
- new_stats->rx_packets = prev_stats->rx_packets+
- amd8111e_read_mib(mmio, rcv_broadcast_pkts)+
- amd8111e_read_mib(mmio, rcv_multicast_pkts)+
- amd8111e_read_mib(mmio, rcv_unicast_pkts);
+ new_stats->rx_packets = amd8111e_read_mib(mmio, rcv_broadcast_pkts)+
+ amd8111e_read_mib(mmio, rcv_multicast_pkts)+
+ amd8111e_read_mib(mmio, rcv_unicast_pkts);
/* stats.tx_packets */
- new_stats->tx_packets = prev_stats->tx_packets+
- amd8111e_read_mib(mmio, xmt_packets);
+ new_stats->tx_packets = amd8111e_read_mib(mmio, xmt_packets);
/*stats.rx_bytes */
- new_stats->rx_bytes = prev_stats->rx_bytes+
- amd8111e_read_mib(mmio, rcv_octets);
+ new_stats->rx_bytes = amd8111e_read_mib(mmio, rcv_octets);
/* stats.tx_bytes */
- new_stats->tx_bytes = prev_stats->tx_bytes+
- amd8111e_read_mib(mmio, xmt_octets);
+ new_stats->tx_bytes = amd8111e_read_mib(mmio, xmt_octets);
/* stats.rx_errors */
- new_stats->rx_errors = prev_stats->rx_errors+
- amd8111e_read_mib(mmio, rcv_undersize_pkts)+
- amd8111e_read_mib(mmio, rcv_fragments)+
- amd8111e_read_mib(mmio, rcv_jabbers)+
- amd8111e_read_mib(mmio, rcv_alignment_errors)+
- amd8111e_read_mib(mmio, rcv_fcs_errors)+
- amd8111e_read_mib(mmio, rcv_miss_pkts);
+ new_stats->rx_errors = amd8111e_read_mib(mmio, rcv_undersize_pkts)+
+ amd8111e_read_mib(mmio, rcv_fragments)+
+ amd8111e_read_mib(mmio, rcv_jabbers)+
+ amd8111e_read_mib(mmio, rcv_alignment_errors)+
+ amd8111e_read_mib(mmio, rcv_fcs_errors)+
+ amd8111e_read_mib(mmio, rcv_miss_pkts);
/* stats.tx_errors */
- new_stats->tx_errors = prev_stats->tx_errors+
- amd8111e_read_mib(mmio, xmt_underrun_pkts);
+ new_stats->tx_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts);
/* stats.rx_dropped*/
- new_stats->rx_dropped = prev_stats->rx_dropped+
- amd8111e_read_mib(mmio, rcv_miss_pkts);
+ new_stats->rx_dropped = amd8111e_read_mib(mmio, rcv_miss_pkts);
/* stats.tx_dropped*/
- new_stats->tx_dropped = prev_stats->tx_dropped+
- amd8111e_read_mib(mmio, xmt_underrun_pkts);
+ new_stats->tx_dropped = amd8111e_read_mib(mmio, xmt_underrun_pkts);
/* stats.multicast*/
- new_stats->multicast = prev_stats->multicast+
- amd8111e_read_mib(mmio, rcv_multicast_pkts);
+ new_stats->multicast = amd8111e_read_mib(mmio, rcv_multicast_pkts);
/* stats.collisions*/
- new_stats->collisions = prev_stats->collisions+
- amd8111e_read_mib(mmio, xmt_collisions);
+ new_stats->collisions = amd8111e_read_mib(mmio, xmt_collisions);
/* stats.rx_length_errors*/
- new_stats->rx_length_errors = prev_stats->rx_length_errors+
+ new_stats->rx_length_errors =
amd8111e_read_mib(mmio, rcv_undersize_pkts)+
amd8111e_read_mib(mmio, rcv_oversize_pkts);
/* stats.rx_over_errors*/
- new_stats->rx_over_errors = prev_stats->rx_over_errors+
- amd8111e_read_mib(mmio, rcv_miss_pkts);
+ new_stats->rx_over_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
/* stats.rx_crc_errors*/
- new_stats->rx_crc_errors = prev_stats->rx_crc_errors+
- amd8111e_read_mib(mmio, rcv_fcs_errors);
+ new_stats->rx_crc_errors = amd8111e_read_mib(mmio, rcv_fcs_errors);
/* stats.rx_frame_errors*/
- new_stats->rx_frame_errors = prev_stats->rx_frame_errors+
+ new_stats->rx_frame_errors =
amd8111e_read_mib(mmio, rcv_alignment_errors);
/* stats.rx_fifo_errors */
- new_stats->rx_fifo_errors = prev_stats->rx_fifo_errors+
- amd8111e_read_mib(mmio, rcv_miss_pkts);
+ new_stats->rx_fifo_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
/* stats.rx_missed_errors */
- new_stats->rx_missed_errors = prev_stats->rx_missed_errors+
- amd8111e_read_mib(mmio, rcv_miss_pkts);
+ new_stats->rx_missed_errors = amd8111e_read_mib(mmio, rcv_miss_pkts);
/* stats.tx_aborted_errors*/
- new_stats->tx_aborted_errors = prev_stats->tx_aborted_errors+
+ new_stats->tx_aborted_errors =
amd8111e_read_mib(mmio, xmt_excessive_collision);
/* stats.tx_carrier_errors*/
- new_stats->tx_carrier_errors = prev_stats->tx_carrier_errors+
+ new_stats->tx_carrier_errors =
amd8111e_read_mib(mmio, xmt_loss_carrier);
/* stats.tx_fifo_errors*/
- new_stats->tx_fifo_errors = prev_stats->tx_fifo_errors+
- amd8111e_read_mib(mmio, xmt_underrun_pkts);
+ new_stats->tx_fifo_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts);
/* stats.tx_window_errors*/
- new_stats->tx_window_errors = prev_stats->tx_window_errors+
+ new_stats->tx_window_errors =
amd8111e_read_mib(mmio, xmt_late_collision);
+ /* Reset the mibs for collecting new statistics */
+ /* writew(MIB_CLEAR, mmio + MIB_ADDR);*/
+
spin_unlock_irqrestore (&lp->lock, flags);
return new_stats;
}
+/* This function recalculate the interupt coalescing mode on every interrupt
+according to the datarate and the packet rate.
+*/
+static int amd8111e_calc_coalesce(struct net_device *dev)
+{
+ struct amd8111e_priv *lp = dev->priv;
+ struct amd8111e_coalesce_conf * coal_conf = &lp->coal_conf;
+ int tx_pkt_rate;
+ int rx_pkt_rate;
+ int tx_data_rate;
+ int rx_data_rate;
+ int rx_pkt_size;
+ int tx_pkt_size;
+
+ tx_pkt_rate = coal_conf->tx_packets - coal_conf->tx_prev_packets;
+ coal_conf->tx_prev_packets = coal_conf->tx_packets;
+
+ tx_data_rate = coal_conf->tx_bytes - coal_conf->tx_prev_bytes;
+ coal_conf->tx_prev_bytes = coal_conf->tx_bytes;
+
+ rx_pkt_rate = coal_conf->rx_packets - coal_conf->rx_prev_packets;
+ coal_conf->rx_prev_packets = coal_conf->rx_packets;
+
+ rx_data_rate = coal_conf->rx_bytes - coal_conf->rx_prev_bytes;
+ coal_conf->rx_prev_bytes = coal_conf->rx_bytes;
+
+ if(rx_pkt_rate < 800){
+ if(coal_conf->rx_coal_type != NO_COALESCE){
+
+ coal_conf->rx_timeout = 0x0;
+ coal_conf->rx_event_count = 0;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = NO_COALESCE;
+ }
+ }
+ else{
+
+ rx_pkt_size = rx_data_rate/rx_pkt_rate;
+ if (rx_pkt_size < 128){
+ if(coal_conf->rx_coal_type != NO_COALESCE){
+
+ coal_conf->rx_timeout = 0;
+ coal_conf->rx_event_count = 0;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = NO_COALESCE;
+ }
+ }
+ else if ( (rx_pkt_size >= 128) && (rx_pkt_size < 512) ){
+
+ if(coal_conf->rx_coal_type != LOW_COALESCE){
+ coal_conf->rx_timeout = 1;
+ coal_conf->rx_event_count = 4;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = LOW_COALESCE;
+ }
+ }
+ else if ((rx_pkt_size >= 512) && (rx_pkt_size < 1024)){
+
+ if(coal_conf->rx_coal_type != MEDIUM_COALESCE){
+ coal_conf->rx_timeout = 1;
+ coal_conf->rx_event_count = 4;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = MEDIUM_COALESCE;
+ }
+
+ }
+ else if(rx_pkt_size >= 1024){
+ if(coal_conf->rx_coal_type != HIGH_COALESCE){
+ coal_conf->rx_timeout = 2;
+ coal_conf->rx_event_count = 3;
+ amd8111e_set_coalesce(dev,RX_INTR_COAL);
+ coal_conf->rx_coal_type = HIGH_COALESCE;
+ }
+ }
+ }
+ /* NOW FOR TX INTR COALESC */
+ if(tx_pkt_rate < 800){
+ if(coal_conf->tx_coal_type != NO_COALESCE){
+
+ coal_conf->tx_timeout = 0x0;
+ coal_conf->tx_event_count = 0;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = NO_COALESCE;
+ }
+ }
+ else{
+
+ tx_pkt_size = tx_data_rate/tx_pkt_rate;
+ if (tx_pkt_size < 128){
+
+ if(coal_conf->tx_coal_type != NO_COALESCE){
+
+ coal_conf->tx_timeout = 0;
+ coal_conf->tx_event_count = 0;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = NO_COALESCE;
+ }
+
+ }
+ else if ( (tx_pkt_size >= 128) && (tx_pkt_size < 512) ){
+
+ if(coal_conf->tx_coal_type != LOW_COALESCE){
+ coal_conf->tx_timeout = 1;
+ coal_conf->tx_event_count = 2;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = LOW_COALESCE;
+
+ }
+ }
+ else if ((tx_pkt_size >= 512) && (tx_pkt_size < 1024)){
+
+ if(coal_conf->tx_coal_type != MEDIUM_COALESCE){
+ coal_conf->tx_timeout = 2;
+ coal_conf->tx_event_count = 5;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = MEDIUM_COALESCE;
+ }
+
+ }
+ else if(tx_pkt_size >= 1024){
+ if (tx_pkt_size >= 1024){
+ if(coal_conf->tx_coal_type != HIGH_COALESCE){
+ coal_conf->tx_timeout = 4;
+ coal_conf->tx_event_count = 8;
+ amd8111e_set_coalesce(dev,TX_INTR_COAL);
+ coal_conf->tx_coal_type = HIGH_COALESCE;
+ }
+ }
+ }
+ }
+ return 0;
+
+}
/*
-This is device interrupt function. It handles transmit, receive and link change interrupts.
+This is device interrupt function. It handles transmit, receive,link change and hardware timer interrupts.
*/
static void amd8111e_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
@@ -791,7 +1109,7 @@
if(dev == NULL)
return;
- spin_lock (&lp->lock);
+ if (regs) spin_lock (&lp->lock);
/* disabling interrupt */
writel(INTREN, mmio + CMD0);
@@ -819,15 +1137,20 @@
/* Check if Link Change Interrupt has occurred. */
if (intr0 & LCINT)
amd8111e_link_change(dev);
-
+
+ /* Check if Hardware Timer Interrupt has occurred. */
+ if (intr0 & STINT)
+ amd8111e_calc_coalesce(dev);
+
err_no_interrupt:
writel( VAL0 | INTREN,mmio + CMD0);
- spin_unlock(&lp->lock);
- return;
-
+
+ if (regs) spin_unlock(&lp->lock);
+
}
+
/*
-This function closes the network interface and copies the new set of statistics into the previous statistics structure so that most recent statistics will be available after the interface is down.
+This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down.
*/
static int amd8111e_close(struct net_device * dev)
{
@@ -842,10 +1165,15 @@
netif_carrier_off(lp->amd8111e_net_dev);
- spin_unlock_irq(&lp->lock);
+ /* Delete ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ del_timer_sync(&lp->ipg_data.ipg_timer);
+ spin_unlock_irq(&lp->lock);
free_irq(dev->irq, dev);
- memcpy(&lp->prev_stats,amd8111e_get_stats(dev), sizeof(lp->prev_stats));
+
+ /* Update the statistics before closing */
+ amd8111e_get_stats(dev);
lp->opened = 0;
return 0;
}
@@ -867,7 +1195,12 @@
spin_unlock_irq(&lp->lock);
return -ENOMEM;
}
-
+ /* Start ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE){
+ add_timer(&lp->ipg_data.ipg_timer);
+ printk(KERN_INFO "%s: Dynamic IPG Enabled.\n",dev->name);
+ }
+
lp->opened = 1;
spin_unlock_irq(&lp->lock);
@@ -905,11 +1238,10 @@
lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len);
lp->tx_skbuff[tx_index] = skb;
- lp->tx_ring[tx_index].tx_dr_offset2 = 0;
+ lp->tx_ring[tx_index].tx_flags = 0;
#if AMD8111E_VLAN_TAG_USED
if((lp->vlgrp != NULL) && vlan_tx_tag_present(skb)){
-
lp->tx_ring[tx_index].tag_ctrl_cmd |=
cpu_to_le32(TCC_VLAN_INSERT);
lp->tx_ring[tx_index].tag_ctrl_info =
@@ -923,7 +1255,7 @@
(u32) cpu_to_le32(lp->tx_dma_addr[tx_index]);
/* Set FCS and LTINT bits */
- lp->tx_ring[tx_index].tx_dr_offset2 |=
+ lp->tx_ring[tx_index].tx_flags |=
cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT);
lp->tx_idx++;
@@ -946,16 +1278,54 @@
static char* amd8111e_read_regs(struct amd8111e_priv* lp)
{
void * mmio = lp->mmio;
- unsigned char * reg_buff;
+ u32 * reg_buff;
- int i;
-
reg_buff = kmalloc( AMD8111E_REG_DUMP_LEN,GFP_KERNEL);
if(NULL == reg_buff)
return NULL;
- for (i=0; i < AMD8111E_REG_DUMP_LEN; i+=4)
- reg_buff[i]= readl(mmio + i);
- return reg_buff;
+
+ /* Read only necessary registers */
+ reg_buff[0] = readl(mmio + XMT_RING_BASE_ADDR0);
+ reg_buff[1] = readl(mmio + XMT_RING_LEN0);
+ reg_buff[2] = readl(mmio + RCV_RING_BASE_ADDR0);
+ reg_buff[3] = readl(mmio + RCV_RING_LEN0);
+ reg_buff[4] = readl(mmio + CMD0);
+ reg_buff[5] = readl(mmio + CMD2);
+ reg_buff[6] = readl(mmio + CMD3);
+ reg_buff[7] = readl(mmio + CMD7);
+ reg_buff[8] = readl(mmio + INT0);
+ reg_buff[9] = readl(mmio + INTEN0);
+ reg_buff[10] = readl(mmio + LADRF);
+ reg_buff[11] = readl(mmio + LADRF+4);
+ reg_buff[12] = readl(mmio + STAT0);
+
+ return (char *)reg_buff;
+}
+/*
+amd8111e crc generator implementation is different from the kernel
+ether_crc() function.
+*/
+int amd8111e_ether_crc(int len, char* mac_addr)
+{
+ int i,byte;
+ unsigned char octet;
+ u32 crc= INITCRC;
+
+ for(byte=0; byte < len; byte++){
+ octet = mac_addr[byte];
+ for( i=0;i < 8; i++){
+ /*If the next bit form the input stream is 1,subtract the divisor (CRC32) from the dividend(crc).*/
+ if( (octet & 0x1) ^ (crc & 0x1) ){
+ crc >>= 1;
+ crc ^= CRC32;
+ }
+ else
+ crc >>= 1;
+
+ octet >>= 1;
+ }
+ }
+ return crc;
}
/*
This function sets promiscuos mode, all-multi mode or the multicast address
@@ -967,9 +1337,8 @@
struct amd8111e_priv *lp = dev->priv;
u32 mc_filter[2] ;
int i,bit_num;
-
if(dev->flags & IFF_PROMISC){
- printk("%s: Setting promiscuous mode.\n",dev->name);
+ printk(KERN_INFO "%s: Setting promiscuous mode.\n",dev->name);
writel( VAL2 | PROM, lp->mmio + CMD2);
return;
}
@@ -980,7 +1349,7 @@
mc_filter[1] = mc_filter[0] = 0xffffffff;
lp->mc_list = dev->mc_list;
lp->options |= OPTION_MULTICAST_ENABLE;
- AMD8111E_WRITE_REG64(lp->mmio, LADRF,mc_filter);
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
return;
}
if( dev->mc_count == 0 ){
@@ -988,7 +1357,7 @@
mc_filter[1] = mc_filter[0] = 0;
lp->mc_list = 0;
lp->options &= ~OPTION_MULTICAST_ENABLE;
- AMD8111E_WRITE_REG64(lp->mmio, LADRF,mc_filter);
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
/* disable promiscous mode */
writel(PROM, lp->mmio + CMD2);
return;
@@ -999,14 +1368,16 @@
mc_filter[1] = mc_filter[0] = 0;
for (i = 0, mc_ptr = dev->mc_list; mc_ptr && i < dev->mc_count;
i++, mc_ptr = mc_ptr->next) {
- bit_num = ether_crc(ETH_ALEN, mc_ptr->dmi_addr) >> 26;
-
+ bit_num = ( amd8111e_ether_crc(ETH_ALEN,mc_ptr->dmi_addr) >> 26 ) & 0x3f;
mc_filter[bit_num >> 5] |= 1 << (bit_num & 31);
}
+ amd8111e_writeq(*(u64*)mc_filter,lp->mmio+ LADRF);
+
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD2);
- AMD8111E_WRITE_REG64(lp->mmio, LADRF, mc_filter);
- return;
}
+
/*
This function handles all the ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application.
*/
@@ -1029,6 +1400,7 @@
strcpy (info.driver, MODULE_NAME);
strcpy (info.version, MODULE_VERSION);
memset(&info.fw_version, 0, sizeof(info.fw_version));
+ sprintf(info.fw_version,"%u",chip_version);
strcpy (info.bus_info, pci_dev->slot_name);
info.eedump_len = 0;
info.regdump_len = AMD8111E_REG_DUMP_LEN;
@@ -1036,85 +1408,27 @@
return -EFAULT;
return 0;
}
- case ETHTOOL_GSET:{
- struct ethtool_cmd cmd = { ETHTOOL_GSET };
-
- if (!lp->opened)
- return -EAGAIN;
-
- cmd.supported = SUPPORTED_Autoneg |
- SUPPORTED_100baseT_Half |
- SUPPORTED_100baseT_Full |
- SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full |
- SUPPORTED_MII;
-
- cmd.advertising = ADVERTISED_Autoneg |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_MII;
- cmd.speed = lp->link_config.speed;
- cmd.duplex = lp->link_config.duplex;
- cmd.port = 0;
- cmd.phy_address = PHY_ID;
- cmd.transceiver = XCVR_EXTERNAL;
- cmd.autoneg = lp->link_config.autoneg;
- cmd.maxtxpkt = 0; /* not implemented interrupt coalasing */
- cmd.maxrxpkt = 0; /* not implemented interrupt coalasing */
- if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
+ /* get settings */
+ case ETHTOOL_GSET: {
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ spin_lock_irq(&lp->lock);
+ mii_ethtool_gset(&lp->mii_if, &ecmd);
+ spin_unlock_irq(&lp->lock);
+ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
- }
+ }
+ /* set settings */
case ETHTOOL_SSET: {
-
- struct ethtool_cmd cmd;
-
- if (!lp->opened)
- return -EAGAIN;
- if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+ int r;
+ struct ethtool_cmd ecmd;
+ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
return -EFAULT;
spin_lock_irq(&lp->lock);
-
- if(cmd.autoneg == AUTONEG_ENABLE){
- /* keeping the original speeds */
- if((lp->link_config.speed != SPEED_INVALID)&&
- (lp->link_config.duplex != DUPLEX_INVALID)){
- lp->link_config.orig_speed = lp->link_config.speed;
- lp->link_config.orig_duplex = lp->link_config.duplex;
- lp->link_config.orig_phy_option = lp->ext_phy_option;
- }
-
- lp->ext_phy_option = XPHYANE;
- }
- else if(cmd.speed == SPEED_100 && cmd.duplex == DUPLEX_HALF)
- lp->ext_phy_option = XPHYSP;
- else if(cmd.speed == SPEED_100 && cmd.duplex == DUPLEX_FULL)
- lp->ext_phy_option = XPHYSP |XPHYFD;
- else if(cmd.speed == SPEED_10 && cmd.duplex == DUPLEX_HALF)
- lp->ext_phy_option = 0;
- else if(cmd.speed == SPEED_10 && cmd.duplex == DUPLEX_FULL)
- lp->ext_phy_option = XPHYFD;
- else {
- /* setting the original speed */
- cmd.speed = lp->link_config.orig_speed;
- cmd.duplex = lp->link_config.orig_duplex;
- lp->ext_phy_option = lp->link_config.orig_phy_option;
- }
- lp->link_config.autoneg = cmd.autoneg;
- if (cmd.autoneg == AUTONEG_ENABLE) {
-
- lp->link_config.speed = SPEED_INVALID;
- lp->link_config.duplex = DUPLEX_INVALID;
- } else {
- lp->link_config.speed = cmd.speed;
- lp->link_config.duplex = cmd.duplex;
- }
- amd8111e_set_ext_phy(dev);
+ r = mii_ethtool_sset(&lp->mii_if, &ecmd);
spin_unlock_irq(&lp->lock);
- return 0;
+ return r;
}
case ETHTOOL_GREGS: {
struct ethtool_regs regs;
@@ -1140,24 +1454,17 @@
kfree(regbuf);
return ret;
}
+ /* restart autonegotiation */
case ETHTOOL_NWAY_RST: {
- int ret;
- spin_lock_irq(&lp->lock);
- if(lp->link_config.autoneg == AUTONEG_ENABLE){
- lp->ext_phy_option = XPHYANE;
- amd8111e_set_ext_phy(dev);
- ret = 0;
- }else
- ret = -EINVAL;
- spin_unlock_irq(&lp->lock);
- return ret;
+ return mii_nway_restart(&lp->mii_if);
}
+ /* get link status */
case ETHTOOL_GLINK: {
- struct ethtool_value val = { ETHTOOL_GLINK };
-
- val.data = netif_carrier_ok(dev) ? 1 : 0;
+ struct ethtool_value val = {ETHTOOL_GLINK};
+ val.data = mii_link_ok(&lp->mii_if);
if (copy_to_user(useraddr, &val, sizeof(val)))
return -EFAULT;
+ return 0;
}
case ETHTOOL_GWOL: {
struct ethtool_wolinfo wol_info = { ETHTOOL_GWOL };
@@ -1196,60 +1503,6 @@
}
return -EOPNOTSUPP;
}
-static int amd8111e_read_phy(struct amd8111e_priv* lp, int phy_id, int reg, u32* val)
-{
- void * mmio = lp->mmio;
- unsigned int reg_val;
- unsigned int repeat= REPEAT_CNT;
-
- reg_val = readl(mmio + PHY_ACCESS);
- while (reg_val & PHY_CMD_ACTIVE)
- reg_val = readl( mmio + PHY_ACCESS );
-
- writel( PHY_RD_CMD | ((phy_id & 0x1f) << 21) |
- ((reg & 0x1f) << 16), mmio +PHY_ACCESS);
- do{
- reg_val = readl(mmio + PHY_ACCESS);
- udelay(30); /* It takes 30 us to read/write data */
- } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
- if(reg_val & PHY_RD_ERR)
- goto err_phy_read;
-
- *val = reg_val & 0xffff;
- return 0;
-err_phy_read:
- *val = 0;
- return -EINVAL;
-
-}
-static int amd8111e_write_phy(struct amd8111e_priv* lp,int phy_id, int reg, u32 val)
-{
- unsigned int repeat = REPEAT_CNT
- void * mmio = lp->mmio;
- unsigned int reg_val;
-
-
- reg_val = readl(mmio + PHY_ACCESS);
- while (reg_val & PHY_CMD_ACTIVE)
- reg_val = readl( mmio + PHY_ACCESS );
-
- writel( PHY_WR_CMD | ((phy_id & 0x1f) << 21) |
- ((reg & 0x1f) << 16)|val, mmio + PHY_ACCESS);
-
- do{
- reg_val = readl(mmio + PHY_ACCESS);
- udelay(30); /* It takes 30 us to read/write the data */
- } while (--repeat && (reg_val & PHY_CMD_ACTIVE));
-
- if(reg_val & PHY_RD_ERR)
- goto err_phy_write;
-
- return 0;
-
-err_phy_write:
- return -EINVAL;
-
-}
static int amd8111e_ioctl(struct net_device * dev , struct ifreq *ifr, int cmd)
{
struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
@@ -1317,15 +1570,10 @@
dev->mtu = new_mtu;
- /* if (new_mtu > ETH_DATA_LEN)
- lp->options |= OPTION_JUMBO_ENABLE;
- else
- lp->options &= ~OPTION_JUMBO_ENABLE;
- */
err = amd8111e_restart(dev);
spin_unlock_irq(&lp->lock);
-
- netif_start_queue(dev);
+ if(!err)
+ netif_start_queue(dev);
return err;
}
@@ -1351,73 +1599,41 @@
{
writel( VAL1|MPPLBA, lp->mmio + CMD3);
writel( VAL0|MPEN_SW, lp->mmio + CMD7);
+
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD7);
return 0;
}
static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
{
+
/* Adapter is already stoped/suspended/interrupt-disabled */
writel(VAL0|LCMODE_SW,lp->mmio + CMD7);
- return 0;
-}
-
-/*
-This function sets the power state of the device. When the device go to lower power states 1,2, and 3 it enables the wake on lan
-*/
-static int amd8111e_set_power_state(struct amd8111e_priv* lp, u32 state)
-{
- u16 power_control;
- int pm = lp->pm_cap;
-
- pci_read_config_word(lp->pci_dev,
- pm + PCI_PM_CTRL,
- &power_control);
-
- power_control |= PCI_PM_CTRL_PME_STATUS;
- power_control &= ~(PCI_PM_CTRL_STATE_MASK);
- switch (state) {
- case 0:
- power_control |= 0;
- pci_write_config_word(lp->pci_dev,
- pm + PCI_PM_CTRL,
- power_control);
- return 0;
-
- case 1:
- power_control |= 1;
- break;
-
- case 2:
- power_control |= 2;
- break;
-
- case 3:
- power_control |= 3;
- break;
- default:
-
- printk(KERN_WARNING "%s: Invalid power state (%d) requested.\n",
- lp->amd8111e_net_dev->name, state);
- return -EINVAL;
- }
- if(lp->options & OPTION_WAKE_MAGIC_ENABLE)
- amd8111e_enable_magicpkt(lp);
- if(lp->options & OPTION_WAKE_PHY_ENABLE)
- amd8111e_enable_link_change(lp);
-
- /* Setting new power state. */
- pci_write_config_word(lp->pci_dev, pm + PCI_PM_CTRL, power_control);
-
+ /* To eliminate PCI posting bug */
+ readl(lp->mmio + CMD7);
return 0;
+}
+/* This function is called when a packet transmission fails to complete within a resonable period, on the assumption that an interrupts have been failed or the interface is locked up. This function will reinitialize the hardware */
+static void amd8111e_tx_timeout(struct net_device *dev)
+{
+ struct amd8111e_priv* lp = dev->priv;
+ int err;
+ printk(KERN_ERR "%s: transmit timed out, resetting\n",
+ dev->name);
+ spin_lock_irq(&lp->lock);
+ err = amd8111e_restart(dev);
+ spin_unlock_irq(&lp->lock);
+ if(!err)
+ netif_wake_queue(dev);
}
static int amd8111e_suspend(struct pci_dev *pci_dev, u32 state)
{
struct net_device *dev = pci_get_drvdata(pci_dev);
struct amd8111e_priv *lp = dev->priv;
- int err;
if (!netif_running(dev))
return 0;
@@ -1431,37 +1647,54 @@
/* stop chip */
spin_lock_irq(&lp->lock);
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ del_timer_sync(&lp->ipg_data.ipg_timer);
amd8111e_stop_chip(lp);
spin_unlock_irq(&lp->lock);
- err = amd8111e_set_power_state(lp, state);
- if (err) {
+ if(lp->options & OPTION_WOL_ENABLE){
+ /* enable wol */
+ if(lp->options & OPTION_WAKE_MAGIC_ENABLE)
+ amd8111e_enable_magicpkt(lp);
+ if(lp->options & OPTION_WAKE_PHY_ENABLE)
+ amd8111e_enable_link_change(lp);
- spin_lock_irq(&lp->lock);
- amd8111e_restart(dev);
- spin_unlock_irq(&lp->lock);
+ pci_enable_wake(pci_dev, 3, 1);
+ pci_enable_wake(pci_dev, 4, 1); /* D3 cold */
- netif_device_attach(dev);
}
- return err;
+ else{
+ pci_enable_wake(pci_dev, 3, 0);
+ pci_enable_wake(pci_dev, 4, 0); /* 4 == D3 cold */
+ }
+
+ pci_save_state(pci_dev, lp->pm_state);
+ pci_set_power_state(pci_dev, 3);
+
+ return 0;
}
static int amd8111e_resume(struct pci_dev *pci_dev)
{
struct net_device *dev = pci_get_drvdata(pci_dev);
struct amd8111e_priv *lp = dev->priv;
- int err;
if (!netif_running(dev))
return 0;
- err = amd8111e_set_power_state(lp, 0);
- if (err)
- return err;
+ pci_set_power_state(pci_dev, 0);
+ pci_restore_state(pci_dev, lp->pm_state);
+
+ pci_enable_wake(pci_dev, 3, 0);
+ pci_enable_wake(pci_dev, 4, 0); /* D3 cold */
netif_device_attach(dev);
spin_lock_irq(&lp->lock);
amd8111e_restart(dev);
+ /* Restart ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE)
+ mod_timer(&lp->ipg_data.ipg_timer,
+ jiffies + (IPG_CONVERGE_TIME * HZ));
spin_unlock_irq(&lp->lock);
return 0;
@@ -1480,6 +1713,65 @@
pci_set_drvdata(pdev, NULL);
}
}
+static void amd8111e_config_ipg(struct net_device* dev)
+{
+ struct amd8111e_priv *lp = dev->priv;
+ struct ipg_info* ipg_data = &lp->ipg_data;
+ void * mmio = lp->mmio;
+ unsigned int prev_col_cnt = ipg_data->col_cnt;
+ unsigned int total_col_cnt;
+ unsigned int tmp_ipg;
+
+ if(lp->link_config.duplex == DUPLEX_FULL){
+ ipg_data->ipg = DEFAULT_IPG;
+ return;
+ }
+
+ if(ipg_data->ipg_state == SSTATE){
+
+ if(ipg_data->timer_tick == IPG_STABLE_TIME){
+
+ ipg_data->timer_tick = 0;
+ ipg_data->ipg = MIN_IPG - IPG_STEP;
+ ipg_data->current_ipg = MIN_IPG;
+ ipg_data->diff_col_cnt = 0xFFFFFFFF;
+ ipg_data->ipg_state = CSTATE;
+ }
+ else
+ ipg_data->timer_tick++;
+ }
+
+ if(ipg_data->ipg_state == CSTATE){
+
+ /* Get the current collision count */
+
+ total_col_cnt = ipg_data->col_cnt =
+ amd8111e_read_mib(mmio, xmt_collisions);
+
+ if ((total_col_cnt - prev_col_cnt) <
+ (ipg_data->diff_col_cnt)){
+
+ ipg_data->diff_col_cnt =
+ total_col_cnt - prev_col_cnt ;
+
+ ipg_data->ipg = ipg_data->current_ipg;
+ }
+
+ ipg_data->current_ipg += IPG_STEP;
+
+ if (ipg_data->current_ipg <= MAX_IPG)
+ tmp_ipg = ipg_data->current_ipg;
+ else{
+ tmp_ipg = ipg_data->ipg;
+ ipg_data->ipg_state = SSTATE;
+ }
+ writew((u32)tmp_ipg, mmio + IPG);
+ writew((u32)(tmp_ipg - IFS1_DELTA), mmio + IFS1);
+ }
+ mod_timer(&lp->ipg_data.ipg_timer, jiffies + (IPG_CONVERGE_TIME * HZ));
+ return;
+
+}
static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -1488,7 +1780,6 @@
unsigned long reg_addr,reg_len;
struct amd8111e_priv* lp;
struct net_device* dev;
- unsigned int chip_version;
err = pci_enable_device(pdev);
if(err){
@@ -1547,11 +1838,16 @@
#endif
lp = dev->priv;
- memset (lp, 0, sizeof (*lp));
lp->pci_dev = pdev;
lp->amd8111e_net_dev = dev;
lp->pm_cap = pm_cap;
+ /* setting mii default values */
+ lp->mii_if.dev = dev;
+ lp->mii_if.mdio_read = amd8111e_mdio_read;
+ lp->mii_if.mdio_write = amd8111e_mdio_write;
+ lp->mii_if.phy_id = PHY_ID;
+
spin_lock_init(&lp->lock);
lp->mmio = ioremap(reg_addr, reg_len);
@@ -1565,12 +1861,14 @@
/* Initializing MAC address */
for(i = 0; i < ETH_ADDR_LEN; i++)
dev->dev_addr[i] =readb(lp->mmio + PADR + i);
- /* Setting user defined speed */
- if (speed_duplex[card_idx] > sizeof(speed_duplex_mapping))
- lp->ext_phy_option = XPHYANE;
- else
- lp->ext_phy_option =
- speed_duplex_mapping[speed_duplex[card_idx]];
+
+ /* Setting user defined parametrs */
+ lp->ext_phy_option = speed_duplex[card_idx];
+ if(coalesce[card_idx])
+ lp->options |= OPTION_INTR_COAL_ENABLE;
+ if(dynamic_ipg[card_idx++])
+ lp->options |= OPTION_DYN_IPG_ENABLE;
+
/* Initialize driver entry points */
dev->open = amd8111e_open;
dev->hard_start_xmit = amd8111e_start_xmit;
@@ -1580,6 +1878,8 @@
dev->do_ioctl = amd8111e_ioctl;
dev->change_mtu = amd8111e_change_mtu;
dev->irq =pdev->irq;
+ dev->tx_timeout = amd8111e_tx_timeout;
+ dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
#if AMD8111E_VLAN_TAG_USED
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
@@ -1589,10 +1889,6 @@
/* Set receive buffer length and set jumbo option*/
amd8111e_set_rx_buff_len(dev);
-
-
- /* dev->tx_timeout = tg3_tx_timeout; */
- /* dev->watchdog_timeo = TG3_TX_TIMEOUT; */
err = register_netdev(dev);
if (err) {
@@ -1603,15 +1899,26 @@
pci_set_drvdata(pdev, dev);
+ /* Initialize software ipg timer */
+ if(lp->options & OPTION_DYN_IPG_ENABLE){
+ init_timer(&lp->ipg_data.ipg_timer);
+ lp->ipg_data.ipg_timer.data = (unsigned long) dev;
+ lp->ipg_data.ipg_timer.function = (void *)&amd8111e_config_ipg;
+ lp->ipg_data.ipg_timer.expires = jiffies +
+ IPG_CONVERGE_TIME * HZ;
+ lp->ipg_data.ipg = DEFAULT_IPG;
+ lp->ipg_data.ipg_state = CSTATE;
+ };
+
/* display driver and device information */
- chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
- printk("%s: AMD-8111e Driver Version: %s\n",dev->name,MODULE_VERSION);
- printk("%s: [ Rev %x ] PCI 10/100BaseT Ethernet ", dev->name, chip_version);
- for (i = 0; i < 6; i++)
- printk("%2.2x%c", dev->dev_addr[i],i == 5 ? ' ' : ':');
- printk("\n");
- return 0;
+ chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
+ printk(KERN_INFO "%s: AMD-8111e Driver Version: %s\n", dev->name,MODULE_VERSION);
+ printk(KERN_INFO "%s: [ Rev %x ] PCI 10/100BaseT Ethernet ", dev->name, chip_version);
+ for (i = 0; i < 6; i++)
+ printk("%2.2x%c",dev->dev_addr[i],i == 5 ? ' ' : ':');
+ printk( "\n");
+ return 0;
err_iounmap:
iounmap((void *) lp->mmio);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)