Add IP1000A Driver
Subsystems (2)
PATCH 1/1 Add IP1000A Driver
From: Jesse Huang <[email protected]>
Change Logs: Add IP1000A Driver to kernel tree.
Signed-off-by Jesse Huang <[email protected]>
---
drivers/net/ipg.c | 2331 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/ipg.h | 856 +++++++++++++++++++ 2 files changed, 3187 insertions(+), 0 deletions(-) create mode 100755 drivers/net/ipg.c create mode 100755 drivers/net/ipg.h
e804d1c265bf1d843f845457f925a1728bbfdff7
drivers/net/ipg.c new file +2331 -0
@@ -0,0 +1,2331 @@ +/* +* ipg.c: Device Driver for the IP1000 Gigabit Ethernet Adapter +* +* Copyright (C) 2003, 2006 IC Plus Corp. +* +* Original Author: +* +* Craig Rich +* Sundance Technology, Inc. +* 1485 Saratoga Avenue +* Suite 200 +* San Jose, CA 95129 +* 408 873 4117 +* www.sundanceti.com +* [email protected] +* +* Current Maintainer: +* +* Sorbica Shieh. +* 10F, No.47, Lane 2, Kwang-Fu RD. +* Sec. 2, Hsin-Chu, Taiwan, R.O.C. +* http://www.icplus.com.tw +* [email protected] +*/ +#include <linux/crc32.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/mutex.h> + +#define IPG_RX_RING_BYTES (sizeof(struct ipg_rx) * IPG_RFDLIST_LENGTH) +#define IPG_TX_RING_BYTES (sizeof(struct ipg_tx) * IPG_TFDLIST_LENGTH) +#define IPG_RESET_MASK \ + (IPG_AC_GLOBAL_RESET | IPG_AC_RX_RESET | IPG_AC_TX_RESET | \ + IPG_AC_DMA | IPG_AC_FIFO | IPG_AC_NETWORK | IPG_AC_HOST | \ + IPG_AC_AUTO_INIT) + +#define ipg_w32 (val32,reg) iowrite32((val32), ioaddr + (reg)) +#define ipg_w16 (val16,reg) iowrite16((val16), ioaddr + (reg)) +#define ipg_w8 (val8,reg) iowrite8((val8), ioaddr + (reg)) + +#define ipg_r32 (reg) ioread32(ioaddr + (reg)) +#define ipg_r16 (reg) ioread16(ioaddr + (reg)) +#define ipg_r8 (reg) ioread8(ioaddr + (reg)) + +#define JUMBO_FRAME_4k_ONLY +enum { + netdev_io_size =128 +}; + +#include "ipg.h" +#define DRV_NAME "ipg" + +MODULE_AUTHOR ("IC Plus Corp. 2003" ); +MODULE_DESCRIPTION ("IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver " + DrvVer); +MODULE_LICENSE ("GPL" ); + +static const char *ipg_brand_name[] = { +"IC PLUS IP1000 1000/100/10 based NIC" , +"Sundance Technology ST2021 based NIC" , +"Tamarack Microelectronics TC9020/9021 based NIC" , +"Tamarack Microelectronics TC9020/9021 based NIC" , +"D-Link NIC" , +"D-Link NIC IP1000A" +}; + +static struct pci_device_id ipg_pci_tbl[]__devinitdata = { + {PCI_DEVICE (PCI_VENDOR_ID_SUNDANCE ,0x1023 ),0 ,0 ,0 }, + {PCI_DEVICE (PCI_VENDOR_ID_SUNDANCE ,0x2021 ),0 ,0 ,1 }, + {PCI_DEVICE (PCI_VENDOR_ID_SUNDANCE ,0x1021 ),0 ,0 ,2 }, + {PCI_DEVICE (PCI_VENDOR_ID_DLINK ,0x9021 ),0 ,0 ,3 }, + {PCI_DEVICE (PCI_VENDOR_ID_DLINK ,0x4000 ),0 ,0 ,4 }, + {PCI_DEVICE (PCI_VENDOR_ID_DLINK ,0x4020 ),0 ,0 ,5 }, + {0 , } +}; + +MODULE_DEVICE_TABLE (pci, ipg_pci_tbl); + +static inline void __iomem *ipg_ioaddr (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +return sp->ioaddr; +} + +#ifdef IPG_DEBUG +static void ipg_dump_rfdlist (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; +u32 offset; + +IPG_DEBUG_MSG ("_dump_rfdlist\n" ); + +printk (KERN_INFO "rx_current = %2.2x\n" , sp->rx_current); +printk (KERN_INFO "rx_dirty = %2.2x\n" , sp->rx_dirty); +printk (KERN_INFO "RFDList start address = %16.16lx\n" , + (unsigned long ) sp->rxd_map); +printk (KERN_INFO "RFDListPtr register = %8.8x%8.8x\n" , +ipg_r32 (IPG_RFDLISTPTR1 ),ipg_r32 (IPG_RFDLISTPTR0 )); + +for (i =0 ; i <IPG_RFDLIST_LENGTH ; i++) { + offset = (u32 ) &sp->rxd[i].next_desc - (u32 ) sp->rxd; +printk (KERN_INFO "%2.2x %4.4x RFDNextPtr = %16.16lx\n" , i, + offset, (unsigned long ) sp->rxd[i].next_desc); + offset = (u32 ) &sp->rxd[i].rfs - (u32 ) sp->rxd; +printk (KERN_INFO "%2.2x %4.4x RFS = %16.16lx\n" , i, + offset, (unsigned long ) sp->rxd[i].rfs); + offset = (u32 ) &sp->rxd[i].frag_info - (u32 ) sp->rxd; +printk (KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n" , i, + offset, (unsigned long ) sp->rxd[i].frag_info); + } +} + +static void ipg_dump_tfdlist (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; +u32 offset; + +IPG_DEBUG_MSG ("_dump_tfdlist\n" ); + +printk (KERN_INFO "tx_current = %2.2x\n" , sp->tx_current); +printk (KERN_INFO "tx_dirty = %2.2x\n" , sp->tx_dirty); +printk (KERN_INFO "TFDList start address = %16.16lx\n" , + (unsigned long ) sp->txd_map); +printk (KERN_INFO "TFDListPtr register = %8.8x%8.8x\n" , +ipg_r32 (IPG_TFDLISTPTR1 ),ipg_r32 (IPG_TFDLISTPTR0 )); + +for (i =0 ; i <IPG_TFDLIST_LENGTH ; i++) { + offset = (u32 ) &sp->txd[i].next_desc - (u32 ) sp->txd; +printk (KERN_INFO "%2.2x %4.4x TFDNextPtr = %16.16lx\n" , i, + offset, (unsigned long ) sp->txd[i].next_desc); + + offset = (u32 ) &sp->txd[i].tfc - (u32 ) sp->txd; +printk (KERN_INFO "%2.2x %4.4x TFC = %16.16lx\n" , i, + offset, (unsigned long ) sp->txd[i].tfc); + offset = (u32 ) &sp->txd[i].frag_info - (u32 ) sp->txd; +printk (KERN_INFO "%2.2x %4.4x frag_info = %16.16lx\n" , i, + offset, (unsigned long ) sp->txd[i].frag_info); + } +} +#endif + +static void ipg_write_phy_ctl (void __iomem *ioaddr,u8 data) +{ +ipg_w8 (IPG_PC_RSVD_MASK & data,PHY_CTRL ); +ndelay (IPG_PC_PHYCTRLWAIT_NS ); +} + +static void ipg_drive_phy_ctl_low_high (void __iomem *ioaddr,u8 data) +{ +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_LO | data); +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_HI | data); +} + +static void send_three_state (void __iomem *ioaddr,u8 phyctrlpolarity) +{ + phyctrlpolarity |= (IPG_PC_MGMTDATA &0 ) |IPG_PC_MGMTDIR ; + +ipg_drive_phy_ctl_low_high (ioaddr, phyctrlpolarity); +} + +static void send_end (void __iomem *ioaddr,u8 phyctrlpolarity) +{ +ipg_w8 ((IPG_PC_MGMTCLK_LO | (IPG_PC_MGMTDATA &0 ) |IPG_PC_MGMTDIR | + phyctrlpolarity) &IPG_PC_RSVD_MASK ,PHY_CTRL ); +} + +static u16 read_phy_bit (void __iomem * ioaddr,u8 phyctrlpolarity) +{ +u16 bit_data; + +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_LO | phyctrlpolarity); + + bit_data = ((ipg_r8 (PHY_CTRL ) &IPG_PC_MGMTDATA ) >>1 ) &1 ; + +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_HI | phyctrlpolarity); + +return bit_data; +} + +/* +* Read a register from the Physical Layer device located +* on the IPG NIC, using the IPG PHYCTRL register. +*/ +static int mdio_read (struct net_device * dev,int phy_id,int phy_reg) +{ +void __iomem *ioaddr =ipg_ioaddr (dev); +/* +* The GMII mangement frame structure for a read is as follows: +* +* |Preamble|st|op|phyad|regad|ta| data |idle| +* |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z | +* +* <32 1s> = 32 consecutive logic 1 values +* A = bit of Physical Layer device address (MSB first) +* R = bit of register address (MSB first) +* z = High impedance state +* D = bit of read data (MSB first) +* +* Transmission order is 'Preamble' field first, bits transmitted +* left to right (first to last). +*/ +struct { +u32 field; +unsigned int len; + } p[] = { + {GMII_PREAMBLE ,32 },/* Preamble */ + {GMII_ST ,2 },/* ST */ + {GMII_READ ,2 },/* OP */ + { phy_id,5 },/* PHYAD */ + { phy_reg,5 },/* REGAD */ + {0x0000 ,2 },/* TA */ + {0x0000 ,16 },/* DATA */ + {0x0000 ,1 }/* IDLE */ + }; +unsigned int i, j; +u8 polarity, data; + + polarity =ipg_r8 (PHY_CTRL ); + polarity &= (IPG_PC_DUPLEX_POLARITY |IPG_PC_LINK_POLARITY ); + +/* Create the Preamble, ST, OP, PHYAD, and REGAD field. */ +for (j =0 ; j <5 ; j++) { +for (i =0 ; i < p[j].len; i++) { +/* For each variable length field, the MSB must be +* transmitted first. Rotate through the field bits, +* starting with the MSB, and move each bit into the +* the 1st (2^1) bit position (this is the bit position +* corresponding to the MgmtData bit of the PhyCtrl +* register for the IPG). +* +* Example: ST = 01; +* +* First write a '0' to bit 1 of the PhyCtrl +* register, then write a '1' to bit 1 of the +* PhyCtrl register. +* +* To do this, right shift the MSB of ST by the value: +* [field length - 1 - #ST bits already written] +* then left shift this result by 1. +*/ + data = (p[j].field >> (p[j].len -1 - i)) <<1 ; + data &=IPG_PC_MGMTDATA ; + data |= polarity |IPG_PC_MGMTDIR ; + +ipg_drive_phy_ctl_low_high (ioaddr, data); + } + } + +send_three_state (ioaddr, polarity); + +read_phy_bit (ioaddr, polarity); + +/* +* For a read cycle, the bits for the next two fields (TA and +* DATA) are driven by the PHY (the IPG reads these bits). +*/ +for (i =0 ; i < p[6 ].len; i++) { + p[6 ].field |= + (read_phy_bit (ioaddr, polarity) << (p[6 ].len -1 - i)); + } + +send_three_state (ioaddr, polarity); +send_three_state (ioaddr, polarity); +send_three_state (ioaddr, polarity); +send_end (ioaddr, polarity); + +/* Return the value of the DATA field. */ +return p[6 ].field; +} + +/* +* Write to a register from the Physical Layer device located +* on the IPG NIC, using the IPG PHYCTRL register. +*/ +static void mdio_write (struct net_device *dev,int phy_id,int phy_reg,int val) +{ +void __iomem *ioaddr =ipg_ioaddr (dev); +/* +* The GMII mangement frame structure for a read is as follows: +* +* |Preamble|st|op|phyad|regad|ta| data |idle| +* |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z | +* +* <32 1s> = 32 consecutive logic 1 values +* A = bit of Physical Layer device address (MSB first) +* R = bit of register address (MSB first) +* z = High impedance state +* D = bit of write data (MSB first) +* +* Transmission order is 'Preamble' field first, bits transmitted +* left to right (first to last). +*/ +struct { +u32 field; +unsigned int len; + } p[] = { + {GMII_PREAMBLE ,32 },/* Preamble */ + {GMII_ST ,2 },/* ST */ + {GMII_WRITE ,2 },/* OP */ + { phy_id,5 },/* PHYAD */ + { phy_reg,5 },/* REGAD */ + {0x0002 ,2 },/* TA */ + { val &0xffff ,16 },/* DATA */ + {0x0000 ,1 }/* IDLE */ + }; +unsigned int i, j; +u8 polarity, data; + + polarity =ipg_r8 (PHY_CTRL ); + polarity &= (IPG_PC_DUPLEX_POLARITY |IPG_PC_LINK_POLARITY ); + +/* Create the Preamble, ST, OP, PHYAD, and REGAD field. */ +for (j =0 ; j <7 ; j++) { +for (i =0 ; i < p[j].len; i++) { +/* For each variable length field, the MSB must be +* transmitted first. Rotate through the field bits, +* starting with the MSB, and move each bit into the +* the 1st (2^1) bit position (this is the bit position +* corresponding to the MgmtData bit of the PhyCtrl +* register for the IPG). +* +* Example: ST = 01; +* +* First write a '0' to bit 1 of the PhyCtrl +* register, then write a '1' to bit 1 of the +* PhyCtrl register. +* +* To do this, right shift the MSB of ST by the value: +* [field length - 1 - #ST bits already written] +* then left shift this result by 1. +*/ + data = (p[j].field >> (p[j].len -1 - i)) <<1 ; + data &=IPG_PC_MGMTDATA ; + data |= polarity |IPG_PC_MGMTDIR ; + +ipg_drive_phy_ctl_low_high (ioaddr, data); + } + } + +/* The last cycle is a tri-state, so read from the PHY. */ +for (j =7 ; j <8 ; j++) { +for (i =0 ; i < p[j].len; i++) { +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_LO | polarity); + + p[j].field |= ((ipg_r8 (PHY_CTRL ) & +IPG_PC_MGMTDATA ) >>1 ) << (p[j].len -1 - i); + +ipg_write_phy_ctl (ioaddr,IPG_PC_MGMTCLK_HI | polarity); + } + } +} + +/* Set LED_Mode JES20040127EEPROM */ +static void ipg_set_led_mode (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +u32 mode; + + mode =ipg_r32 (ASIC_CTRL ); + mode &= ~(IPG_AC_LED_MODE_BIT_1 |IPG_AC_LED_MODE |IPG_AC_LED_SPEED ); + +if ((sp->LED_Mode &0x03 ) >1 ) + mode |=IPG_AC_LED_MODE_BIT_1 ;/* Write Asic Control Bit 29 */ + +if ((sp->LED_Mode &0x01 ) ==1 ) + mode |=IPG_AC_LED_MODE ;/* Write Asic Control Bit 14 */ + +if ((sp->LED_Mode &0x08 ) ==8 ) + mode |=IPG_AC_LED_SPEED ;/* Write Asic Control Bit 27 */ + +ipg_w32 (mode,ASIC_CTRL ); +} + +/* Set PHYSet JES20040127EEPROM */ +static void ipg_set_phy_set (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +int physet; + + physet =ipg_r8 (PHY_SET ); + physet &= ~(IPG_PS_MEM_LENB9B |IPG_PS_MEM_LEN9 |IPG_PS_NON_COMPDET ); + physet |= ((sp->LED_Mode &0x70 ) >>4 ); +ipg_w8 (physet,PHY_SET ); +} + +static int ipg_reset (struct net_device *dev,u32 resetflags) +{ +/* Assert functional resets via the IPG AsicCtrl +* register as specified by the 'resetflags' input +* parameter. +*/ +void __iomem *ioaddr =ipg_ioaddr (dev);//JES20040127EEPROM: +unsigned int timeout_count =0 ; + +IPG_DEBUG_MSG ("_reset\n" ); + +ipg_w32 (ipg_r32 (ASIC_CTRL ) | resetflags,ASIC_CTRL ); + +/* Delay added to account for problem with 10Mbps reset. */ +mdelay (IPG_AC_RESETWAIT ); + +while (IPG_AC_RESET_BUSY &ipg_r32 (ASIC_CTRL )) { +mdelay (IPG_AC_RESETWAIT ); +if (++timeout_count >IPG_AC_RESET_TIMEOUT ) +return -ETIME ; + } +/* Set LED Mode in Asic Control JES20040127EEPROM */ +ipg_set_led_mode (dev); + +/* Set PHYSet Register Value JES20040127EEPROM */ +ipg_set_phy_set (dev); +return 0 ; +} + +/* Find the GMII PHY address. */ +static int ipg_find_phyaddr (struct net_device *dev) +{ +unsigned int phyaddr, i; + +for (i =0 ; i <32 ; i++) { +u32 status; + +/* Search for the correct PHY address among 32 possible. */ + phyaddr = (IPG_NIC_PHY_ADDRESS + i) %32 ; + +/* 10/22/03 Grace change verify from GMII_PHY_STATUS to +GMII_PHY_ID1 +*/ + + status =mdio_read (dev, phyaddr,MII_BMSR ); + +if ((status !=0xFFFF ) && (status !=0 )) +return phyaddr; + } + +return 0x1f ; +} + +/* +* Configure IPG based on result of IEEE 802.3 PHY +* auto-negotiation. +*/ +static int ipg_config_autoneg (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int txflowcontrol; +unsigned int rxflowcontrol; +unsigned int fullduplex; +unsigned int gig; +u32 mac_ctrl_val; +u32 asicctrl; +u8 phyctrl; + +IPG_DEBUG_MSG ("_config_autoneg\n" ); + + asicctrl =ipg_r32 (ASIC_CTRL ); + phyctrl =ipg_r8 (PHY_CTRL ); + mac_ctrl_val =ipg_r32 (MAC_CTRL ); + +/* Set flags for use in resolving auto-negotation, assuming +* non-1000Mbps, half duplex, no flow control. +*/ + fullduplex =0 ; + txflowcontrol =0 ; + rxflowcontrol =0 ; + gig =0 ; + +/* To accomodate a problem in 10Mbps operation, +* set a global flag if PHY running in 10Mbps mode. +*/ + sp->tenmbpsmode =0 ; + +printk (KERN_INFO "%s: Link speed = " , dev->name); + +/* Determine actual speed of operation. */ +switch (phyctrl &IPG_PC_LINK_SPEED ) { +case IPG_PC_LINK_SPEED_10MBPS : +printk ("10Mbps.\n" ); +printk (KERN_INFO "%s: 10Mbps operational mode enabled.\n" , + dev->name); + sp->tenmbpsmode =1 ; +break ; +case IPG_PC_LINK_SPEED_100MBPS : +printk ("100Mbps.\n" ); +break ; +case IPG_PC_LINK_SPEED_1000MBPS : +printk ("1000Mbps.\n" ); + gig =1 ; +break ; +default : +printk ("undefined!\n" ); +return 0 ; + } + +if (phyctrl &IPG_PC_DUPLEX_STATUS ) { + fullduplex =1 ; + txflowcontrol =1 ; + rxflowcontrol =1 ; + } + +/* Configure full duplex, and flow control. */ +if (fullduplex ==1 ) { +/* Configure IPG for full duplex operation. */ +printk (KERN_INFO "%s: setting full duplex, " , dev->name); + + mac_ctrl_val |=IPG_MC_DUPLEX_SELECT_FD ; + +if (txflowcontrol ==1 ) { +printk ("TX flow control" ); + mac_ctrl_val |=IPG_MC_TX_FLOW_CONTROL_ENABLE ; + }else { +printk ("no TX flow control" ); + mac_ctrl_val &= ~IPG_MC_TX_FLOW_CONTROL_ENABLE ; + } + +if (rxflowcontrol ==1 ) { +printk (", RX flow control." ); + mac_ctrl_val |=IPG_MC_RX_FLOW_CONTROL_ENABLE ; + }else { +printk (", no RX flow control." ); + mac_ctrl_val &= ~IPG_MC_RX_FLOW_CONTROL_ENABLE ; + } + +printk ("\n" ); + }else { +/* Configure IPG for half duplex operation. */ +printk (KERN_INFO "%s: setting half duplex, " +"no TX flow control, no RX flow control.\n" , dev->name); + + mac_ctrl_val &= ~IPG_MC_DUPLEX_SELECT_FD & + ~IPG_MC_TX_FLOW_CONTROL_ENABLE & + ~IPG_MC_RX_FLOW_CONTROL_ENABLE ; + } +ipg_w32 (mac_ctrl_val,MAC_CTRL ); +return 0 ; +} + +/* Determine and configure multicast operation and set +* receive mode for IPG. +*/ +static void ipg_nic_set_multicast_list (struct net_device *dev) +{ +void __iomem *ioaddr =ipg_ioaddr (dev); +struct dev_mc_list *mc_list_ptr; +unsigned int hashindex; +u32 hashtable[2 ]; +u8 receivemode; + +IPG_DEBUG_MSG ("_nic_set_multicast_list\n" ); + + receivemode =IPG_RM_RECEIVEUNICAST |IPG_RM_RECEIVEBROADCAST ; + +if (dev->flags &IFF_PROMISC ) { +/* NIC to be configured in promiscuous mode. */ + receivemode =IPG_RM_RECEIVEALLFRAMES ; + }else if ((dev->flags &IFF_ALLMULTI ) || + (dev->flags &IFF_MULTICAST & + (dev->mc_count >IPG_MULTICAST_HASHTABLE_SIZE ))) { +/* NIC to be configured to receive all multicast +* frames. */ + receivemode |=IPG_RM_RECEIVEMULTICAST ; + }else if (dev->flags &IFF_MULTICAST & (dev->mc_count >0 )) { +/* NIC to be configured to receive selected +* multicast addresses. */ + receivemode |=IPG_RM_RECEIVEMULTICASTHASH ; + } + +/* Calculate the bits to set for the 64 bit, IPG HASHTABLE. +* The IPG applies a cyclic-redundancy-check (the same CRC +* used to calculate the frame data FCS) to the destination +* address all incoming multicast frames whose destination +* address has the multicast bit set. The least significant +* 6 bits of the CRC result are used as an addressing index +* into the hash table. If the value of the bit addressed by +* this index is a 1, the frame is passed to the host system. +*/ + +/* Clear hashtable. */ + hashtable[0 ] =0x00000000 ; + hashtable[1 ] =0x00000000 ; + +/* Cycle through all multicast addresses to filter. */ +for (mc_list_ptr = dev->mc_list; + mc_list_ptr !=NULL ; mc_list_ptr = mc_list_ptr->next) { +/* Calculate CRC result for each multicast address. */ + hashindex =crc32_le (0xffffffff , mc_list_ptr->dmi_addr, +ETH_ALEN ); + +/* Use only the least significant 6 bits. */ + hashindex = hashindex &0x3F ; + +/* Within "hashtable", set bit number "hashindex" +* to a logic 1. +*/ +set_bit (hashindex, (void *)hashtable); + } + +/* Write the value of the hashtable, to the 4, 16 bit +* HASHTABLE IPG registers. +*/ +ipg_w32 (hashtable[0 ],HASHTABLE_0 ); +ipg_w32 (hashtable[1 ],HASHTABLE_1 ); + +ipg_w8 (IPG_RM_RSVD_MASK & receivemode,RECEIVE_MODE ); + +IPG_DEBUG_MSG ("ReceiveMode = %x\n" ,ipg_r8 (RECEIVE_MODE )); +} + +static int ipg_io_config (struct net_device *dev) +{ +void __iomem *ioaddr =ipg_ioaddr (dev); +u32 origmacctrl; +u32 restoremacctrl; + +IPG_DEBUG_MSG ("_io_config\n" ); + + origmacctrl =ipg_r32 (MAC_CTRL ); + + restoremacctrl = origmacctrl |IPG_MC_STATISTICS_ENABLE ; + +/* Based on compilation option, determine if FCS is to be +* stripped on receive frames by IPG. +*/ +if (!IPG_STRIP_FCS_ON_RX ) + restoremacctrl |=IPG_MC_RCV_FCS ; + +/* Determine if transmitter and/or receiver are +* enabled so we may restore MACCTRL correctly. +*/ +if (origmacctrl &IPG_MC_TX_ENABLED ) + restoremacctrl |=IPG_MC_TX_ENABLE ; + +if (origmacctrl &IPG_MC_RX_ENABLED ) + restoremacctrl |=IPG_MC_RX_ENABLE ; + +/* Transmitter and receiver must be disabled before setting +* IFSSelect. +*/ +ipg_w32 ((origmacctrl & (IPG_MC_RX_DISABLE |IPG_MC_TX_DISABLE )) & +IPG_MC_RSVD_MASK ,MAC_CTRL ); + +/* Now that transmitter and receiver are disabled, write +* to IFSSelect. +*/ +ipg_w32 ((origmacctrl &IPG_MC_IFS_96BIT ) &IPG_MC_RSVD_MASK ,MAC_CTRL ); + +/* Set RECEIVEMODE register. */ +ipg_nic_set_multicast_list (dev); + +ipg_w16 (IPG_MAX_RXFRAME_SIZE ,MAX_FRAME_SIZE ); + +ipg_w8 (IPG_RXDMAPOLLPERIOD_VALUE ,RX_DMA_POLL_PERIOD ); +ipg_w8 (IPG_RXDMAURGENTTHRESH_VALUE ,RX_DMA_URGENT_THRESH ); +ipg_w8 (IPG_RXDMABURSTTHRESH_VALUE ,RX_DMA_BURST_THRESH ); +ipg_w8 (IPG_TXDMAPOLLPERIOD_VALUE ,TX_DMA_POLL_PERIOD ); +ipg_w8 (IPG_TXDMAURGENTTHRESH_VALUE ,TX_DMA_URGENT_THRESH ); +ipg_w8 (IPG_TXDMABURSTTHRESH_VALUE ,TX_DMA_BURST_THRESH ); +ipg_w16 ((IPG_IE_HOST_ERROR |IPG_IE_TX_DMA_COMPLETE | +IPG_IE_TX_COMPLETE |IPG_IE_INT_REQUESTED | +IPG_IE_UPDATE_STATS |IPG_IE_LINK_EVENT | +IPG_IE_RX_DMA_COMPLETE |IPG_IE_RX_DMA_PRIORITY ),INT_ENABLE ); +ipg_w16 (IPG_FLOWONTHRESH_VALUE ,FLOW_ON_THRESH ); +ipg_w16 (IPG_FLOWOFFTHRESH_VALUE ,FLOW_OFF_THRESH ); + +/* IPG multi-frag frame bug workaround. +* Per silicon revision B3 eratta. +*/ +ipg_w16 (ipg_r16 (DEBUG_CTRL ) |0x0200 ,DEBUG_CTRL ); + +/* IPG TX poll now bug workaround. +* Per silicon revision B3 eratta. +*/ +ipg_w16 (ipg_r16 (DEBUG_CTRL ) |0x0010 ,DEBUG_CTRL ); + +/* IPG RX poll now bug workaround. +* Per silicon revision B3 eratta. +*/ +ipg_w16 (ipg_r16 (DEBUG_CTRL ) |0x0020 ,DEBUG_CTRL ); + +/* Now restore MACCTRL to original setting. */ +ipg_w32 (IPG_MC_RSVD_MASK & restoremacctrl,MAC_CTRL ); + +/* Disable unused RMON statistics. */ +ipg_w32 (IPG_RZ_ALL ,RMON_STATISTICS_MASK ); + +/* Disable unused MIB statistics. */ +ipg_w32 (IPG_SM_MACCONTROLFRAMESXMTD |IPG_SM_MACCONTROLFRAMESRCVD | +IPG_SM_BCSTOCTETXMTOK_BCSTFRAMESXMTDOK |IPG_SM_TXJUMBOFRAMES | +IPG_SM_MCSTOCTETXMTOK_MCSTFRAMESXMTDOK |IPG_SM_RXJUMBOFRAMES | +IPG_SM_BCSTOCTETRCVDOK_BCSTFRAMESRCVDOK | +IPG_SM_UDPCHECKSUMERRORS |IPG_SM_TCPCHECKSUMERRORS | +IPG_SM_IPCHECKSUMERRORS ,STATISTICS_MASK ); + +return 0 ; +} + +/* +* Create a receive buffer within system memory and update +* NIC private structure appropriately. +*/ +static int ipg_get_rxbuff (struct net_device *dev,int entry) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +struct ipg_rx *rxfd = sp->rxd + entry; +struct sk_buff *skb; +u64 rxfragsize; + +IPG_DEBUG_MSG ("_get_rxbuff\n" ); + + skb =netdev_alloc_skb (dev,IPG_RXSUPPORT_SIZE +NET_IP_ALIGN ); +if (!skb) { + sp->RxBuff[entry] =NULL ; +return -ENOMEM ; + } + +/* Adjust the data start location within the buffer to +* align IP address field to a 16 byte boundary. +*/ +skb_reserve (skb,NET_IP_ALIGN ); + +/* Associate the receive buffer with the IPG NIC. */ + skb->dev = dev; + +/* Save the address of the sk_buff structure. */ + sp->RxBuff[entry] = skb; + + rxfd->frag_info =cpu_to_le64 (pci_map_single (sp->pdev, skb->data, + sp->rx_buf_sz,PCI_DMA_FROMDEVICE )); + +/* Set the RFD fragment length. */ + rxfragsize =IPG_RXFRAG_SIZE ; + rxfd->frag_info |=cpu_to_le64 ((rxfragsize <<48 ) &IPG_RFI_FRAGLEN ); + +return 0 ; +} + +static int init_rfdlist (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; + +IPG_DEBUG_MSG ("_init_rfdlist\n" ); + +for (i =0 ; i <IPG_RFDLIST_LENGTH ; i++) { +struct ipg_rx *rxfd = sp->rxd + i; + +if (sp->RxBuff[i]) { +pci_unmap_single (sp->pdev, +le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); +IPG_DEV_KFREE_SKB (sp->RxBuff[i]); + sp->RxBuff[i] =NULL ; + } + +/* Clear out the RFS field. */ + rxfd->rfs =0x0000000000000000 ; + +if (ipg_get_rxbuff (dev, i) <0 ) { +/* +* A receive buffer was not ready, break the +* RFD list here. +*/ +IPG_DEBUG_MSG ("Cannot allocate Rx buffer.\n" ); + +/* Just in case we cannot allocate a single RFD. +* Should not occur. +*/ +if (i ==0 ) { +printk (KERN_ERR "%s: No memory available" +" for RFD list.\n" , dev->name); +return -ENOMEM ; + } + } + + rxfd->next_desc =cpu_to_le64 (sp->rxd_map + +sizeof (struct ipg_rx )*(i +1 )); + } + sp->rxd[i -1 ].next_desc =cpu_to_le64 (sp->rxd_map); + + sp->rx_current =0 ; + sp->rx_dirty =0 ; + +/* Write the location of the RFDList to the IPG. */ +ipg_w32 ((u32 ) sp->rxd_map,RFD_LIST_PTR_0 ); +ipg_w32 (0x00000000 ,RFD_LIST_PTR_1 ); + +return 0 ; +} + +static void init_tfdlist (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; + +IPG_DEBUG_MSG ("_init_tfdlist\n" ); + +for (i =0 ; i <IPG_TFDLIST_LENGTH ; i++) { +struct ipg_tx *txfd = sp->txd + i; + + txfd->tfc =cpu_to_le64 (IPG_TFC_TFDDONE ); + +if (sp->TxBuff[i]) { +IPG_DEV_KFREE_SKB (sp->TxBuff[i]); + sp->TxBuff[i] =NULL ; + } + + txfd->next_desc =cpu_to_le64 (sp->txd_map + +sizeof (struct ipg_tx )*(i +1 )); + } + sp->txd[i -1 ].next_desc =cpu_to_le64 (sp->txd_map); + + sp->tx_current =0 ; + sp->tx_dirty =0 ; + +/* Write the location of the TFDList to the IPG. */ +IPG_DDEBUG_MSG ("Starting TFDListPtr = %8.8x\n" , + (u32 ) sp->txd_map); +ipg_w32 ((u32 ) sp->txd_map,TFD_LIST_PTR_0 ); +ipg_w32 (0x00000000 ,TFD_LIST_PTR_1 ); + + sp->ResetCurrentTFD =1 ; +} + +/* +* Free all transmit buffers which have already been transfered +* via DMA to the IPG. +*/ +static void ipg_nic_txfree (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +const unsigned int curr =ipg_r32 (TFD_LIST_PTR_0 ) - + (sp->txd_map /sizeof (struct ipg_tx )) -1 ; +unsigned int released, pending; + +IPG_DEBUG_MSG ("_nic_txfree\n" ); + + pending = sp->tx_current - sp->tx_dirty; + +for (released =0 ; released < pending; released++) { +unsigned int dirty = sp->tx_dirty %IPG_TFDLIST_LENGTH ; +struct sk_buff *skb = sp->TxBuff[dirty]; +struct ipg_tx *txfd = sp->txd + dirty; + +IPG_DEBUG_MSG ("TFC = %16.16lx\n" , (unsigned long ) txfd->tfc); + +/* Look at each TFD's TFC field beginning +* at the last freed TFD up to the current TFD. +* If the TFDDone bit is set, free the associated +* buffer. +*/ +if (dirty == curr) +break ; + +/* Setup TFDDONE for compatible issue. */ + txfd->tfc |=cpu_to_le64 (IPG_TFC_TFDDONE ); + +/* Free the transmit buffer. */ +if (skb) { +pci_unmap_single (sp->pdev, +le64_to_cpu (txfd->frag_info & ~IPG_TFI_FRAGLEN ), + skb->len,PCI_DMA_TODEVICE ); + +IPG_DEV_KFREE_SKB (skb); + + sp->TxBuff[dirty] =NULL ; + } + } + + sp->tx_dirty += released; + +if (netif_queue_stopped (dev) && + (sp->tx_current != (sp->tx_dirty +IPG_TFDLIST_LENGTH ))) { +netif_wake_queue (dev); + } +} + +static void ipg_tx_timeout (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; + +ipg_reset (dev,IPG_AC_TX_RESET |IPG_AC_DMA |IPG_AC_NETWORK | +IPG_AC_FIFO ); + +spin_lock_irq (&sp->lock); + +/* Re-configure after DMA reset. */ +if (ipg_io_config (dev) <0 ) { +printk (KERN_INFO "%s: Error during re-configuration.\n" , + dev->name); + } + +init_tfdlist (dev); + +spin_unlock_irq (&sp->lock); + +ipg_w32 ((ipg_r32 (MAC_CTRL ) |IPG_MC_TX_ENABLE ) &IPG_MC_RSVD_MASK , +MAC_CTRL ); +} + +/* +* For TxComplete interrupts, free all transmit +* buffers which have already been transfered via DMA +* to the IPG. +*/ +static void ipg_nic_txcleanup (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; + +IPG_DEBUG_MSG ("_nic_txcleanup\n" ); + +for (i =0 ; i <IPG_TFDLIST_LENGTH ; i++) { +/* Reading the TXSTATUS register clears the +* TX_COMPLETE interrupt. +*/ +u32 txstatusdword =ipg_r32 (TX_STATUS ); + +IPG_DEBUG_MSG ("TxStatus = %8.8x\n" , txstatusdword); + +/* Check for Transmit errors. Error bits only valid if +* TX_COMPLETE bit in the TXSTATUS register is a 1. +*/ +if (!(txstatusdword &IPG_TS_TX_COMPLETE )) +break ; + +/* If in 10Mbps mode, indicate transmit is ready. */ +if (sp->tenmbpsmode) { +netif_wake_queue (dev); + } + +/* Transmit error, increment stat counters. */ +if (txstatusdword &IPG_TS_TX_ERROR ) { +IPG_DEBUG_MSG ("Transmit error.\n" ); + sp->stats.tx_errors++; + } + +/* Late collision, re-enable transmitter. */ +if (txstatusdword &IPG_TS_LATE_COLLISION ) { +IPG_DEBUG_MSG ("Late collision on transmit.\n" ); +ipg_w32 ((ipg_r32 (MAC_CTRL ) |IPG_MC_TX_ENABLE ) & +IPG_MC_RSVD_MASK ,MAC_CTRL ); + } + +/* Maximum collisions, re-enable transmitter. */ +if (txstatusdword &IPG_TS_TX_MAX_COLL ) { +IPG_DEBUG_MSG ("Maximum collisions on transmit.\n" ); +ipg_w32 ((ipg_r32 (MAC_CTRL ) |IPG_MC_TX_ENABLE ) & +IPG_MC_RSVD_MASK ,MAC_CTRL ); + } + +/* Transmit underrun, reset and re-enable +* transmitter. +*/ +if (txstatusdword &IPG_TS_TX_UNDERRUN ) { +IPG_DEBUG_MSG ("Transmitter underrun.\n" ); + sp->stats.tx_fifo_errors++; +ipg_reset (dev,IPG_AC_TX_RESET |IPG_AC_DMA | +IPG_AC_NETWORK |IPG_AC_FIFO ); + +/* Re-configure after DMA reset. */ +if (ipg_io_config (dev) <0 ) { +printk (KERN_INFO +"%s: Error during re-configuration.\n" , + dev->name); + } +init_tfdlist (dev); + +ipg_w32 ((ipg_r32 (MAC_CTRL ) |IPG_MC_TX_ENABLE ) & +IPG_MC_RSVD_MASK ,MAC_CTRL ); + } + } + +ipg_nic_txfree (dev); +} + +/* Provides statistical information about the IPG NIC. */ +struct net_device_stats *ipg_nic_get_stats (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +u16 temp1; +u16 temp2; + +IPG_DEBUG_MSG ("_nic_get_stats\n" ); + +/* Check to see if the NIC has been initialized via nic_open, +* before trying to read statistic registers. +*/ +if (!test_bit (__LINK_STATE_START, &dev->state)) +return &sp->stats; + + sp->stats.rx_packets +=ipg_r32 (IPG_FRAMESRCVDOK ); + sp->stats.tx_packets +=ipg_r32 (IPG_FRAMESXMTDOK ); + sp->stats.rx_bytes +=ipg_r32 (IPG_OCTETRCVOK ); + sp->stats.tx_bytes +=ipg_r32 (IPG_OCTETXMTOK ); + temp1 =ipg_r16 (IPG_FRAMESLOSTRXERRORS ); + sp->stats.rx_errors += temp1; + sp->stats.rx_missed_errors += temp1; + temp1 =ipg_r32 (IPG_SINGLECOLFRAMES ) +ipg_r32 (IPG_MULTICOLFRAMES ) + +ipg_r32 (IPG_LATECOLLISIONS ); + temp2 =ipg_r16 (IPG_CARRIERSENSEERRORS ); + sp->stats.collisions += temp1; + sp->stats.tx_dropped +=ipg_r16 (IPG_FRAMESABORTXSCOLLS ); + sp->stats.tx_errors +=ipg_r16 (IPG_FRAMESWEXDEFERRAL ) + +ipg_r32 (IPG_FRAMESWDEFERREDXMT ) + temp1 + temp2; + sp->stats.multicast +=ipg_r32 (IPG_MCSTOCTETRCVDOK ); + +/* detailed tx_errors */ + sp->stats.tx_carrier_errors += temp2; + +/* detailed rx_errors */ + sp->stats.rx_length_errors +=ipg_r16 (IPG_INRANGELENGTHERRORS ) + +ipg_r16 (IPG_FRAMETOOLONGERRRORS ); + sp->stats.rx_crc_errors +=ipg_r16 (IPG_FRAMECHECKSEQERRORS ); + +/* Unutilized IPG statistic registers. */ +ipg_r32 (IPG_MCSTFRAMESRCVDOK ); + +return &sp->stats; +} + +/* Restore used receive buffers. */ +static int ipg_nic_rxrestore (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +const unsigned int curr = sp->rx_current; +unsigned int dirty = sp->rx_dirty; + +IPG_DEBUG_MSG ("_nic_rxrestore\n" ); + +for (dirty = sp->rx_dirty; curr - dirty >0 ; dirty++) { +unsigned int entry = dirty %IPG_RFDLIST_LENGTH ; + +/* rx_copybreak may poke hole here and there. */ +if (sp->RxBuff[entry]) +continue ; + +/* Generate a new receive buffer to replace the +* current buffer (which will be released by the +* Linux system). +*/ +if (ipg_get_rxbuff (dev, entry) <0 ) { +IPG_DEBUG_MSG ("Cannot allocate new Rx buffer.\n" ); + +break ; + } + +/* Reset the RFS field. */ + sp->rxd[entry].rfs =0x0000000000000000 ; + } + sp->rx_dirty = dirty; + +return 0 ; +} + +#ifdef JUMBO_FRAME + +/* use jumboindex and jumbosize to control jumbo frame status +initial status is jumboindex=-1 and jumbosize=0 +1. jumboindex = -1 and jumbosize=0 : previous jumbo frame has been done. +2. jumboindex != -1 and jumbosize != 0 : jumbo frame is not over size and receiving +3. jumboindex = -1 and jumbosize != 0 : jumbo frame is over size, already dump +previous receiving and need to continue dumping the current one +*/ +enum { + NormalPacket, + ErrorPacket +}; + +enum { + Frame_NoStart_NoEnd =0 , + Frame_WithStart =1 , + Frame_WithEnd =10 , + Frame_WithStart_WithEnd =11 +}; + +inline void ipg_nic_rx_free_skb (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +unsigned int entry = sp->rx_current %IPG_RFDLIST_LENGTH ; + +if (sp->RxBuff[entry]) { +struct ipg_rx *rxfd = sp->rxd + entry; + +pci_unmap_single (sp->pdev, +le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); +IPG_DEV_KFREE_SKB (sp->RxBuff[entry]); + sp->RxBuff[entry] =NULL ; + } +} + +inline int ipg_nic_rx_check_frame_type (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +struct ipg_rx *rxfd = sp->rxd + (sp->rx_current %IPG_RFDLIST_LENGTH ); +int type = Frame_NoStart_NoEnd; + +if (le64_to_cpu (rxfd->rfs) &IPG_RFS_FRAMESTART ) + type += Frame_WithStart; +if (le64_to_cpu (rxfd->rfs) &IPG_RFS_FRAMEEND ) + type += Frame_WithEnd; +return type; +} + +inline int ipg_nic_rx_check_error (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +unsigned int entry = sp->rx_current %IPG_RFDLIST_LENGTH ; +struct ipg_rx *rxfd = sp->rxd + entry; + +if (IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu (rxfd->rfs) & + (IPG_RFS_RXFIFOOVERRUN |IPG_RFS_RXRUNTFRAME | +IPG_RFS_RXALIGNMENTERROR |IPG_RFS_RXFCSERROR | +IPG_RFS_RXOVERSIZEDFRAME |IPG_RFS_RXLENGTHERROR ))) { +IPG_DEBUG_MSG ("Rx error, RFS = %16.16lx\n" , + (unsigned long ) rxfd->rfs); + +/* Increment general receive error statistic. */ + sp->stats.rx_errors++; + +/* Increment detailed receive error statistics. */ +if (le64_to_cpu (rxfd->rfs) &IPG_RFS_RXFIFOOVERRUN ) { +IPG_DEBUG_MSG ("RX FIFO overrun occured.\n" ); + + sp->stats.rx_fifo_errors++; + } + +if (le64_to_cpu (rxfd->rfs) &IPG_RFS_RXRUNTFRAME ) { +IPG_DEBUG_MSG ("RX runt occured.\n" ); + sp->stats.rx_length_errors++; + } + +/* Do nothing for IPG_RFS_RXOVERSIZEDFRAME, +* error count handled by a IPG statistic register. +*/ + +if (le64_to_cpu (rxfd->rfs) &IPG_RFS_RXALIGNMENTERROR ) { +IPG_DEBUG_MSG ("RX alignment error occured.\n" ); + sp->stats.rx_frame_errors++; + } + +/* Do nothing for IPG_RFS_RXFCSERROR, error count +* handled by a IPG statistic register. +*/ + +/* Free the memory associated with the RX +* buffer since it is erroneous and we will +* not pass it to higher layer processes. +*/ +if (sp->RxBuff[entry]) { +pci_unmap_single (sp->pdev, +le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); + +IPG_DEV_KFREE_SKB (sp->RxBuff[entry]); + sp->RxBuff[entry] =NULL ; + } +return ErrorPacket; + } +return NormalPacket; +} + +static void ipg_nic_rx_with_start_and_end (struct net_device *dev, +struct ipg_nic_private *sp, +struct ipg_rx *rxfd,unsigned entry) +{ +struct SJumbo *jumbo = &sp->Jumbo; +struct sk_buff *skb; +int framelen; + +if (jumbo->FoundStart) { +IPG_DEV_KFREE_SKB (jumbo->skb); + jumbo->FoundStart =0 ; + jumbo->CurrentSize =0 ; + jumbo->skb =NULL ; + } + +// 1: found error, 0 no error +if (ipg_nic_rx_check_error (dev) != NormalPacket) +return ; + + skb = sp->RxBuff[entry]; +if (!skb) +return ; + +// accept this frame and send to upper layer + framelen =le64_to_cpu (rxfd->rfs) &IPG_RFS_RXFRAMELEN ; +if (framelen >IPG_RXFRAG_SIZE ) + framelen =IPG_RXFRAG_SIZE ; + +skb_put (skb, framelen); + skb->protocol =eth_type_trans (skb, dev); + skb->ip_summed =CHECKSUM_NONE ; +netif_rx (skb); + dev->last_rx = jiffies; + sp->RxBuff[entry] =NULL ; +} + +static void ipg_nic_rx_with_start (struct net_device *dev, +struct ipg_nic_private *sp, +struct ipg_rx *rxfd,unsigned entry) +{ +struct SJumbo *jumbo = &sp->Jumbo; +struct pci_dev *pdev = sp->pdev; +struct sk_buff *skb; + +// 1: found error, 0 no error +if (ipg_nic_rx_check_error (dev) != NormalPacket) +return ; + +// accept this frame and send to upper layer + skb = sp->RxBuff[entry]; +if (!skb) +return ; + +if (jumbo->FoundStart) +IPG_DEV_KFREE_SKB (jumbo->skb); + +pci_unmap_single (pdev,le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); + +skb_put (skb,IPG_RXFRAG_SIZE ); + + jumbo->FoundStart =1 ; + jumbo->CurrentSize =IPG_RXFRAG_SIZE ; + jumbo->skb = skb; + + sp->RxBuff[entry] =NULL ; + dev->last_rx = jiffies; +} + +static void ipg_nic_rx_with_end (struct net_device *dev, +struct ipg_nic_private *sp, +struct ipg_rx *rxfd,unsigned entry) +{ +struct SJumbo *jumbo = &sp->Jumbo; + +//1: found error, 0 no error +if (ipg_nic_rx_check_error (dev) == NormalPacket) { +struct sk_buff *skb = sp->RxBuff[entry]; + +if (!skb) +return ; + +if (jumbo->FoundStart) { +int framelen, endframelen; + + framelen =le64_to_cpu (rxfd->rfs) &IPG_RFS_RXFRAMELEN ; + + endframeLen = framelen - jumbo->CurrentSize; +/* +if (framelen > IPG_RXFRAG_SIZE) +framelen=IPG_RXFRAG_SIZE; +*/ +if (framelen >IPG_RXSUPPORT_SIZE ) +IPG_DEV_KFREE_SKB (jumbo->skb); +else { +memcpy (skb_put (jumbo->skb, endframeLen), + skb->data, endframeLen); + + jumbo->skb->protocol = +eth_type_trans (jumbo->skb, dev); + + jumbo->skb->ip_summed =CHECKSUM_NONE ; +netif_rx (jumbo->skb); + } + } + + dev->last_rx = jiffies; + jumbo->FoundStart =0 ; + jumbo->CurrentSize =0 ; + jumbo->skb =NULL ; + +ipg_nic_rx_free_skb (dev); + }else { +IPG_DEV_KFREE_SKB (jumbo->skb); + jumbo->FoundStart =0 ; + jumbo->CurrentSize =0 ; + jumbo->skb =NULL ; + } +} + +static void ipg_nic_rx_no_start_no_end (struct net_device *dev, +struct ipg_nic_private *sp, +struct ipg_rx *rxfd,unsigned entry) +{ +struct SJumbo *jumbo = &sp->Jumbo; + +//1: found error, 0 no error +if (ipg_nic_rx_check_error (dev) == NormalPacket) { +struct sk_buff *skb = sp->RxBuff[entry]; + +if (skb) { +if (jumbo->FoundStart) { + jumbo->CurrentSize +=IPG_RXFRAG_SIZE ; +if (jumbo->CurrentSize <=IPG_RXSUPPORT_SIZE ) { +memcpy (skb_put (jumbo->skb, +IPG_RXFRAG_SIZE ), + skb->data,IPG_RXFRAG_SIZE ); + } + } + dev->last_rx = jiffies; +ipg_nic_rx_free_skb (dev); + } + }else { +IPG_DEV_KFREE_SKB (jumbo->skb); + jumbo->FoundStart =0 ; + jumbo->CurrentSize =0 ; + jumbo->skb =NULL ; + } +} + +static int ipg_nic_rx (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +unsigned int curr = sp->rx_current; +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; + +IPG_DEBUG_MSG ("_nic_rx\n" ); + +for (i =0 ; i <IPG_MAXRFDPROCESS_COUNT ; i++, curr++) { +unsigned int entry = curr %IPG_RFDLIST_LENGTH ; +struct ipg_rx *rxfd = sp->rxd + entry; + +if (!(rxfd->rfs &le64_to_cpu (IPG_RFS_RFDDONE ))) +break ; + +switch (ipg_nic_rx_check_frame_type (dev)) { +case Frame_WithStart_WithEnd: +ipg_nic_rx_with_start_and_end (dev, tp, rxfd, entry); +break ; +case Frame_WithStart: +ipg_nic_rx_with_start (dev, tp, rxfd, entry); +break ; +case Frame_WithEnd: +ipg_nic_rx_with_end (dev, tp, rxfd, entry); +break ; +case Frame_NoStart_NoEnd: +ipg_nic_rx_no_start_no_end (dev, tp, rxfd, entry); +break ; + } + } + + sp->rx_current = curr; + +if (i ==IPG_MAXRFDPROCESS_COUNT ) { +/* There are more RFDs to process, however the +* allocated amount of RFD processing time has +* expired. Assert Interrupt Requested to make +* sure we come back to process the remaining RFDs. +*/ +ipg_w32 (ipg_r32 (ASIC_CTRL ) |IPG_AC_INT_REQUEST ,ASIC_CTRL ); + } + +ipg_nic_rxrestore (dev); + +return 0 ; +} + +#else +static int ipg_nic_rx (struct net_device *dev) +{ +/* Transfer received Ethernet frames to higher network layers. */ +struct ipg_nic_private *sp =netdev_priv (dev); +unsigned int curr = sp->rx_current; +void __iomem *ioaddr = sp->ioaddr; +struct ipg_rx *rxfd; +unsigned int i; + +IPG_DEBUG_MSG ("_nic_rx\n" ); + +#define __RFS_MASK \ + cpu_to_le64(IPG_RFS_RFDDONE | IPG_RFS_FRAMESTART | IPG_RFS_FRAMEEND) + +for (i =0 ; i <IPG_MAXRFDPROCESS_COUNT ; i++, curr++) { +unsigned int entry = curr %IPG_RFDLIST_LENGTH ; +struct sk_buff *skb = sp->RxBuff[entry]; +unsigned int framelen; + + rxfd = sp->rxd + entry; + +if (((rxfd->rfs & __RFS_MASK) != __RFS_MASK) || !skb) +break ; + +/* Get received frame length. */ + framelen =le64_to_cpu (rxfd->rfs) &IPG_RFS_RXFRAMELEN ; + +/* Check for jumbo frame arrival with too small +* RXFRAG_SIZE. +*/ +if (framelen >IPG_RXFRAG_SIZE ) { +IPG_DEBUG_MSG + ("RFS FrameLen > allocated fragment size.\n" ); + + framelen =IPG_RXFRAG_SIZE ; + } + +if ((IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu (rxfd->rfs & + (IPG_RFS_RXFIFOOVERRUN |IPG_RFS_RXRUNTFRAME | +IPG_RFS_RXALIGNMENTERROR |IPG_RFS_RXFCSERROR | +IPG_RFS_RXOVERSIZEDFRAME |IPG_RFS_RXLENGTHERROR ))))) { + +IPG_DEBUG_MSG ("Rx error, RFS = %16.16lx\n" , + (unsigned long int ) rxfd->rfs); + +/* Increment general receive error statistic. */ + sp->stats.rx_errors++; + +/* Increment detailed receive error statistics. */ +if (le64_to_cpu (rxfd->rfs &IPG_RFS_RXFIFOOVERRUN )) { +IPG_DEBUG_MSG ("RX FIFO overrun occured.\n" ); + sp->stats.rx_fifo_errors++; + } + +if (le64_to_cpu (rxfd->rfs &IPG_RFS_RXRUNTFRAME )) { +IPG_DEBUG_MSG ("RX runt occured.\n" ); + sp->stats.rx_length_errors++; + } + +if (le64_to_cpu (rxfd->rfs &IPG_RFS_RXOVERSIZEDFRAME )) ; +/* Do nothing, error count handled by a IPG +* statistic register. +*/ + +if (le64_to_cpu (rxfd->rfs &IPG_RFS_RXALIGNMENTERROR )) { +IPG_DEBUG_MSG ("RX alignment error occured.\n" ); + sp->stats.rx_frame_errors++; + } + +if (le64_to_cpu (rxfd->rfs &IPG_RFS_RXFCSERROR )) ; +/* Do nothing, error count handled by a IPG +* statistic register. +*/ + +/* Free the memory associated with the RX +* buffer since it is erroneous and we will +* not pass it to higher layer processes. +*/ +if (skb) { +u64 info = rxfd->frag_info; + +pci_unmap_single (sp->pdev, +le64_to_cpu (info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); + +IPG_DEV_KFREE_SKB (skb); + } + }else { + +/* Adjust the new buffer length to accomodate the size +* of the received frame. +*/ +skb_put (skb, framelen); + +/* Set the buffer's protocol field to Ethernet. */ + skb->protocol =eth_type_trans (skb, dev); + +/* If the frame contains an IP/TCP/UDP frame, +* determine if upper layer must check IP/TCP/UDP +* checksums. +* +* NOTE: DO NOT RELY ON THE TCP/UDP CHECKSUM +* VERIFICATION FOR SILICON REVISIONS B3 +* AND EARLIER! +* +if ((le64_to_cpu(rxfd->rfs & +(IPG_RFS_TCPDETECTED | IPG_RFS_UDPDETECTED | +IPG_RFS_IPDETECTED))) && +!(le64_to_cpu(rxfd->rfs & +(IPG_RFS_TCPERROR | IPG_RFS_UDPERROR | +IPG_RFS_IPERROR)))) +{ +* Indicate IP checksums were performed +* by the IPG. +* +skb->ip_summed = CHECKSUM_UNNECESSARY; +} +else +*/ +if (1 ==1 ) { +/* The IPG encountered an error with (or +* there were no) IP/TCP/UDP checksums. +* This may or may not indicate an invalid +* IP/TCP/UDP frame was received. Let the +* upper layer decide. +*/ + skb->ip_summed =CHECKSUM_NONE ; + } + +/* Hand off frame for higher layer processing. +* The function netif_rx() releases the sk_buff +* when processing completes. +*/ +netif_rx (skb); + +/* Record frame receive time (jiffies = Linux +* kernel current time stamp). +*/ + dev->last_rx = jiffies; + } + +/* Assure RX buffer is not reused by IPG. */ + sp->RxBuff[entry] =NULL ; + } + +/* +* If there are more RFDs to proces and the allocated amount of RFD +* processing time has expired, assert Interrupt Requested to make +* sure we come back to process the remaining RFDs. +*/ +if (i ==IPG_MAXRFDPROCESS_COUNT ) +ipg_w32 (ipg_r32 (ASIC_CTRL ) |IPG_AC_INT_REQUEST ,ASIC_CTRL ); + +#ifdef IPG_DEBUG +/* Check if the RFD list contained no receive frame data. */ +if (!i) + sp->EmptyRFDListCount++; +#endif +while ((le64_to_cpu (rxfd->rfs &IPG_RFS_RFDDONE )) && + !((le64_to_cpu (rxfd->rfs &IPG_RFS_FRAMESTART )) && + (le64_to_cpu (rxfd->rfs &IPG_RFS_FRAMEEND )))) { +unsigned int entry = curr++ %IPG_RFDLIST_LENGTH ; + + rxfd = sp->rxd + entry; + +IPG_DEBUG_MSG ("Frame requires multiple RFDs.\n" ); + +/* An unexpected event, additional code needed to handle +* properly. So for the time being, just disregard the +* frame. +*/ + +/* Free the memory associated with the RX +* buffer since it is erroneous and we will +* not pass it to higher layer processes. +*/ +if (sp->RxBuff[entry]) { +pci_unmap_single (sp->pdev, +le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); +IPG_DEV_KFREE_SKB (sp->RxBuff[entry]); + } + +/* Assure RX buffer is not reused by IPG. */ + sp->RxBuff[entry] =NULL ; + } + + sp->rx_current = curr; + +/* Check to see if there are a minimum number of used +* RFDs before restoring any (should improve performance.) +*/ +if ((curr - sp->rx_dirty) >=IPG_MINUSEDRFDSTOFREE ) +ipg_nic_rxrestore (dev); + +return 0 ; +} +#endif + +static void ipg_reset_after_host_error (struct work_struct *work) +{ +struct ipg_nic_private *sp = + container_of(work, struct ipg_nic_private, task.work); +struct net_device *dev = sp->dev; + +IPG_DDEBUG_MSG ("DMACtrl = %8.8x\n" ,ioread32 (sp->ioaddr +IPG_DMACTRL )); + +/* +* Acknowledge HostError interrupt by resetting +* IPG DMA and HOST. +*/ +ipg_reset (dev,IPG_AC_GLOBAL_RESET |IPG_AC_HOST |IPG_AC_DMA ); + +init_rfdlist (dev); +init_tfdlist (dev); + +if (ipg_io_config (dev) <0 ) { +printk (KERN_INFO "%s: Cannot recover from PCI error.\n" , + dev->name); +schedule_delayed_work (&sp->task,HZ ); + } +} + +static irqreturn_t ipg_interrupt_handler (int irq,void *dev_inst) +{ +struct net_device *dev = dev_inst; +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int handled =0 ; +u16 status; + +IPG_DEBUG_MSG ("_interrupt_handler\n" ); + +#ifdef JUMBO_FRAME +ipg_nic_rxrestore (dev); +#endif +/* Get interrupt source information, and acknowledge +* some (i.e. TxDMAComplete, RxDMAComplete, RxEarly, +* IntRequested, MacControlFrame, LinkEvent) interrupts +* if issued. Also, all IPG interrupts are disabled by +* reading IntStatusAck. +*/ + status =ipg_r16 (INT_STATUS_ACK ); + +IPG_DEBUG_MSG ("IntStatusAck = %4.4x\n" , status); + +/* Shared IRQ of remove event. */ +if (!(status &IPG_IS_RSVD_MASK )) + goto out_enable; + + handled =1 ; + +if (unlikely (!netif_running (dev))) + goto out; + +spin_lock (&sp->lock); + +/* If RFDListEnd interrupt, restore all used RFDs. */ +if (status &IPG_IS_RFD_LIST_END ) { +IPG_DEBUG_MSG ("RFDListEnd Interrupt.\n" ); + +/* The RFD list end indicates an RFD was encountered +* with a 0 NextPtr, or with an RFDDone bit set to 1 +* (indicating the RFD is not read for use by the +* IPG.) Try to restore all RFDs. +*/ +ipg_nic_rxrestore (dev); + +#ifdef IPG_DEBUG +/* Increment the RFDlistendCount counter. */ + sp->RFDlistendCount++; +#endif + } + +/* If RFDListEnd, RxDMAPriority, RxDMAComplete, or +* IntRequested interrupt, process received frames. */ +if ((status &IPG_IS_RX_DMA_PRIORITY ) || + (status &IPG_IS_RFD_LIST_END ) || + (status &IPG_IS_RX_DMA_COMPLETE ) || + (status &IPG_IS_INT_REQUESTED )) { +#ifdef IPG_DEBUG +/* Increment the RFD list checked counter if interrupted +* only to check the RFD list. */ +if (status & (~(IPG_IS_RX_DMA_PRIORITY |IPG_IS_RFD_LIST_END | +IPG_IS_RX_DMA_COMPLETE |IPG_IS_INT_REQUESTED ) & + (IPG_IS_HOST_ERROR |IPG_IS_TX_DMA_COMPLETE | +IPG_IS_LINK_EVENT |IPG_IS_TX_COMPLETE | +IPG_IS_UPDATE_STATS ))) + sp->RFDListCheckedCount++; +#endif + +ipg_nic_rx (dev); + } + +/* If TxDMAComplete interrupt, free used TFDs. */ +if (status &IPG_IS_TX_DMA_COMPLETE ) +ipg_nic_txfree (dev); + +/* TxComplete interrupts indicate one of numerous actions. +* Determine what action to take based on TXSTATUS register. +*/ +if (status &IPG_IS_TX_COMPLETE ) +ipg_nic_txcleanup (dev); + +/* If UpdateStats interrupt, update Linux Ethernet statistics */ +if (status &IPG_IS_UPDATE_STATS ) +ipg_nic_get_stats (dev); + +/* If HostError interrupt, reset IPG. */ +if (status &IPG_IS_HOST_ERROR ) { +IPG_DDEBUG_MSG ("HostError Interrupt\n" ); + +schedule_delayed_work (&sp->task,0 ); + } + +/* If LinkEvent interrupt, resolve autonegotiation. */ +if (status &IPG_IS_LINK_EVENT ) { +if (ipg_config_autoneg (dev) <0 ) +printk (KERN_INFO "%s: Auto-negotiation error.\n" , + dev->name); + } + +/* If MACCtrlFrame interrupt, do nothing. */ +if (status &IPG_IS_MAC_CTRL_FRAME ) +IPG_DEBUG_MSG ("MACCtrlFrame interrupt.\n" ); + +/* If RxComplete interrupt, do nothing. */ +if (status &IPG_IS_RX_COMPLETE ) +IPG_DEBUG_MSG ("RxComplete interrupt.\n" ); + +/* If RxEarly interrupt, do nothing. */ +if (status &IPG_IS_RX_EARLY ) +IPG_DEBUG_MSG ("RxEarly interrupt.\n" ); + +out_enable: +/* Re-enable IPG interrupts. */ +ipg_w16 (IPG_IE_TX_DMA_COMPLETE |IPG_IE_RX_DMA_COMPLETE | +IPG_IE_HOST_ERROR |IPG_IE_INT_REQUESTED |IPG_IE_TX_COMPLETE | +IPG_IE_LINK_EVENT |IPG_IE_UPDATE_STATS ,INT_ENABLE ); + +spin_unlock (&sp->lock); +out: +return IRQ_RETVAL (handled); +} + +static void ipg_rx_clear (struct ipg_nic_private *sp) +{ +unsigned int i; + +for (i =0 ; i <IPG_RFDLIST_LENGTH ; i++) { +if (sp->RxBuff[i]) { +struct ipg_rx *rxfd = sp->rxd + i; + +IPG_DEV_KFREE_SKB (sp->RxBuff[i]); + sp->RxBuff[i] =NULL ; +pci_unmap_single (sp->pdev, +le64_to_cpu (rxfd->frag_info & ~IPG_RFI_FRAGLEN ), + sp->rx_buf_sz,PCI_DMA_FROMDEVICE ); + } + } +} + +static void ipg_tx_clear (struct ipg_nic_private *sp) +{ +unsigned int i; + +for (i =0 ; i <IPG_TFDLIST_LENGTH ; i++) { +if (sp->TxBuff[i]) { +struct ipg_tx *txfd = sp->txd + i; + +pci_unmap_single (sp->pdev, +le64_to_cpu (txfd->frag_info & ~IPG_TFI_FRAGLEN ), + sp->TxBuff[i]->len,PCI_DMA_TODEVICE ); + +IPG_DEV_KFREE_SKB (sp->TxBuff[i]); + + sp->TxBuff[i] =NULL ; + } + } +} + +static int ipg_nic_open (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +struct pci_dev *pdev = sp->pdev; +int rc; + +IPG_DEBUG_MSG ("_nic_open\n" ); + + sp->rx_buf_sz =IPG_RXSUPPORT_SIZE ; + +/* Check for interrupt line conflicts, and request interrupt +* line for IPG. +* +* IMPORTANT: Disable IPG interrupts prior to registering +* IRQ. +*/ +ipg_w16 (0x0000 ,INT_ENABLE ); + +/* Register the interrupt line to be used by the IPG within +* the Linux system. +*/ + rc =request_irq (pdev->irq, &ipg_interrupt_handler,IRQF_SHARED , + dev->name, dev); +if (rc <0 ) { +printk (KERN_INFO "%s: Error when requesting interrupt.\n" , + dev->name); + goto out; + } + + dev->irq = pdev->irq; + + rc = -ENOMEM ; + + sp->rxd =dma_alloc_coherent (&pdev->dev,IPG_RX_RING_BYTES , + &sp->rxd_map,GFP_KERNEL ); +if (!sp->rxd) + goto err_free_irq_0; + + sp->txd =dma_alloc_coherent (&pdev->dev,IPG_TX_RING_BYTES , + &sp->txd_map,GFP_KERNEL ); +if (!sp->txd) + goto err_free_rx_1; + + rc =init_rfdlist (dev); +if (rc <0 ) { +printk (KERN_INFO "%s: Error during configuration.\n" , + dev->name); + goto err_free_tx_2; + } + +init_tfdlist (dev); + + rc =ipg_io_config (dev); +if (rc <0 ) { +printk (KERN_INFO "%s: Error during configuration.\n" , + dev->name); + goto err_release_tfdlist_3; + } + +/* Resolve autonegotiation. */ +if (ipg_config_autoneg (dev) <0 ) +printk (KERN_INFO "%s: Auto-negotiation error.\n" , dev->name); + +#ifdef JUMBO_FRAME +/* initialize JUMBO Frame control variable */ + sp->Jumbo.FoundStart =0 ; + sp->Jumbo.CurrentSize =0 ; + sp->Jumbo.skb =0 ; + dev->mtu =IPG_TXFRAG_SIZE ; +#endif + +/* Enable transmit and receive operation of the IPG. */ +ipg_w32 ((ipg_r32 (MAC_CTRL ) |IPG_MC_RX_ENABLE |IPG_MC_TX_ENABLE ) & +IPG_MC_RSVD_MASK ,MAC_CTRL ); + +netif_start_queue (dev); +out: +return rc; + +err_release_tfdlist_3: +ipg_tx_clear (sp); +ipg_rx_clear (sp); +err_free_tx_2: +dma_free_coherent (&pdev->dev,IPG_TX_RING_BYTES , sp->txd, sp->txd_map); +err_free_rx_1: +dma_free_coherent (&pdev->dev,IPG_RX_RING_BYTES , sp->rxd, sp->rxd_map); +err_free_irq_0: +free_irq (pdev->irq, dev); + goto out; +} + +static int ipg_nic_stop (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +struct pci_dev *pdev = sp->pdev; + +IPG_DEBUG_MSG ("_nic_stop\n" ); + +netif_stop_queue (dev); + +IPG_DDEBUG_MSG ("RFDlistendCount = %i\n" , sp->RFDlistendCount); +IPG_DDEBUG_MSG ("RFDListCheckedCount = %i\n" , sp->rxdCheckedCount); +IPG_DDEBUG_MSG ("EmptyRFDListCount = %i\n" , sp->EmptyRFDListCount); +IPG_DUMPTFDLIST (dev); + +do { + (void )ipg_r16 (INT_STATUS_ACK ); + +ipg_reset (dev,IPG_AC_GLOBAL_RESET |IPG_AC_HOST |IPG_AC_DMA ); + +synchronize_irq (pdev->irq); + }while (ipg_r16 (INT_ENABLE ) &IPG_IE_RSVD_MASK ); + +ipg_rx_clear (sp); + +ipg_tx_clear (sp); + +pci_free_consistent (pdev,IPG_RX_RING_BYTES , sp->rxd, sp->rxd_map); +pci_free_consistent (pdev,IPG_TX_RING_BYTES , sp->txd, sp->txd_map); + +free_irq (pdev->irq, dev); + +return 0 ; +} + +static int ipg_nic_hard_start_xmit (struct sk_buff *skb,struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int entry = sp->tx_current %IPG_TFDLIST_LENGTH ; +unsigned long flags; +struct ipg_tx *txfd; + +IPG_DDEBUG_MSG ("_nic_hard_start_xmit\n" ); + +/* If in 10Mbps mode, stop the transmit queue so +* no more transmit frames are accepted. +*/ +if (sp->tenmbpsmode) +netif_stop_queue (dev); + +if (sp->ResetCurrentTFD) { + sp->ResetCurrentTFD =0 ; + entry =0 ; + } + + txfd = sp->txd + entry; + + sp->TxBuff[entry] = skb; + +/* Clear all TFC fields, except TFDDONE. */ + txfd->tfc =cpu_to_le64 (IPG_TFC_TFDDONE ); + +/* Specify the TFC field within the TFD. */ + txfd->tfc |=cpu_to_le64 (IPG_TFC_WORDALIGNDISABLED | + (IPG_TFC_FRAMEID &cpu_to_le64 (sp->tx_current)) | + (IPG_TFC_FRAGCOUNT & (1 <<24 ))); + +/* Request TxComplete interrupts at an interval defined +* by the constant IPG_FRAMESBETWEENTXCOMPLETES. +* Request TxComplete interrupt for every frame +* if in 10Mbps mode to accomodate problem with 10Mbps +* processing. +*/ +if (sp->tenmbpsmode) + txfd->tfc |=cpu_to_le64 (IPG_TFC_TXINDICATE ); +else if (!((sp->tx_current - sp->tx_dirty +1 ) > +IPG_FRAMESBETWEENTXDMACOMPLETES )) { + txfd->tfc |=cpu_to_le64 (IPG_TFC_TXDMAINDICATE ); + } +/* Based on compilation option, determine if FCS is to be +* appended to transmit frame by IPG. +*/ +if (!(IPG_APPEND_FCS_ON_TX )) + txfd->tfc |=cpu_to_le64 (IPG_TFC_FCSAPPENDDISABLE ); + +/* Based on compilation option, determine if IP, TCP and/or +* UDP checksums are to be added to transmit frame by IPG. +*/ +if (IPG_ADD_IPCHECKSUM_ON_TX ) + txfd->tfc |=cpu_to_le64 (IPG_TFC_IPCHECKSUMENABLE ); + +if (IPG_ADD_TCPCHECKSUM_ON_TX ) + txfd->tfc |=cpu_to_le64 (IPG_TFC_TCPCHECKSUMENABLE ); + +if (IPG_ADD_UDPCHECKSUM_ON_TX ) + txfd->tfc |=cpu_to_le64 (IPG_TFC_UDPCHECKSUMENABLE ); + +/* Based on compilation option, determine if VLAN tag info is to be +* inserted into transmit frame by IPG. +*/ +if (IPG_INSERT_MANUAL_VLAN_TAG ) { + txfd->tfc |=cpu_to_le64 (IPG_TFC_VLANTAGINSERT | + ((u64 )IPG_MANUAL_VLAN_VID <<32 ) | + ((u64 )IPG_MANUAL_VLAN_CFI <<44 ) | + ((u64 )IPG_MANUAL_VLAN_USERPRIORITY <<45 )); + } + +/* The fragment start location within system memory is defined +* by the sk_buff structure's data field. The physical address +* of this location within the system's virtual memory space +* is determined using the IPG_HOST2BUS_MAP function. +*/ + txfd->frag_info =cpu_to_le64 (pci_map_single (sp->pdev, skb->data, + skb->len,PCI_DMA_TODEVICE )); + +/* The length of the fragment within system memory is defined by +* the sk_buff structure's len field. +*/ + txfd->frag_info |=cpu_to_le64 (IPG_TFI_FRAGLEN & + ((u64) (skb->len &0xffff ) <<48 )); + +/* Clear the TFDDone bit last to indicate the TFD is ready +* for transfer to the IPG. +*/ + txfd->tfc &=cpu_to_le64 (~IPG_TFC_TFDDONE ); + +spin_lock_irqsave (&sp->lock, flags); + + sp->tx_current++; + +mmiowb (); + +ipg_w32 (IPG_DC_TX_DMA_POLL_NOW ,DMA_CTRL ); + +if (sp->tx_current == (sp->tx_dirty +IPG_TFDLIST_LENGTH )) +netif_wake_queue (dev); + +spin_unlock_irqrestore (&sp->lock, flags); + +return NETDEV_TX_OK ; +} + +static void ipg_set_phy_default_param (unsigned char rev, +struct net_device *dev,int phy_address) +{ +unsigned short length; +unsigned char revision; +unsigned short *phy_param; +unsigned short address, value; + + phy_param = &DefaultPhyParam[0 ]; + length = *phy_param &0x00FF ; + revision = (unsigned char )((*phy_param) >>8 ); + phy_param++; +while (length !=0 ) { +if (rev == revision) { +while (length >1 ) { + address = *phy_param; + value = *(phy_param +1 ); + phy_param +=2 ; +mdio_write (dev, phy_address, address, value); + length -=4 ; + } +break ; + }else { + phy_param += length /2 ; + length = *phy_param &0x00FF ; + revision = (unsigned char )((*phy_param) >>8 ); + phy_param++; + } + } +} + +/* JES20040127EEPROM */ +static int read_eeprom (struct net_device *dev,int eep_addr) +{ +void __iomem *ioaddr =ipg_ioaddr (dev); +unsigned int i; +int ret =0 ; +u16 value; + + value =IPG_EC_EEPROM_READOPCODE | (eep_addr &0xff ); +ipg_w16 (value,EEPROM_CTRL ); + +for (i =0 ; i <1000 ; i++) { +u16 data; + +mdelay (10 ); + data =ipg_r16 (EEPROM_CTRL ); +if (!(data &IPG_EC_EEPROM_BUSY )) { + ret =ipg_r16 (EEPROM_DATA ); +break ; + } + } +return ret; +} + +static void ipg_init_mii (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +struct mii_if_info *mii_if = &sp->mii_if; +int phyaddr; + + mii_if->dev = dev; + mii_if->mdio_read = mdio_read; + mii_if->mdio_write = mdio_write; + mii_if->phy_id_mask =0x1f ; + mii_if->reg_num_mask =0x1f ; + + mii_if->phy_id = phyaddr =ipg_find_phyaddr (dev); + +if (phyaddr !=0x1f ) { +u16 mii_phyctrl, mii_1000cr; +u8 revisionid =0 ; + + mii_1000cr =mdio_read (dev, phyaddr,MII_CTRL1000 ); + mii_1000cr |=ADVERTISE_1000FULL |ADVERTISE_1000HALF | + GMII_PHY_1000BASETCONTROL_PreferMaster; +mdio_write (dev, phyaddr,MII_CTRL1000 , mii_1000cr); + + mii_phyctrl =mdio_read (dev, phyaddr,MII_BMCR ); + +/* Set default phyparam */ +pci_read_config_byte (sp->pdev,PCI_REVISION_ID , &revisionid); +ipg_set_phy_default_param (revisionid, dev, phyaddr); + +/* Reset PHY */ + mii_phyctrl |=BMCR_RESET |BMCR_ANRESTART ; +mdio_write (dev, phyaddr,MII_BMCR , mii_phyctrl); + + } +} + +static int ipg_hw_init (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +void __iomem *ioaddr = sp->ioaddr; +unsigned int i; +int rc; + +/* Read/Write and Reset EEPROM Value Jesse20040128EEPROM_VALUE */ +/* Read LED Mode Configuration from EEPROM */ + sp->LED_Mode =read_eeprom (dev,6 ); + +/* Reset all functions within the IPG. Do not assert +* RST_OUT as not compatible with some PHYs. +*/ + rc =ipg_reset (dev,IPG_RESET_MASK ); +if (rc <0 ) + goto out; + +ipg_init_mii (dev); + +/* Read MAC Address from EEPROM */ +for (i =0 ; i <3 ; i++) + sp->station_addr[i] =read_eeprom (dev,16 + i); + +for (i =0 ; i <3 ; i++) +ipg_w16 (sp->station_addr[i],STATION_ADDRESS_0 +2 *i); + +/* Set station address in ethernet_device structure. */ + dev->dev_addr[0 ] =ipg_r16 (STATION_ADDRESS_0 ) &0x00ff ; + dev->dev_addr[1 ] = (ipg_r16 (STATION_ADDRESS_0 ) &0xff00 ) >>8 ; + dev->dev_addr[2 ] =ipg_r16 (STATION_ADDRESS_1 ) &0x00ff ; + dev->dev_addr[3 ] = (ipg_r16 (STATION_ADDRESS_1 ) &0xff00 ) >>8 ; + dev->dev_addr[4 ] =ipg_r16 (STATION_ADDRESS_2 ) &0x00ff ; + dev->dev_addr[5 ] = (ipg_r16 (STATION_ADDRESS_2 ) &0xff00 ) >>8 ; +out: +return rc; +} + +static int ipg_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +int rc; + +mutex_lock (&sp->mii_mutex); + rc =generic_mii_ioctl (&sp->mii_if,if_mii (ifr), cmd,NULL ); +mutex_unlock (&sp->mii_mutex); + +return rc; +} + +static int ipg_nic_change_mtu (struct net_device *dev,int new_mtu) +{ +/* Function to accomodate changes to Maximum Transfer Unit +* (or MTU) of IPG NIC. Cannot use default function since +* the default will not allow for MTU > 1500 bytes. +*/ + +IPG_DEBUG_MSG ("_nic_change_mtu\n" ); + +/* Check that the new MTU value is between 68 (14 byte header, 46 +* byte payload, 4 byte FCS) and IPG_MAX_RXFRAME_SIZE, which +* corresponds to the MAXFRAMESIZE register in the IPG. +*/ +if ((new_mtu <68 ) || (new_mtu >IPG_MAX_RXFRAME_SIZE )) +return -EINVAL ; + + dev->mtu = new_mtu; + +return 0 ; +} + +static int ipg_get_settings (struct net_device *dev,struct ethtool_cmd *cmd) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +int rc; + +mutex_lock (&sp->mii_mutex); + rc =mii_ethtool_gset (&sp->mii_if, cmd); +mutex_unlock (&sp->mii_mutex); + +return rc; +} + +static int ipg_set_settings (struct net_device *dev,struct ethtool_cmd *cmd) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +int rc; + +mutex_lock (&sp->mii_mutex); + rc =mii_ethtool_sset (&sp->mii_if, cmd); +mutex_unlock (&sp->mii_mutex); + +return rc; +} + +static int ipg_nway_reset (struct net_device *dev) +{ +struct ipg_nic_private *sp =netdev_priv (dev); +int rc; + +mutex_lock (&sp->mii_mutex); + rc =mii_nway_restart (&sp->mii_if); +mutex_unlock (&sp->mii_mutex); + +return rc; +} + +static struct ethtool_ops ipg_ethtool_ops = { + .get_settings = ipg_get_settings, + .set_settings = ipg_set_settings, + .nway_reset = ipg_nway_reset, +}; + +static void ipg_remove (struct pci_dev *pdev) +{ +struct net_device *dev =pci_get_drvdata (pdev); +struct ipg_nic_private *sp =netdev_priv (dev); + +IPG_DEBUG_MSG ("_remove\n" ); + +/* Un-register Ethernet device. */ +unregister_netdev (dev); + +pci_iounmap (pdev, sp->ioaddr); + +pci_release_regions (pdev); + +free_netdev (dev); +pci_disable_device (pdev); +pci_set_drvdata (pdev,NULL ); +} + +static int __devinit ipg_probe (struct pci_dev *pdev, +const struct pci_device_id *id) +{ +unsigned int i = id->driver_data; +struct ipg_nic_private *sp; +struct net_device *dev; +void __iomem *ioaddr; +int rc; + + rc =pci_enable_device (pdev); +if (rc <0 ) + goto out; + +printk (KERN_INFO "%s: %s\n" ,pci_name (pdev), ipg_brand_name[i]); + +pci_set_master (pdev); + + rc =pci_set_dma_mask (pdev,DMA_40BIT_MASK ); +if (rc <0 ) { + rc =pci_set_dma_mask (pdev,DMA_32BIT_MASK ); +if (rc <0 ) { +printk (KERN_ERR "%s: DMA config failed.\n" , +pci_name (pdev)); + goto err_disable_0; + } + } + +/* +* Initialize net device. +*/ + dev =alloc_etherdev (sizeof (struct ipg_nic_private )); +if (!dev) { +printk (KERN_ERR "%s: alloc_etherdev failed\n" ,pci_name (pdev)); + rc = -ENOMEM ; + goto err_disable_0; + } + + sp =netdev_priv (dev); +spin_lock_init (&sp->lock); +mutex_init (&sp->mii_mutex); + +/* Declare IPG NIC functions for Ethernet device methods. +*/ + dev->open = &ipg_nic_open; + dev->stop = &ipg_nic_stop; + dev->hard_start_xmit = &ipg_nic_hard_start_xmit; + dev->get_stats = &ipg_nic_get_stats; + dev->set_multicast_list = &ipg_nic_set_multicast_list; + dev->do_ioctl = ipg_ioctl; + dev->tx_timeout = ipg_tx_timeout; + dev->change_mtu = &ipg_nic_change_mtu; + +SET_MODULE_OWNER (dev); +SET_NETDEV_DEV (dev, &pdev->dev); +SET_ETHTOOL_OPS (dev, &ipg_ethtool_ops); + + rc =pci_request_regions (pdev,DRV_NAME ); +if (rc) + goto err_free_dev_1; + + ioaddr =pci_iomap (pdev,1 ,pci_resource_len (pdev,1 )); +if (!ioaddr) { +printk (KERN_ERR "%s cannot map MMIO\n" ,pci_name (pdev)); + rc = -EIO ; + goto err_release_regions_2; + } + +/* Save the pointer to the PCI device information. */ + sp->ioaddr = ioaddr; + sp->pdev = pdev; + sp->dev = dev; + +INIT_DELAYED_WORK (&sp->task, ipg_reset_after_host_error); + +pci_set_drvdata (pdev, dev); + + rc =ipg_hw_init (dev); +if (rc <0 ) + goto err_unmap_3; + + rc =register_netdev (dev); +if (rc <0 ) + goto err_unmap_3; + +printk (KERN_INFO "Ethernet device registered as: %s\n" , dev->name); +out: +return rc; + +err_unmap_3: +pci_iounmap (pdev, ioaddr); +err_release_regions_2: +pci_release_regions (pdev); +err_free_dev_1: +free_netdev (dev); +err_disable_0: +pci_disable_device (pdev); + goto out; +} + +static struct pci_driver ipg_pci_driver = { + .name =IPG_DRIVER_NAME , + .id_table = ipg_pci_tbl, + .probe = ipg_probe, + .remove =__devexit_p (ipg_remove), +}; + +static int __init ipg_init_module (void ) +{ +return pci_register_driver (&ipg_pci_driver); +} + +static void __exit ipg_exit_module(void) +{ +pci_unregister_driver (&ipg_pci_driver); +} + +module_init (ipg_init_module); +module_exit (ipg_exit_module);
drivers/net/ipg.h new file +856 -0
669 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
1.3.GIT
Part of the diff is not shown here. View the raw message.