--- /dev/null
+/*PCI_DEVICE_ID_IP1000\r
+ *\r
+ * ipg.c\r
+ *\r
+ * IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver v2.01 \r
+ * by IC Plus Corp. 2003 \r
+ *\r
+ * Craig Rich\r
+ * Sundance Technology, Inc.\r
+ * 1485 Saratoga Avenue\r
+ * Suite 200\r
+ * San Jose, CA 95129\r
+ * 408 873 4117\r
+ * www.sundanceti.com\r
+ * craig_rich@sundanceti.com\r
+ *\r
+ * Rev Date Description\r
+ * --------------------------------------------------------------\r
+ * 0.1 11/8/99 Initial revision work begins.\r
+ *\r
+ * 0.2 11/12/99 Basic operation achieved, continuing work.\r
+ *\r
+ * 0.3 11/19/99 MAC Loop Back for sync problem testing.\r
+ *\r
+ * 0.4 12/22/99 ioctl for diagnotic program 'hunter' support.\r
+ *\r
+ * 0.5 4/13/00 Updates to\r
+ *\r
+ * 0.6 6/14/00 Slight correction to handling TFDDONE, and\r
+ * preservation of PHYCTRL polarity bits.\r
+ *\r
+ * 0.7 7/27/00 Modifications to accomodate triple speed\r
+ * autonegotiation. Also change to ioctl routine\r
+ * to handle unknown PHY address.\r
+ *\r
+ * 0.8 8/11/00 Added change_mtu function.\r
+ *\r
+ * 0.9 8/15/00 Corrected autonegotiation resolution.\r
+ *\r
+ * 0.10 8/30/00 Changed constants to use IPG in place\r
+ * of RIO. Also, removed most of debug\r
+ * code in preparation for production release.\r
+ *\r
+ * 0.11 8/31/00 Utilize 64 bit data types where appropriate.\r
+ *\r
+ * 0.12 9/1/00 Move some constants to include file and utilize\r
+ * RxDMAInt register.\r
+ *\r
+ * 0.13 10/31/00 Several minor modifications to improve stability.\r
+ *\r
+ * 0.14 11/28/00 Added call to nic_tx_free if TFD not available.\r
+ *\r
+ * 0.15 12/5/00 Corrected problem with receive errors, always set\r
+ * receive buffer address to NULL. Release RX buffers\r
+ * on errors.\r
+ *\r
+ * 0.16 12/20/00 Corrected autoneg resolution issue, must detect\r
+ * speed via PHYCTRL register. Also, perform only 1\r
+ * loop in the nic_txcleanup routine.\r
+ *\r
+ * 0.17 2/7/01 Changed all references of ST2021 to IPG.\r
+ * When next TFD not available, return -ENOMEM instead\r
+ * of 0. Removed references to RUBICON.\r
+ *\r
+ * 0.18 2/14/01 Corrected problem when unexpected jumbo frames are\r
+ * received (now dropped properly.) Changed\r
+ * "DROP_ON_ERRORS" breaking out Ethernet errors and\r
+ * TCP/IP errors serparately. Corrected Gigabit\r
+ * copper PAUSE autonegotiation.\r
+ *\r
+ * 0.19 2/22/01 Changed interrupt handling of RFD_LIST_END,\r
+ * INT_REQUESTED, and RX_DMA_COMPLETE. Masked off\r
+ * RMON statistics and unused MIB statistics.\r
+ * Make sure *all* statistics are accounted for\r
+ * (either masked or read in get_stats) to avoid\r
+ * perpetual UpdateStats interrupt from causing\r
+ * driver to crash.\r
+ *\r
+ * 0.20 3/2/01 Corrected error in nic_stop. Need to set\r
+ * TxBuff[] = NULL after freeing TxBuff and\r
+ * RxBuff[] = NULL after freeing RxBuff.\r
+ *\r
+ * 0.21 3/5/01 Correct 10/100Mbit PAUSE autonegotiation.\r
+ *\r
+ * 0.22 3/16/01 Used TxDMAIndicate for 100/1000Mbps modes. Add\r
+ * "TFD unavailable" and "RFD list end" counters\r
+ * to assist with performance measurement. Added\r
+ * check for maxtfdcnt != 0 to while loop within\r
+ * txcleanup.\r
+ *\r
+ * 0.23 3/22/01 Set the CurrentTxFrameID to 1 upon detecting\r
+ * a TxDMAComplete to reduce the number of TxDMAComplete.\r
+ * Also, indicate IP/TCP/UDP checksum is unneseccary\r
+ * if IPG indicates checksum validates.\r
+ *\r
+ * 0.24 3/23/01 Changed the txfree routine, eliminating the margin\r
+ * between the last freed TFD and the current TFD.\r
+ *\r
+ * 0.25 4/3/01 Corrected errors in config_autoneg to deal with\r
+ * fiber flag properly.\r
+ *\r
+ * 0.26 5/1/01 Port for operation with Linux 2.2 or 2.4 kernel.\r
+ *\r
+ * 0.27 5/22/01 Cleaned up some extraneous comments.\r
+ *\r
+ * 0.28 6/20/01 Added auto IP, TCP, and UDP checksum addition\r
+ * on transmit based on compilation option.\r
+ *\r
+ * 0.29 7/26/01 Comment out #include <asm/spinlock.h> from ipg.h\r
+ * for compatibility with RedHat 7.1. Unkown reason.\r
+ *\r
+ * 0.30 8/10/01 Added debug message to each function, print function\r
+ * name when entered. Added DEBUGCTRL register bit 5 for\r
+ * Rx DMA Poll Now bug work around. Added ifdef IPG_DEBUG\r
+ * flags to IPG_TFDlistunabvail and IPG_RFDlistend\r
+ * counters. Removed clearing of sp->stat struct from\r
+ * nic_open and added check in get_stats to make sure\r
+ * NIC is initialized before reading statistic registers.\r
+ * Corrected erroneous MACCTRL setting for Fiber based\r
+ * 10/100 boards. Corrected storage of phyctrlpolarity\r
+ * variable.\r
+ *\r
+ * 0.31 8/13/01 Incorporate STI or TMI fiber based NIC detection.\r
+ * Corrected problem with _pciremove_linux2_4 routine.\r
+ * Corrected setting of IP/TCP/UDP checksumming on receive\r
+ * and transmit.\r
+ *\r
+ * 0.32 8/15/01 Changed the tmi_fiber_detect routine.\r
+ *\r
+ * 0.33 8/16/01 Changed PHY reset method in nic_open routine. Added\r
+ * a chip reset in nic_stop to shut down the IPG.\r
+ *\r
+ * 0.34 9/5/01 Corrected some misuage of dev_kfree_skb.\r
+ *\r
+ * 0.35 10/30/01 Unmap register space (IO or memory) in the nic_stop\r
+ * routine instead of in the cleanup or remove routines.\r
+ * Corrects driver up/down/up problem when using IO\r
+ * register mapping.\r
+ *\r
+ * 0.36 10/31/01 Modify the constant IPG_FRAMESBETWEENTXDMACOMPLETES\r
+ * from 0x10 to 1.\r
+ * 0.37 11/05/03 Modify the IPG_PHY_1000BASETCONTROL\r
+ * in IP1000A this register is without 1000BPS Ability by default\r
+ * so enable 1000BPS ability before PHY RESET/RESTART_AN\r
+ * 0.38 11/05/03 update ipg_config_autoneg routine\r
+ * 0.39 11/05/03 add Vendor_ID=13F0/Device_ID=1023 into support_cards\r
+ * 2.05 10/16/04 Remove IPG_IE_RFD_LIST_END for pass SmartBit test.\r
+ * (see 20041019Jesse_For_SmartBit.)\r
+ * 2.06 10/27/04 Support for kernel 2.6.x\r
+ * 2.06a 11/03/04 remove some compile warring message.\r
+ * 2.09b 06/03/05 Support 4k jumbo (IC Plus, Jesse)\r
+ * 2.09d 06/22/05 Support 10k jumbo, more than 4k will using copy mode (IC Plus, Jesse)\r
+ */\r
+#define JUMBO_FRAME_4k_ONLY\r
+enum {\r
+ netdev_io_size = 128\r
+};\r
+\r
+#include "ipg.h"\r
+#define DRV_NAME "ipg"\r
+/* notify kernel that this software is under GNU Public License */\r
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) )\r
+ MODULE_LICENSE("GPL");\r
+#endif\r
+\r
+/* Function prototypes. */\r
+u16 read_phy_register(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int phy_address, int phy_register);\r
+//int ipg_reset(u32 baseaddr, u32 resetflags); //JES20040127EEPROM: change type of param1\r
+int ipg_reset(IPG_DEVICE_TYPE *ipg_ethernet_device, u32 resetflags);\r
+int ipg_io_config(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+void ipg_interrupt_handler(int ipg_irq, void *device_instance,\r
+ struct pt_regs *regs);\r
+#else\r
+static irqreturn_t ipg_interrupt_handler(int ipg_irq, void *device_instance,\r
+ struct pt_regs *regs);\r
+#endif\r
+\r
+void ipg_nic_txcleanup(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+void ipg_nic_txfree(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_nic_open(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_nic_stop(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_nic_hard_start_xmit(struct sk_buff *skb,\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+IPG_STATS_TYPE* ipg_nic_get_stats(IPG_DEVICE_TYPE\r
+ *ipg_ethernet_device);\r
+void ipg_nic_set_multicast_list(IPG_DEVICE_TYPE *dev);\r
+int ipg_nic_init(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_nic_rx(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_nic_rxrestore(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int init_rfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int init_tfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_get_rxbuff(IPG_DEVICE_TYPE *ipg_ethernet_device, int rfd);\r
+int ipg_sti_fiber_detect(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+int ipg_tmi_fiber_detect(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int phyaddr);\r
+int ipg_config_autoneg(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+unsigned ether_crc_le(int length, unsigned char *data);\r
+int ipg_nic_do_ioctl(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ struct ifreq *req, int cmd);\r
+int ipg_nic_change_mtu(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int new_mtu);\r
+\r
+#ifdef IPG_LINUX2_2\r
+int ipg_pcibussearch_linux2_2(void);\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+int ipg_pciprobe_linux2_4(struct pci_dev*, const struct pci_device_id*);\r
+void ipg_pciremove_linux2_4(struct pci_dev*);\r
+#endif\r
+\r
+void bSetPhyDefaultParam(unsigned char Rev,\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device,int phy_address);\r
+\r
+//JES20040127EEPROM:Add three new function\r
+int read_eeprom(IPG_DEVICE_TYPE *ipg_ethernet_device, int eep_addr);\r
+void Set_LED_Mode(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+void Set_PHYSet(IPG_DEVICE_TYPE *ipg_ethernet_device);\r
+/* End function prototypes. */\r
+\r
+#ifdef IPG_DEBUG\r
+void ipg_dump_rfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ u32 baseaddr;\r
+ int i;\r
+ u32 offset;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_dump_rfdlist\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ printk(KERN_INFO "CurrentRFD = %2.2x\n", sp->CurrentRFD);\r
+ printk(KERN_INFO "LastRestoredRxBuff = %2.2x\n",\r
+ sp->LastRestoredRxBuff);\r
+#ifdef IPG_LINUX2_2\r
+ printk(KERN_INFO "RFDList start address = %16.16lx\n",\r
+ (unsigned long int)(IPG_HOST2BUS_MAP(sp->RFDList)));\r
+#endif\r
+#ifdef IPG_LINUX2_4\r
+ printk(KERN_INFO "RFDList start address = %16.16lx\n",\r
+ (unsigned long int)(sp->RFDListDMAhandle));\r
+#endif\r
+ printk(KERN_INFO "RFDListPtr register = %8.8x%8.8x\n",\r
+ IPG_READ_RFDLISTPTR1(baseaddr),\r
+ IPG_READ_RFDLISTPTR0(baseaddr));\r
+\r
+ for(i=0;i<IPG_RFDLIST_LENGTH;i++)\r
+ {\r
+ offset = (u32)(&sp->RFDList[i].RFDNextPtr)\r
+ - (u32)(sp->RFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x RFDNextPtr = %16.16lx\n", i,\r
+ offset, (unsigned long int) sp->RFDList[i].RFDNextPtr);\r
+ offset = (u32)(&sp->RFDList[i].RFS)\r
+ - (u32)(sp->RFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x RFS = %16.16lx\n", i,\r
+ offset, (unsigned long int)sp->RFDList[i].RFS);\r
+ offset = (u32)(&sp->RFDList[i].FragInfo)\r
+ - (u32)(sp->RFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x FragInfo = %16.16lx\n", i,\r
+ offset, (unsigned long int)sp->RFDList[i].FragInfo);\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+void ipg_dump_tfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ u32 baseaddr;\r
+ int i;\r
+ u32 offset;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_dump_tfdlist\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ printk(KERN_INFO "CurrentTFD = %2.2x\n", sp->CurrentTFD);\r
+ printk(KERN_INFO "LastFreedTxBuff = %2.2x\n",\r
+ sp->LastFreedTxBuff);\r
+#ifdef IPG_LINUX2_2\r
+ printk(KERN_INFO "TFDList start address = %16.16lx\n",\r
+ (unsigned long int)(IPG_HOST2BUS_MAP(sp->TFDList)));\r
+#endif\r
+#ifdef IPG_LINUX2_4\r
+ printk(KERN_INFO "TFDList start address = %16.16lx\n",\r
+ (unsigned long int)(sp->TFDListDMAhandle));\r
+#endif\r
+ printk(KERN_INFO "TFDListPtr register = %8.8x%8.8x\n",\r
+ IPG_READ_TFDLISTPTR1(baseaddr),\r
+ IPG_READ_TFDLISTPTR0(baseaddr));\r
+\r
+ for(i=0;i<IPG_TFDLIST_LENGTH;i++)\r
+ {\r
+ offset = (u32)(&sp->TFDList[i].TFDNextPtr)\r
+ - (u32)(sp->TFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x TFDNextPtr = %16.16lx\n", i,\r
+ offset, (unsigned long int)sp->TFDList[i].TFDNextPtr);\r
+\r
+ offset = (u32)(&sp->TFDList[i].TFC)\r
+ - (u32)(sp->TFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x TFC = %16.16lx\n", i,\r
+ offset, (unsigned long int)sp->TFDList[i].TFC);\r
+ offset = (u32)(&sp->TFDList[i].FragInfo)\r
+ - (u32)(sp->TFDList);\r
+ printk(KERN_INFO "%2.2x %4.4x FragInfo = %16.16lx\n", i,\r
+ offset, (unsigned long int)sp->TFDList[i].FragInfo);\r
+ }\r
+\r
+ return;\r
+}\r
+#else /* Not in debug mode. */\r
+#endif\r
+\r
+void send_three_state(u32 baseaddr, u8 phyctrlpolarity)\r
+{\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ (IPG_PC_MGMTDATA & 0) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_HI |\r
+ (IPG_PC_MGMTDATA & 0) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+ return;\r
+}\r
+\r
+void send_end(u32 baseaddr, u8 phyctrlpolarity)\r
+{\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ (IPG_PC_MGMTDATA & 0) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+ return;\r
+}\r
+\r
+\r
+u16 read_phy_bit(u32 baseaddr, u8 phyctrlpolarity)\r
+{ u16 bit_data;\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ bit_data=((IPG_READ_PHYCTRL(baseaddr) & IPG_PC_MGMTDATA) >> 1) & 1;\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_HI |\r
+ phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+ return bit_data;\r
+}\r
+\r
+u16 read_phy_register(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int phy_address, int phy_register)\r
+{\r
+ /* Read a register from the Physical Layer device located\r
+ * on the IPG NIC, using the IPG PHYCTRL register.\r
+ */\r
+\r
+ u32 baseaddr;\r
+ int i;\r
+ int j;\r
+ int fieldlen[8];\r
+ u32 field[8];\r
+ u8 databit;\r
+ u8 phyctrlpolarity;\r
+\r
+ IPG_DEBUG_MSG("read_phy_register\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ /* The GMII mangement frame structure for a read is as follows:\r
+ *\r
+ * |Preamble|st|op|phyad|regad|ta| data |idle|\r
+ * |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z |\r
+ *\r
+ * <32 1s> = 32 consecutive logic 1 values\r
+ * A = bit of Physical Layer device address (MSB first)\r
+ * R = bit of register address (MSB first)\r
+ * z = High impedance state\r
+ * D = bit of read data (MSB first)\r
+ *\r
+ * Transmission order is 'Preamble' field first, bits transmitted\r
+ * left to right (first to last).\r
+ */\r
+\r
+ field[0] = GMII_PREAMBLE;\r
+ fieldlen[0] = 32; /* Preamble */\r
+ field[1] = GMII_ST;\r
+ fieldlen[1] = 2; /* ST */\r
+ field[2] = GMII_READ;\r
+ fieldlen[2] = 2; /* OP */\r
+ field[3] = phy_address;\r
+ fieldlen[3] = 5; /* PHYAD */\r
+ field[4] = phy_register;\r
+ fieldlen[4] = 5; /* REGAD */\r
+ field[5] = 0x0000;\r
+ fieldlen[5] = 2; /* TA */\r
+ field[6] = 0x0000;\r
+ fieldlen[6] = 16; /* DATA */\r
+ field[7] = 0x0000;\r
+ fieldlen[7] = 1; /* IDLE */\r
+\r
+ /* Store the polarity values of PHYCTRL. */\r
+ phyctrlpolarity = IPG_READ_PHYCTRL(baseaddr) &\r
+ (IPG_PC_DUPLEX_POLARITY |\r
+ IPG_PC_LINK_POLARITY);\r
+\r
+ /* Create the Preamble, ST, OP, PHYAD, and REGAD field. */\r
+ for(j=0; j<5; j++)\r
+ for(i=0; i<fieldlen[j]; i++)\r
+ {\r
+ /* For each variable length field, the MSB must be\r
+ * transmitted first. Rotate through the field bits,\r
+ * starting with the MSB, and move each bit into the\r
+ * the 1st (2^1) bit position (this is the bit position\r
+ * corresponding to the MgmtData bit of the PhyCtrl\r
+ * register for the IPG).\r
+ *\r
+ * Example: ST = 01;\r
+ *\r
+ * First write a '0' to bit 1 of the PhyCtrl\r
+ * register, then write a '1' to bit 1 of the\r
+ * PhyCtrl register.\r
+ *\r
+ * To do this, right shift the MSB of ST by the value:\r
+ * [field length - 1 - #ST bits already written]\r
+ * then left shift this result by 1.\r
+ */\r
+ databit = (field[j] >> (fieldlen[j] - 1 - i)) << 1;\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ (IPG_PC_MGMTDATA & databit) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_HI |\r
+ (IPG_PC_MGMTDATA & databit) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+ }\r
+\r
+ send_three_state(baseaddr, phyctrlpolarity);\r
+\r
+ read_phy_bit(baseaddr, phyctrlpolarity);\r
+\r
+ /* For a read cycle, the bits for the next two fields (TA and\r
+ * DATA) are driven by the PHY (the IPG reads these bits).\r
+ */\r
+// for(j=6; j<8; j++)\r
+ for(i=0; i<fieldlen[6]; i++)\r
+ {\r
+ field[6] |= (read_phy_bit(baseaddr, phyctrlpolarity) << (fieldlen[6]- 1 - i));\r
+\r
+ }\r
+\r
+ send_three_state(baseaddr, phyctrlpolarity);\r
+ send_three_state(baseaddr, phyctrlpolarity);\r
+ send_three_state(baseaddr, phyctrlpolarity);\r
+ send_end(baseaddr, phyctrlpolarity);\r
+\r
+ /* Return the value of the DATA field. */\r
+ return field[6];\r
+}\r
+\r
+void write_phy_register(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int phy_address, int phy_register, u16 writeval)\r
+{\r
+ /* Write to a register from the Physical Layer device located\r
+ * on the IPG NIC, using the IPG PHYCTRL register.\r
+ */\r
+\r
+ u32 baseaddr;\r
+ int i;\r
+ int j;\r
+ int fieldlen[8];\r
+ u32 field[8];\r
+ u8 databit;\r
+ u8 phyctrlpolarity;\r
+\r
+ IPG_DEBUG_MSG("write_phy_register\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ /* The GMII mangement frame structure for a read is as follows:\r
+ *\r
+ * |Preamble|st|op|phyad|regad|ta| data |idle|\r
+ * |< 32 1s>|01|10|AAAAA|RRRRR|z0|DDDDDDDDDDDDDDDD|z |\r
+ *\r
+ * <32 1s> = 32 consecutive logic 1 values\r
+ * A = bit of Physical Layer device address (MSB first)\r
+ * R = bit of register address (MSB first)\r
+ * z = High impedance state\r
+ * D = bit of write data (MSB first)\r
+ *\r
+ * Transmission order is 'Preamble' field first, bits transmitted\r
+ * left to right (first to last).\r
+ */\r
+\r
+ field[0] = GMII_PREAMBLE;\r
+ fieldlen[0] = 32; /* Preamble */\r
+ field[1] = GMII_ST;\r
+ fieldlen[1] = 2; /* ST */\r
+ field[2] = GMII_WRITE;\r
+ fieldlen[2] = 2; /* OP */\r
+ field[3] = phy_address;\r
+ fieldlen[3] = 5; /* PHYAD */\r
+ field[4] = phy_register;\r
+ fieldlen[4] = 5; /* REGAD */\r
+ field[5] = 0x0002;\r
+ fieldlen[5] = 2; /* TA */\r
+ field[6] = writeval;\r
+ fieldlen[6] = 16; /* DATA */\r
+ field[7] = 0x0000;\r
+ fieldlen[7] = 1; /* IDLE */\r
+\r
+ /* Store the polarity values of PHYCTRL. */\r
+ phyctrlpolarity = IPG_READ_PHYCTRL(baseaddr) &\r
+ (IPG_PC_DUPLEX_POLARITY |\r
+ IPG_PC_LINK_POLARITY);\r
+\r
+ /* Create the Preamble, ST, OP, PHYAD, and REGAD field. */\r
+ for(j=0; j<7; j++)\r
+ for(i=0; i<fieldlen[j]; i++)\r
+ {\r
+ /* For each variable length field, the MSB must be\r
+ * transmitted first. Rotate through the field bits,\r
+ * starting with the MSB, and move each bit into the\r
+ * the 1st (2^1) bit position (this is the bit position\r
+ * corresponding to the MgmtData bit of the PhyCtrl\r
+ * register for the IPG).\r
+ *\r
+ * Example: ST = 01;\r
+ *\r
+ * First write a '0' to bit 1 of the PhyCtrl\r
+ * register, then write a '1' to bit 1 of the\r
+ * PhyCtrl register.\r
+ *\r
+ * To do this, right shift the MSB of ST by the value:\r
+ * [field length - 1 - #ST bits already written]\r
+ * then left shift this result by 1.\r
+ */\r
+ databit = (field[j] >> (fieldlen[j] - 1 - i)) << 1;\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ (IPG_PC_MGMTDATA & databit) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_HI |\r
+ (IPG_PC_MGMTDATA & databit) |\r
+ IPG_PC_MGMTDIR | phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+ }\r
+\r
+ /* The last cycle is a tri-state, so read from the PHY.\r
+ */\r
+ for(j=7; j<8; j++)\r
+ for(i=0; i<fieldlen[j]; i++)\r
+ {\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_LO |\r
+ phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ field[j] |= ((IPG_READ_PHYCTRL(baseaddr) &\r
+ IPG_PC_MGMTDATA) >> 1)\r
+ << (fieldlen[j] - 1 - i);\r
+\r
+ IPG_WRITE_PHYCTRL(baseaddr, IPG_PC_MGMTCLK_HI |\r
+ phyctrlpolarity);\r
+\r
+ mdelay(IPG_PC_PHYCTRLWAIT);\r
+\r
+ }\r
+\r
+ return;\r
+}\r
+//int ipg_reset(u32 baseaddr, u32 resetflags) //JES20040127EEPROM: change type of param1\r
+int ipg_reset(IPG_DEVICE_TYPE *ipg_ethernet_device, u32 resetflags)\r
+{\r
+ /* Assert functional resets via the IPG AsicCtrl\r
+ * register as specified by the 'resetflags' input\r
+ * parameter.\r
+ */\r
+ u32 baseaddr;//JES20040127EEPROM: \r
+ int timeout_count;\r
+ baseaddr = ipg_ethernet_device->base_addr;//JES20040127EEPROM:\r
+\r
+ IPG_DEBUG_MSG("_reset\n");\r
+\r
+ IPG_WRITE_ASICCTRL(baseaddr, IPG_READ_ASICCTRL(baseaddr) |\r
+ resetflags);\r
+\r
+ /* Wait until IPG reset is complete. */\r
+ timeout_count = 0;\r
+\r
+ /* Delay added to account for problem with 10Mbps reset. */\r
+ mdelay(IPG_AC_RESETWAIT);\r
+\r
+ while (IPG_AC_RESET_BUSY & IPG_READ_ASICCTRL(baseaddr))\r
+ {\r
+ mdelay(IPG_AC_RESETWAIT);\r
+ timeout_count++;\r
+ if (timeout_count > IPG_AC_RESET_TIMEOUT)\r
+ return -ETIME;\r
+ }\r
+ /* Set LED Mode in Asic Control JES20040127EEPROM */\r
+ Set_LED_Mode(ipg_ethernet_device);\r
+ \r
+ /* Set PHYSet Register Value JES20040127EEPROM */\r
+ Set_PHYSet(ipg_ethernet_device);\r
+ return 0;\r
+}\r
+\r
+int ipg_sti_fiber_detect(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Determine if NIC is fiber based by reading the PhyMedia\r
+ * bit in the AsicCtrl register.\r
+ */\r
+\r
+ u32 asicctrl;\r
+ u32 baseaddr;\r
+\r
+ IPG_DEBUG_MSG("_sti_fiber_detect\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ asicctrl = IPG_READ_ASICCTRL(baseaddr);\r
+\r
+ if (asicctrl & IPG_AC_PHY_MEDIA)\r
+ {\r
+ /* Fiber NIC. */\r
+ return 1;\r
+ } else\r
+ {\r
+ /* Not a fiber NIC. */\r
+ return 0;\r
+ }\r
+}\r
+\r
+int ipg_tmi_fiber_detect(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int phyaddr)\r
+{\r
+ /* Determine if NIC is fiber based by reading the ID register\r
+ * of the PHY and the GMII address.\r
+ */\r
+\r
+ u16 phyid;\r
+ u32 baseaddr;\r
+\r
+ IPG_DEBUG_MSG("_tmi_fiber_detect\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ phyid = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_ID_1);\r
+\r
+ IPG_DEBUG_MSG("PHY ID = %x\n", phyid);\r
+\r
+ /* We conclude the mode is fiber if the GMII address\r
+ * is 0x1 and the PHY ID is 0x0000.\r
+ */\r
+ if ((phyaddr == 0x1) && (phyid == 0x0000))\r
+ {\r
+ /* Fiber NIC. */\r
+ return 1;\r
+ } else\r
+ {\r
+ /* Not a fiber NIC. */\r
+ return 0;\r
+ }\r
+}\r
+\r
+int ipg_find_phyaddr(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Find the GMII PHY address. */\r
+\r
+ int i;\r
+ int phyaddr;\r
+ u32 status;\r
+\r
+ for(i=0;i<32;i++)\r
+ {\r
+ /* Search for the correct PHY address among 32 possible. */\r
+ phyaddr = (IPG_NIC_PHY_ADDRESS + i) % 32;\r
+\r
+ /* 10/22/03 Grace change verify from GMII_PHY_STATUS to\r
+ GMII_PHY_ID1\r
+ */\r
+\r
+ status = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_STATUS);\r
+\r
+ // if (status != 0xFFFF)\r
+ if ((status != 0xFFFF) && (status != 0))\r
+ return phyaddr;\r
+\r
+ /*----------------------------------------------------\r
+ status = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_ID_1);\r
+ if (status == 0x243) {\r
+ printk("PHY Addr = %x\n", phyaddr);\r
+ return phyaddr;\r
+ }\r
+ -------------------------------------------------*/\r
+ }\r
+\r
+ return -1;\r
+}\r
+\r
+#ifdef NOTGRACE\r
+int ipg_config_autoneg(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Configure IPG based on result of IEEE 802.3 PHY\r
+ * auto-negotiation.\r
+ */\r
+\r
+ int phyaddr = 0;\r
+ u8 phyctrl;\r
+ u32 asicctrl;\r
+ u32 baseaddr;\r
+ u16 status = 0;\r
+ u16 advertisement;\r
+ u16 linkpartner_ability;\r
+ u16 gigadvertisement;\r
+ u16 giglinkpartner_ability;\r
+ u16 techabilities;\r
+ int fiber;\r
+ int gig;\r
+ int fullduplex;\r
+ int txflowcontrol;\r
+ int rxflowcontrol;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_config_autoneg\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ asicctrl = IPG_READ_ASICCTRL(baseaddr);\r
+ phyctrl = IPG_READ_PHYCTRL(baseaddr);\r
+\r
+ /* Set flags for use in resolving auto-negotation, assuming\r
+ * non-1000Mbps, half duplex, no flow control.\r
+ */\r
+ fiber = 0;\r
+ fullduplex = 0;\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ gig = 0;\r
+\r
+ /* To accomodate a problem in 10Mbps operation,\r
+ * set a global flag if PHY running in 10Mbps mode.\r
+ */\r
+ sp->tenmbpsmode = 0;\r
+\r
+ printk("Link speed = ");\r
+\r
+ /* Determine actual speed of operation. */\r
+ switch (phyctrl & IPG_PC_LINK_SPEED)\r
+ {\r
+ case IPG_PC_LINK_SPEED_10MBPS :\r
+ printk("10Mbps.\n");\r
+ printk(KERN_INFO "%s: 10Mbps operational mode enabled.\n",ipg_ethernet_device->name);\r
+ sp->tenmbpsmode = 1;\r
+ break;\r
+ case IPG_PC_LINK_SPEED_100MBPS :\r
+ printk("100Mbps.\n");\r
+ break;\r
+ case IPG_PC_LINK_SPEED_1000MBPS :\r
+ printk("1000Mbps.\n");\r
+ gig = 1;\r
+ break;\r
+ default : printk("undefined!\n");\r
+ }\r
+\r
+#ifndef IPG_TMI_FIBER_DETECT\r
+ fiber = ipg_sti_fiber_detect(ipg_ethernet_device);\r
+\r
+ /* Determine if auto-negotiation resolution is necessary.\r
+ * First check for fiber based media 10/100 media.\r
+ */\r
+ if ((fiber == 1) && (asicctrl &\r
+ (IPG_AC_PHY_SPEED10 | IPG_AC_PHY_SPEED100)))\r
+ {\r
+ printk(KERN_INFO "%s: Fiber based PHY, setting full duplex, no flow control.\n", ipg_ethernet_device->name);\r
+ return -EILSEQ;\r
+ IPG_WRITE_MACCTRL(baseaddr, (IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_DUPLEX_SELECT_FD) &\r
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE &\r
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+\r
+ return 0;\r
+ }\r
+#endif\r
+\r
+ /* Determine if PHY is auto-negotiation capable. */\r
+ phyaddr = ipg_find_phyaddr(ipg_ethernet_device);\r
+\r
+ if (phyaddr == -1)\r
+ {\r
+ printk(KERN_INFO "%s: Error on read to GMII/MII Status register.\n",ipg_ethernet_device->name);\r
+ return -EILSEQ;\r
+ }\r
+\r
+ IPG_DEBUG_MSG("GMII/MII PHY address = %x\n", phyaddr);\r
+\r
+ status = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_STATUS);\r
+\r
+ printk("PHYStatus = %x \n", status);\r
+ if ((status & GMII_PHY_STATUS_AUTONEG_ABILITY) == 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error PHY unable to perform auto-negotiation.\n",\r
+ ipg_ethernet_device->name);\r
+ return -EILSEQ;\r
+ }\r
+\r
+ advertisement = read_phy_register(ipg_ethernet_device, phyaddr,\r
+ GMII_PHY_AUTONEGADVERTISEMENT);\r
+ linkpartner_ability = read_phy_register(ipg_ethernet_device, phyaddr,\r
+ GMII_PHY_AUTONEGLINKPARTABILITY);\r
+\r
+ printk("PHYadvertisement=%x LinkPartner=%x \n",advertisement,linkpartner_ability);\r
+ if ((advertisement == 0xFFFF) || (linkpartner_ability == 0xFFFF))\r
+ {\r
+ printk(KERN_INFO "%s: Error on read to GMII/MII registers 4 and/or 5.\n", ipg_ethernet_device->name);\r
+ return -EILSEQ;\r
+ }\r
+\r
+#ifdef IPG_TMI_FIBER_DETECT\r
+ fiber = ipg_tmi_fiber_detect(ipg_ethernet_device, phyaddr);\r
+#endif\r
+\r
+ /* Resolve full/half duplex if 1000BASE-X. */\r
+ if ((gig == 1) && (fiber == 1))\r
+ {\r
+ /* Compare the full duplex bits in the GMII registers\r
+ * for the local device, and the link partner. If these\r
+ * bits are logic 1 in both registers, configure the\r
+ * IPG for full duplex operation.\r
+ */\r
+ if ((advertisement & GMII_PHY_ADV_FULL_DUPLEX) ==\r
+ (linkpartner_ability & GMII_PHY_ADV_FULL_DUPLEX))\r
+ {\r
+ fullduplex = 1;\r
+\r
+ /* In 1000BASE-X using IPG's internal PCS\r
+ * layer, so write to the GMII duplex bit.\r
+ */\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_CONTROL,\r
+ read_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_CONTROL) |\r
+ GMII_PHY_CONTROL_FULL_DUPLEX);\r
+\r
+ } else\r
+ {\r
+ fullduplex = 0;\r
+\r
+ /* In 1000BASE-X using IPG's internal PCS\r
+ * layer, so write to the GMII duplex bit.\r
+ */\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_CONTROL,\r
+ read_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_CONTROL) &\r
+ ~GMII_PHY_CONTROL_FULL_DUPLEX);\r
+ }\r
+ }\r
+\r
+ /* Resolve full/half duplex if 1000BASE-T. */\r
+ if ((gig == 1) && (fiber == 0))\r
+ {\r
+ /* Read the 1000BASE-T "Control" and "Status"\r
+ * registers which represent the advertised and\r
+ * link partner abilities exchanged via next page\r
+ * transfers.\r
+ */\r
+ gigadvertisement = read_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_1000BASETCONTROL);\r
+ giglinkpartner_ability = read_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ GMII_PHY_1000BASETSTATUS);\r
+\r
+ /* Compare the full duplex bits in the 1000BASE-T GMII\r
+ * registers for the local device, and the link partner.\r
+ * If these bits are logic 1 in both registers, configure\r
+ * the IPG for full duplex operation.\r
+ */\r
+ if ((gigadvertisement & GMII_PHY_1000BASETCONTROL_FULL_DUPLEX) &&\r
+ (giglinkpartner_ability & GMII_PHY_1000BASETSTATUS_FULL_DUPLEX))\r
+ {\r
+ fullduplex = 1;\r
+ } else\r
+ {\r
+ fullduplex = 0;\r
+ }\r
+ }\r
+\r
+ /* Resolve full/half duplex for 10/100BASE-T. */\r
+ if (gig == 0)\r
+ {\r
+ /* Autonegotiation Priority Resolution algorithm, as defined in\r
+ * IEEE 802.3 Annex 28B.\r
+ */\r
+ if (((advertisement & MII_PHY_SELECTORFIELD) ==\r
+ MII_PHY_SELECTOR_IEEE8023) &&\r
+ ((linkpartner_ability & MII_PHY_SELECTORFIELD) ==\r
+ MII_PHY_SELECTOR_IEEE8023))\r
+ {\r
+ techabilities = (advertisement & linkpartner_ability &\r
+ MII_PHY_TECHABILITYFIELD);\r
+\r
+ fullduplex = 0;\r
+\r
+ /* 10BASE-TX half duplex is lowest priority. */\r
+ if (techabilities & MII_PHY_TECHABILITY_10BT)\r
+ {\r
+ fullduplex = 0;\r
+ }\r
+\r
+ if (techabilities & MII_PHY_TECHABILITY_10BTFD)\r
+ {\r
+ fullduplex = 1;\r
+ }\r
+\r
+ if (techabilities & MII_PHY_TECHABILITY_100BTX)\r
+ {\r
+ fullduplex = 0;\r
+ }\r
+\r
+ if (techabilities & MII_PHY_TECHABILITY_100BT4)\r
+ {\r
+ fullduplex = 0;\r
+ }\r
+\r
+ /* 100BASE-TX half duplex is highest priority. */ //Sorbica full duplex ?\r
+ if (techabilities & MII_PHY_TECHABILITY_100BTXFD)\r
+ {\r
+ fullduplex = 1;\r
+ }\r
+\r
+ if (fullduplex == 1)\r
+ {\r
+ /* If in full duplex mode, determine if PAUSE\r
+ * functionality is supported by the local\r
+ * device, and the link partner.\r
+ */\r
+ if (techabilities & MII_PHY_TECHABILITY_PAUSE)\r
+ {\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 1;\r
+ }\r
+ else\r
+ {\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /* If in 1000Mbps, fiber, and full duplex mode, resolve\r
+ * 1000BASE-X PAUSE capabilities. */\r
+ if ((fullduplex == 1) && (fiber == 1) && (gig == 1))\r
+ {\r
+ /* In full duplex mode, resolve PAUSE\r
+ * functionality.\r
+ */\r
+ switch(((advertisement & GMII_PHY_ADV_PAUSE) >> 5) |\r
+ ((linkpartner_ability & GMII_PHY_ADV_PAUSE) >> 7))\r
+ {\r
+ case 0x7 :\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 0;\r
+ break;\r
+\r
+ case 0xA : case 0xB: case 0xE: case 0xF:\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 1;\r
+ break;\r
+\r
+ case 0xD :\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 1;\r
+ break;\r
+\r
+ default :\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ }\r
+ }\r
+\r
+ /* If in 1000Mbps, non-fiber, full duplex mode, resolve\r
+ * 1000BASE-T PAUSE capabilities. */\r
+ if ((fullduplex == 1) && (fiber == 0) && (gig == 1))\r
+ {\r
+ /* Make sure the PHY is advertising we are PAUSE\r
+ * capable.\r
+ */\r
+ if (!(advertisement & (MII_PHY_TECHABILITY_PAUSE |\r
+ MII_PHY_TECHABILITY_ASM_DIR)))\r
+ {\r
+ /* PAUSE is not being advertised. Advertise\r
+ * PAUSE and restart auto-negotiation.\r
+ */\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ MII_PHY_AUTONEGADVERTISEMENT,\r
+ (advertisement |\r
+ MII_PHY_TECHABILITY_PAUSE |\r
+ MII_PHY_TECHABILITY_ASM_DIR));\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ MII_PHY_CONTROL,\r
+ MII_PHY_CONTROL_RESTARTAN);\r
+\r
+ return -EAGAIN;\r
+ }\r
+\r
+ /* In full duplex mode, resolve PAUSE\r
+ * functionality.\r
+ */\r
+ switch(((advertisement &\r
+ MII_PHY_TECHABILITY_PAUSE_FIELDS) >> 0x8) |\r
+ ((linkpartner_ability &\r
+ MII_PHY_TECHABILITY_PAUSE_FIELDS) >> 0xA))\r
+ {\r
+ case 0x7 :\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 0;\r
+ break;\r
+\r
+ case 0xA : case 0xB: case 0xE: case 0xF:\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 1;\r
+ break;\r
+\r
+ case 0xD :\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 1;\r
+ break;\r
+\r
+ default :\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ }\r
+ }\r
+\r
+ /* If in 10/100Mbps, non-fiber, full duplex mode, assure\r
+ * 10/100BASE-T PAUSE capabilities are advertised. */\r
+ if ((fullduplex == 1) && (fiber == 0) && (gig == 0))\r
+ {\r
+ /* Make sure the PHY is advertising we are PAUSE\r
+ * capable.\r
+ */\r
+ if (!(advertisement & (MII_PHY_TECHABILITY_PAUSE)))\r
+ {\r
+ /* PAUSE is not being advertised. Advertise\r
+ * PAUSE and restart auto-negotiation.\r
+ */\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ MII_PHY_AUTONEGADVERTISEMENT,\r
+ (advertisement |\r
+ MII_PHY_TECHABILITY_PAUSE));\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr,\r
+ MII_PHY_CONTROL,\r
+ MII_PHY_CONTROL_RESTARTAN);\r
+\r
+ return -EAGAIN;\r
+ }\r
+\r
+ }\r
+\r
+ if (fiber == 1)\r
+ {\r
+ printk(KERN_INFO "%s: Fiber based PHY, ",\r
+ ipg_ethernet_device->name);\r
+ } else\r
+ {\r
+ printk(KERN_INFO "%s: Copper based PHY, ",\r
+ ipg_ethernet_device->name);\r
+ }\r
+\r
+ /* Configure full duplex, and flow control. */\r
+ if (fullduplex == 1)\r
+ {\r
+ /* Configure IPG for full duplex operation. */\r
+ printk("setting full duplex, ");\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr, IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_DUPLEX_SELECT_FD);\r
+\r
+ if (txflowcontrol == 1)\r
+ {\r
+ printk("TX flow control");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_TX_FLOW_CONTROL_ENABLE);\r
+ } else\r
+ {\r
+ printk("no TX flow control");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE);\r
+ }\r
+\r
+ if (rxflowcontrol == 1)\r
+ {\r
+ printk(", RX flow control.");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ } else\r
+ {\r
+ printk(", no RX flow control.");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ }\r
+\r
+ printk("\n");\r
+ } else\r
+ {\r
+ /* Configure IPG for half duplex operation. */\r
+ printk("setting half duplex, no TX flow control, no RX flow control.\n");\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr, IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_DUPLEX_SELECT_FD &\r
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE &\r
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ }\r
+\r
+\r
+ IPG_DEBUG_MSG("G/MII reg 4 (advertisement) = %4.4x\n", advertisement);\r
+ IPG_DEBUG_MSG("G/MII reg 5 (link partner) = %4.4x\n", linkpartner_ability);\r
+ IPG_DEBUG_MSG("G/MII reg 9 (1000BASE-T control) = %4.4x\n", advertisement);\r
+ IPG_DEBUG_MSG("G/MII reg 10 (1000BASE-T status) = %4.4x\n", linkpartner_ability);\r
+\r
+ IPG_DEBUG_MSG("Auto-neg complete, MACCTRL = %8.8x\n",\r
+ IPG_READ_MACCTRL(baseaddr));\r
+\r
+ return 0;\r
+}\r
+#else\r
+int ipg_config_autoneg(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Configure IPG based on result of IEEE 802.3 PHY\r
+ * auto-negotiation.\r
+ */\r
+\r
+// int phyaddr = 0;\r
+ u8 phyctrl;\r
+ u32 asicctrl;\r
+ u32 baseaddr;\r
+// u16 status = 0;\r
+// u16 advertisement;\r
+// u16 linkpartner_ability;\r
+// u16 gigadvertisement;\r
+// u16 giglinkpartner_ability;\r
+// u16 techabilities;\r
+ int fiber;\r
+ int gig;\r
+ int fullduplex;\r
+ int txflowcontrol;\r
+ int rxflowcontrol;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_config_autoneg\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ asicctrl = IPG_READ_ASICCTRL(baseaddr);\r
+ phyctrl = IPG_READ_PHYCTRL(baseaddr);\r
+\r
+ /* Set flags for use in resolving auto-negotation, assuming\r
+ * non-1000Mbps, half duplex, no flow control.\r
+ */\r
+ fiber = 0;\r
+ fullduplex = 0;\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ gig = 0;\r
+\r
+ /* To accomodate a problem in 10Mbps operation,\r
+ * set a global flag if PHY running in 10Mbps mode.\r
+ */\r
+ sp->tenmbpsmode = 0;\r
+\r
+ printk("Link speed = ");\r
+\r
+ /* Determine actual speed of operation. */\r
+ switch (phyctrl & IPG_PC_LINK_SPEED)\r
+ {\r
+ case IPG_PC_LINK_SPEED_10MBPS :\r
+ printk("10Mbps.\n");\r
+ printk(KERN_INFO "%s: 10Mbps operational mode enabled.\n",ipg_ethernet_device->name);\r
+ sp->tenmbpsmode = 1;\r
+ break;\r
+ case IPG_PC_LINK_SPEED_100MBPS :\r
+ printk("100Mbps.\n");\r
+ break;\r
+ case IPG_PC_LINK_SPEED_1000MBPS :\r
+ printk("1000Mbps.\n");\r
+ gig = 1;\r
+ break;\r
+ default : printk("undefined!\n");\r
+ return 0;\r
+ }\r
+\r
+ if ( phyctrl & IPG_PC_DUPLEX_STATUS)\r
+ {\r
+ fullduplex = 1;\r
+ txflowcontrol = 1;\r
+ rxflowcontrol = 1;\r
+ }\r
+ else\r
+ {\r
+ fullduplex = 0;\r
+ txflowcontrol = 0;\r
+ rxflowcontrol = 0;\r
+ }\r
+\r
+\r
+ /* Configure full duplex, and flow control. */\r
+ if (fullduplex == 1)\r
+ {\r
+ /* Configure IPG for full duplex operation. */\r
+ printk("setting full duplex, ");\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr, IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_DUPLEX_SELECT_FD);\r
+\r
+ if (txflowcontrol == 1)\r
+ {\r
+ printk("TX flow control");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_TX_FLOW_CONTROL_ENABLE);\r
+ } else\r
+ {\r
+ printk("no TX flow control");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE);\r
+ }\r
+\r
+ if (rxflowcontrol == 1)\r
+ {\r
+ printk(", RX flow control.");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ } else\r
+ {\r
+ printk(", no RX flow control.");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ }\r
+\r
+ printk("\n");\r
+ } else\r
+ {\r
+ /* Configure IPG for half duplex operation. */\r
+ printk("setting half duplex, no TX flow control, no RX flow control.\n");\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr, IPG_READ_MACCTRL(baseaddr) &\r
+ ~IPG_MC_DUPLEX_SELECT_FD &\r
+ ~IPG_MC_TX_FLOW_CONTROL_ENABLE &\r
+ ~IPG_MC_RX_FLOW_CONTROL_ENABLE);\r
+ }\r
+ return 0;\r
+}\r
+\r
+#endif\r
+\r
+int ipg_io_config(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Initialize the IPG I/O registers. */\r
+\r
+ u32 baseaddr;\r
+ u32 origmacctrl;\r
+ u32 restoremacctrl;\r
+\r
+ IPG_DEBUG_MSG("_io_config\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ /* Save the original value of MACCTRL. */\r
+ origmacctrl = IPG_READ_MACCTRL(baseaddr);\r
+\r
+ /* Establish a vlaue to restore MACCTRL when done. */\r
+ restoremacctrl = origmacctrl;\r
+\r
+ /* Enable statistics gathering. */\r
+ restoremacctrl |= IPG_MC_STATISTICS_ENABLE;\r
+\r
+ /* Based on compilation option, determine if FCS is to be\r
+ * stripped on receive frames by IPG.\r
+ */\r
+ if (!(IPG_STRIP_FCS_ON_RX))\r
+ {\r
+ restoremacctrl |= IPG_MC_RCV_FCS;\r
+ }\r
+\r
+ /* Determine if transmitter and/or receiver are\r
+ * enabled so we may restore MACCTRL correctly.\r
+ */\r
+ if (origmacctrl & IPG_MC_TX_ENABLED)\r
+ {\r
+ restoremacctrl |= IPG_MC_TX_ENABLE;\r
+ }\r
+\r
+ if (origmacctrl & IPG_MC_RX_ENABLED)\r
+ {\r
+ restoremacctrl |= IPG_MC_RX_ENABLE;\r
+ }\r
+\r
+ /* Transmitter and receiver must be disabled before setting\r
+ * IFSSelect.\r
+ */\r
+ IPG_WRITE_MACCTRL(baseaddr, origmacctrl & (IPG_MC_RX_DISABLE |\r
+ IPG_MC_TX_DISABLE));\r
+\r
+ /* Now that transmitter and receiver are disabled, write\r
+ * to IFSSelect.\r
+ */\r
+ IPG_WRITE_MACCTRL(baseaddr, origmacctrl & IPG_MC_IFS_96BIT);\r
+\r
+ /* Set RECEIVEMODE register. */\r
+ ipg_nic_set_multicast_list(ipg_ethernet_device);\r
+\r
+ IPG_WRITE_MAXFRAMESIZE(baseaddr, IPG_MAX_RXFRAME_SIZE);\r
+ IPG_WRITE_RXEARLYTHRESH(baseaddr, IPG_RXEARLYTHRESH_VALUE);\r
+ IPG_WRITE_TXSTARTTHRESH(baseaddr, IPG_TXSTARTTHRESH_VALUE);\r
+ IPG_WRITE_RXDMAINTCTRL(baseaddr, (IPG_RI_RSVD_MASK &\r
+ ((IPG_RI_RXFRAME_COUNT &\r
+ IPG_RXFRAME_COUNT) |\r
+ (IPG_RI_PRIORITY_THRESH &\r
+ (IPG_PRIORITY_THRESH << 12)) |\r
+ (IPG_RI_RXDMAWAIT_TIME &\r
+ (IPG_RXDMAWAIT_TIME << 16)))));\r
+ IPG_WRITE_RXDMAPOLLPERIOD(baseaddr, IPG_RXDMAPOLLPERIOD_VALUE);\r
+ IPG_WRITE_RXDMAURGENTTHRESH(baseaddr,\r
+ IPG_RXDMAURGENTTHRESH_VALUE);\r
+ IPG_WRITE_RXDMABURSTTHRESH(baseaddr,\r
+ IPG_RXDMABURSTTHRESH_VALUE);\r
+ IPG_WRITE_TXDMAPOLLPERIOD(baseaddr,\r
+ IPG_TXDMAPOLLPERIOD_VALUE);\r
+ IPG_WRITE_TXDMAURGENTTHRESH(baseaddr,\r
+ IPG_TXDMAURGENTTHRESH_VALUE);\r
+ IPG_WRITE_TXDMABURSTTHRESH(baseaddr,\r
+ IPG_TXDMABURSTTHRESH_VALUE);\r
+ IPG_WRITE_INTENABLE(baseaddr, IPG_IE_HOST_ERROR |\r
+ IPG_IE_TX_DMA_COMPLETE |\r
+ IPG_IE_TX_COMPLETE |\r
+ IPG_IE_INT_REQUESTED |\r
+ IPG_IE_UPDATE_STATS |\r
+ IPG_IE_LINK_EVENT |\r
+ IPG_IE_RX_DMA_COMPLETE |\r
+ //IPG_IE_RFD_LIST_END | //20041019Jesse_For_SmartBit: remove\r
+ IPG_IE_RX_DMA_PRIORITY);\r
+\r
+ IPG_WRITE_FLOWONTHRESH(baseaddr, IPG_FLOWONTHRESH_VALUE);\r
+ IPG_WRITE_FLOWOFFTHRESH(baseaddr, IPG_FLOWOFFTHRESH_VALUE);\r
+\r
+ /* IPG multi-frag frame bug workaround.\r
+ * Per silicon revision B3 eratta.\r
+ */\r
+ IPG_WRITE_DEBUGCTRL(baseaddr,\r
+ IPG_READ_DEBUGCTRL(baseaddr) | 0x0200);\r
+\r
+ /* IPG TX poll now bug workaround.\r
+ * Per silicon revision B3 eratta.\r
+ */\r
+ IPG_WRITE_DEBUGCTRL(baseaddr,\r
+ IPG_READ_DEBUGCTRL(baseaddr) | 0x0010);\r
+\r
+ /* IPG RX poll now bug workaround.\r
+ * Per silicon revision B3 eratta.\r
+ */\r
+ IPG_WRITE_DEBUGCTRL(baseaddr,\r
+ IPG_READ_DEBUGCTRL(baseaddr) | 0x0020);\r
+\r
+ /* Now restore MACCTRL to original setting. */\r
+ IPG_WRITE_MACCTRL(baseaddr, restoremacctrl);\r
+\r
+ /* Disable unused RMON statistics. */\r
+ IPG_WRITE_RMONSTATISTICSMASK(baseaddr, IPG_RZ_ALL);\r
+\r
+ /* Disable unused MIB statistics. */\r
+ IPG_WRITE_STATISTICSMASK(baseaddr,\r
+ IPG_SM_MACCONTROLFRAMESXMTD |\r
+ IPG_SM_BCSTOCTETXMTOK_BCSTFRAMESXMTDOK |\r
+ IPG_SM_MCSTOCTETXMTOK_MCSTFRAMESXMTDOK |\r
+ IPG_SM_MACCONTROLFRAMESRCVD |\r
+ IPG_SM_BCSTOCTETRCVDOK_BCSTFRAMESRCVDOK |\r
+ IPG_SM_TXJUMBOFRAMES |\r
+ IPG_SM_UDPCHECKSUMERRORS |\r
+ IPG_SM_IPCHECKSUMERRORS |\r
+ IPG_SM_TCPCHECKSUMERRORS |\r
+ IPG_SM_RXJUMBOFRAMES);\r
+\r
+ return 0;\r
+}\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+void ipg_interrupt_handler(int ipg_irq, void *device_instance,\r
+ struct pt_regs *regs)\r
+#else\r
+static irqreturn_t ipg_interrupt_handler(int ipg_irq, void *device_instance,\r
+ struct pt_regs *regs)\r
+#endif\r
+{\r
+ int error;\r
+ u32 baseaddr;\r
+ u16 intstatusackword;\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device =\r
+ (IPG_DEVICE_TYPE *)device_instance;\r
+\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ irqreturn_t intr_handled= IRQ_HANDLED;//IRQ_NONE;\r
+#endif\r
+\r
+#ifdef IPG_DEBUG\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+#endif\r
+\r
+ IPG_DEBUG_MSG("_interrupt_handler\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /*\r
+ * The following code fragment was authored by Donald Becker.\r
+ */\r
+#if defined(__i386__)\r
+ /* A lock to prevent simultaneous entry bug on Intel SMP machines. */\r
+ if (test_and_set_bit(0, (void*)&ipg_ethernet_device->interrupt)) {\r
+ printk(KERN_ERR "%s: SMP simultaneous entry of an interrupt handler.\n",\r
+ ipg_ethernet_device->name);\r
+ ipg_ethernet_device->interrupt = 0; /* Avoid halting machine. */\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ return intr_handled;\r
+#else\r
+ return;\r
+#endif\r
+\r
+ }\r
+#else\r
+ if (ipg_ethernet_device->interrupt) {\r
+ printk(KERN_ERR "%s: Re-entering the interrupt handler.\n",\r
+ ipg_ethernet_device->name);\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ return intr_handled;\r
+#else\r
+ return;\r
+#endif\r
+ }\r
+ ipg_ethernet_device->interrupt = 1;\r
+#endif\r
+ /*\r
+ * End of code fragment authored by Donald Becker.\r
+ */\r
+\r
+ /* Setting ipg_ethernet_device->interrupt = 1\r
+ * indicates to higher network layers an\r
+ * Ethernet NIC interrupt is being serviced.\r
+ */\r
+#endif\r
+\r
+#ifdef JUMBO_FRAME\r
+ ipg_nic_rxrestore(ipg_ethernet_device);\r
+#endif\r
+ /* Get interrupt source information, and acknowledge\r
+ * some (i.e. TxDMAComplete, RxDMAComplete, RxEarly,\r
+ * IntRequested, MacControlFrame, LinkEvent) interrupts\r
+ * if issued. Also, all IPG interrupts are disabled by\r
+ * reading IntStatusAck.\r
+ */\r
+ intstatusackword = IPG_READ_INTSTATUSACK(baseaddr);\r
+\r
+ IPG_DEBUG_MSG("IntStatusAck = %4.4x\n", intstatusackword);\r
+\r
+ /* If RFDListEnd interrupt, restore all used RFDs. */\r
+ if (intstatusackword & IPG_IS_RFD_LIST_END)\r
+ {\r
+ IPG_DEBUG_MSG("RFDListEnd Interrupt.\n");\r
+\r
+ /* The RFD list end indicates an RFD was encountered\r
+ * with a 0 NextPtr, or with an RFDDone bit set to 1\r
+ * (indicating the RFD is not read for use by the\r
+ * IPG.) Try to restore all RFDs.\r
+ */\r
+ ipg_nic_rxrestore(ipg_ethernet_device);\r
+\r
+#ifdef IPG_DEBUG\r
+ /* Increment the RFDlistendCount counter. */\r
+ sp->RFDlistendCount++;\r
+#endif\r
+ }\r
+\r
+ /* If RFDListEnd, RxDMAPriority, RxDMAComplete, or\r
+ * IntRequested interrupt, process received frames. */\r
+ if ((intstatusackword & IPG_IS_RX_DMA_PRIORITY) ||\r
+ (intstatusackword & IPG_IS_RFD_LIST_END) ||\r
+ (intstatusackword & IPG_IS_RX_DMA_COMPLETE) ||\r
+ (intstatusackword & IPG_IS_INT_REQUESTED))\r
+ {\r
+\r
+#ifdef IPG_DEBUG\r
+ /* Increment the RFD list checked counter if interrupted\r
+ * only to check the RFD list. */\r
+ if (intstatusackword & (~(IPG_IS_RX_DMA_PRIORITY |\r
+ IPG_IS_RFD_LIST_END | IPG_IS_RX_DMA_COMPLETE |\r
+ IPG_IS_INT_REQUESTED) &\r
+ (IPG_IS_HOST_ERROR |\r
+ IPG_IS_TX_DMA_COMPLETE |\r
+ IPG_IS_TX_COMPLETE |\r
+ IPG_IS_UPDATE_STATS |\r
+ IPG_IS_LINK_EVENT)))\r
+\r
+ {\r
+ sp->RFDListCheckedCount++;\r
+ }\r
+#endif\r
+\r
+ ipg_nic_rx(ipg_ethernet_device);\r
+ }\r
+\r
+ /* If TxDMAComplete interrupt, free used TFDs. */\r
+ if (intstatusackword & IPG_IS_TX_DMA_COMPLETE)\r
+ {\r
+ /* Free used TFDs. */\r
+ ipg_nic_txfree(ipg_ethernet_device);\r
+ }\r
+\r
+ /* TxComplete interrupts indicate one of numerous actions.\r
+ * Determine what action to take based on TXSTATUS register.\r
+ */\r
+ if (intstatusackword & IPG_IS_TX_COMPLETE)\r
+ {\r
+ ipg_nic_txcleanup(ipg_ethernet_device);\r
+ }\r
+\r
+ /* If UpdateStats interrupt, update Linux Ethernet statistics */\r
+ if (intstatusackword & IPG_IS_UPDATE_STATS)\r
+ {\r
+ ipg_nic_get_stats(ipg_ethernet_device);\r
+ }\r
+\r
+ /* If HostError interrupt, reset IPG. */\r
+ if (intstatusackword & IPG_IS_HOST_ERROR)\r
+ {\r
+ IPG_DDEBUG_MSG("HostError Interrupt\n");\r
+\r
+ IPG_DDEBUG_MSG("DMACtrl = %8.8x\n",\r
+ IPG_READ_DMACTRL(baseaddr));\r
+\r
+ /* Acknowledge HostError interrupt by resetting\r
+ * IPG DMA and HOST.\r
+ */\r
+ ipg_reset(ipg_ethernet_device,\r
+ IPG_AC_GLOBAL_RESET |\r
+ IPG_AC_HOST |\r
+ IPG_AC_DMA);\r
+\r
+ error = ipg_io_config(ipg_ethernet_device);\r
+ if (error < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Cannot recover from PCI error.\n",\r
+ ipg_ethernet_device->name);\r
+ }\r
+\r
+ init_rfdlist(ipg_ethernet_device);\r
+\r
+ init_tfdlist(ipg_ethernet_device);\r
+ }\r
+\r
+ /* If LinkEvent interrupt, resolve autonegotiation. */\r
+ if (intstatusackword & IPG_IS_LINK_EVENT)\r
+ {\r
+ if (ipg_config_autoneg(ipg_ethernet_device) < 0)\r
+ printk(KERN_INFO "%s: Auto-negotiation error.\n",\r
+ ipg_ethernet_device->name);\r
+\r
+ }\r
+\r
+ /* If MACCtrlFrame interrupt, do nothing. */\r
+ if (intstatusackword & IPG_IS_MAC_CTRL_FRAME)\r
+ {\r
+ IPG_DEBUG_MSG("MACCtrlFrame interrupt.\n");\r
+ }\r
+\r
+ /* If RxComplete interrupt, do nothing. */\r
+ if (intstatusackword & IPG_IS_RX_COMPLETE)\r
+ {\r
+ IPG_DEBUG_MSG("RxComplete interrupt.\n");\r
+ }\r
+\r
+ /* If RxEarly interrupt, do nothing. */\r
+ if (intstatusackword & IPG_IS_RX_EARLY)\r
+ {\r
+ IPG_DEBUG_MSG("RxEarly interrupt.\n");\r
+ }\r
+\r
+ /* Re-enable IPG interrupts. */\r
+ IPG_WRITE_INTENABLE(baseaddr, IPG_IE_HOST_ERROR |\r
+ IPG_IE_TX_DMA_COMPLETE |\r
+ IPG_IE_TX_COMPLETE |\r
+ IPG_IE_INT_REQUESTED |\r
+ IPG_IE_UPDATE_STATS |\r
+ IPG_IE_LINK_EVENT |\r
+ IPG_IE_RX_DMA_COMPLETE |\r
+ //IPG_IE_RFD_LIST_END | //20041019Jesse_For_SmartBit: remove\r
+ IPG_IE_RX_DMA_PRIORITY);\r
+\r
+ /* Indicate to higher network layers the Ethernet NIC\r
+ * interrupt servicing is complete.\r
+ */\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /*\r
+ * The following code fragment was authored by Donald Becker.\r
+ */\r
+#if defined(__i386__)\r
+ clear_bit(0, (void*)&ipg_ethernet_device->interrupt);\r
+#else\r
+ ipg_ethernet_device->interrupt = 0;\r
+#endif\r
+ /*\r
+ * End of code fragment authored by Donald Becker.\r
+ */\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ return intr_handled;\r
+#else\r
+ return;\r
+#endif\r
+}\r
+\r
+void ipg_nic_txcleanup(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* For TxComplete interrupts, free all transmit\r
+ * buffers which have already been transfered via DMA\r
+ * to the IPG.\r
+ */\r
+\r
+ int maxtfdcount;\r
+ u32 baseaddr;\r
+ u32 txstatusdword;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_txcleanup\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ maxtfdcount = IPG_TFDLIST_LENGTH;\r
+\r
+ do\r
+ {\r
+ /* Reading the TXSTATUS register clears the\r
+ * TX_COMPLETE interrupt.\r
+ */\r
+ txstatusdword = IPG_READ_TXSTATUS(baseaddr);\r
+\r
+ IPG_DEBUG_MSG("TxStatus = %8.8x\n",txstatusdword);\r
+\r
+ /* Check for Transmit errors. Error bits only valid if\r
+ * TX_COMPLETE bit in the TXSTATUS register is a 1.\r
+ */\r
+ if (txstatusdword & IPG_TS_TX_COMPLETE)\r
+ {\r
+\r
+ /* If in 10Mbps mode, indicate transmit is ready. */\r
+ if (sp->tenmbpsmode)\r
+ {\r
+ spin_lock(&sp->lock);\r
+ IPG_TX_NOTBUSY(ipg_ethernet_device);\r
+ spin_unlock(&sp->lock);\r
+ }\r
+\r
+ /* Transmit error, increment stat counters. */\r
+ if (txstatusdword & IPG_TS_TX_ERROR)\r
+ {\r
+ IPG_DEBUG_MSG("Transmit error.\n");\r
+ sp->stats.tx_errors++;\r
+ }\r
+\r
+ /* Late collision, re-enable transmitter. */\r
+ if (txstatusdword & IPG_TS_LATE_COLLISION)\r
+ {\r
+ IPG_DEBUG_MSG("Late collision on transmit.\n");\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_TX_ENABLE);\r
+ }\r
+\r
+ /* Maximum collisions, re-enable transmitter. */\r
+ if (txstatusdword & IPG_TS_TX_MAX_COLL)\r
+ {\r
+ IPG_DEBUG_MSG("Maximum collisions on transmit.\n");\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_TX_ENABLE);\r
+ }\r
+\r
+ /* Transmit underrun, reset and re-enable\r
+ * transmitter.\r
+ */\r
+ if (txstatusdword & IPG_TS_TX_UNDERRUN)\r
+ {\r
+ IPG_DEBUG_MSG("Transmitter underrun.\n");\r
+ sp->stats.tx_fifo_errors++;\r
+ ipg_reset(ipg_ethernet_device, IPG_AC_TX_RESET |\r
+ IPG_AC_DMA |\r
+ IPG_AC_NETWORK);\r
+\r
+ /* Re-configure after DMA reset. */\r
+ if ((ipg_io_config(ipg_ethernet_device) < 0) ||\r
+ (init_tfdlist(ipg_ethernet_device) < 0))\r
+ {\r
+ printk(KERN_INFO "%s: Error during re-configuration.\n",\r
+ ipg_ethernet_device->name);\r
+ }\r
+\r
+ IPG_WRITE_MACCTRL(baseaddr,\r
+ IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_TX_ENABLE);\r
+ }\r
+ }\r
+ else\r
+ break;\r
+\r
+ maxtfdcount--;\r
+\r
+ }\r
+ while(maxtfdcount != 0);\r
+\r
+ ipg_nic_txfree(ipg_ethernet_device);\r
+\r
+ return;\r
+}\r
+\r
+void ipg_nic_txfree(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Free all transmit buffers which have already been transfered\r
+ * via DMA to the IPG.\r
+ */\r
+\r
+ int NextToFree;\r
+ int maxtfdcount;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_txfree\n");\r
+\r
+ maxtfdcount = IPG_TFDLIST_LENGTH;\r
+\r
+ /* Set the CurrentTxFrameID to skip the next\r
+ * TxDMACompleteInterrupt.\r
+ */\r
+ sp->CurrentTxFrameID = 1;\r
+\r
+ do\r
+ {\r
+ /* Calculate next TFD to release. */\r
+ NextToFree = (sp->LastFreedTxBuff + 1) % IPG_TFDLIST_LENGTH;\r
+\r
+ IPG_DEBUG_MSG("TFC = %16.16lx\n", (unsigned long int)\r
+ sp->TFDList[NextToFree].TFC);\r
+\r
+ /* Look at each TFD's TFC field beginning\r
+ * at the last freed TFD up to the current TFD.\r
+ * If the TFDDone bit is set, free the associated\r
+ * buffer.\r
+ */\r
+ if ((le64_to_cpu(sp->TFDList[NextToFree].TFC) &\r
+ IPG_TFC_TFDDONE) &&\r
+ (NextToFree != sp->CurrentTFD))\r
+ {\r
+ /* Free the transmit buffer. */\r
+ if (sp->TxBuff[NextToFree] != NULL)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->TxBuffDMAhandle[NextToFree].dmahandle,\r
+ sp->TxBuffDMAhandle[NextToFree].len,\r
+ PCI_DMA_TODEVICE);\r
+#endif\r
+\r
+ IPG_DEV_KFREE_SKB(sp->TxBuff[NextToFree]);\r
+\r
+ sp->TxBuff[NextToFree] = NULL;\r
+ }\r
+\r
+ sp->LastFreedTxBuff = NextToFree;\r
+ }\r
+ else\r
+ break;\r
+\r
+ maxtfdcount--;\r
+\r
+ } while(maxtfdcount != 0);\r
+\r
+ return;\r
+}\r
+\r
+int ipg_nic_open(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* The IPG NIC Ethernet interface is opened when activated\r
+ * by ifconfig.\r
+ */\r
+\r
+ int phyaddr = 0;\r
+ int error = 0;\r
+ int i;\r
+ u32 baseaddr;\r
+ u8 revisionid=0;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_open\n");\r
+\r
+\r
+\r
+#ifdef USE_IO_OPS\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ /* Check for I/O port address conflicts, return error\r
+ * if requested range is already in use.\r
+ */\r
+ if ((error = check_region(IPG_PCI_RESOURCE_START(\r
+ sp->ipg_pci_device, 0) &\r
+ IPG_PIB_IOBASEADDRESS,\r
+ IPG_IO_REG_RANGE)) < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error registering I/O ports.\n",\r
+ ipg_ethernet_device->name);\r
+ return error;\r
+ }\r
+\r
+ /* Requested range is free, reserve that range for\r
+ * use by the IPG.\r
+ */\r
+ request_region(IPG_PCI_RESOURCE_START(sp->ipg_pci_device, 0) &\r
+ IPG_PIB_IOBASEADDRESS,\r
+ IPG_IO_REG_RANGE,\r
+ IPG_DRIVER_NAME);\r
+\r
+ /* Use I/O space to access IPG registers. */\r
+ ipg_ethernet_device->base_addr =\r
+ (IPG_PCI_RESOURCE_START(sp->ipg_pci_device, 0) &\r
+ IPG_PIB_IOBASEADDRESS);\r
+#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+\r
+#else /* Not using I/O space. */\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ /* Use memory space to access IPG registers. ioremap\r
+ * is used to map high-memory PCI buffer address space\r
+ * to user address space.\r
+ */\r
+ ipg_ethernet_device->base_addr = (unsigned long)\r
+ (ioremap(IPG_PCI_RESOURCE_START(sp->ipg_pci_device, 1) &\r
+ IPG_PMB_MEMBASEADDRESS, IPG_MEM_REG_RANGE));\r
+#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+#ifdef IPG_LINUX2_4\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ ipg_ethernet_device->mem_start = pci_resource_start(\r
+ sp->ipg_pci_device, 1);\r
+ ipg_ethernet_device->mem_end = ipg_ethernet_device->mem_start + IPG_MEM_REG_RANGE;\r
+#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) \r
+#endif\r
+\r
+#endif\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ /* Reset all functions within the IPG. Do not assert\r
+ * RST_OUT as not compatible with some PHYs.\r
+ */\r
+ i = IPG_AC_GLOBAL_RESET | IPG_AC_RX_RESET |\r
+ IPG_AC_TX_RESET | IPG_AC_DMA |\r
+ IPG_AC_FIFO | IPG_AC_NETWORK |\r
+ IPG_AC_HOST | IPG_AC_AUTO_INIT;\r
+ /* Read/Write and Reset EEPROM Value Jesse20040128EEPROM_VALUE */\r
+ /* Read LED Mode Configuration from EEPROM */\r
+ sp->LED_Mode=read_eeprom(ipg_ethernet_device, 6);\r
+ \r
+ error = ipg_reset(ipg_ethernet_device, i);\r
+ if (error < 0)\r
+ {\r
+ return error;\r
+ }\r
+\r
+ /* Reset PHY. */\r
+ phyaddr = ipg_find_phyaddr(ipg_ethernet_device);\r
+\r
+ if (phyaddr != -1)\r
+ { u16 mii_phyctrl, mii_1000cr;\r
+ mii_1000cr = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_1000BASETCONTROL);\r
+ write_phy_register(ipg_ethernet_device, phyaddr,\r
+ GMII_PHY_1000BASETCONTROL,\r
+ mii_1000cr |\r
+ GMII_PHY_1000BASETCONTROL_FULL_DUPLEX |\r
+ GMII_PHY_1000BASETCONTROL_HALF_DUPLEX |\r
+ GMII_PHY_1000BASETCONTROL_PreferMaster);\r
+\r
+ mii_phyctrl = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_CONTROL);\r
+ /* Set default phyparam*/\r
+ pci_read_config_byte(sp->ipg_pci_device,PCI_REVISION_ID,&revisionid);\r
+ bSetPhyDefaultParam(revisionid,ipg_ethernet_device,phyaddr);\r
+ \r
+ /* reset Phy*/ \r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr, GMII_PHY_CONTROL,\r
+ (mii_phyctrl | GMII_PHY_CONTROL_RESET |\r
+ MII_PHY_CONTROL_RESTARTAN));\r
+\r
+ }\r
+\r
+ /* Intitalize lock variable before requesting interrupt. */\r
+ sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;\r
+\r
+ /* Check for interrupt line conflicts, and request interrupt\r
+ * line for IPG.\r
+ *\r
+ * IMPORTANT: Disable IPG interrupts prior to registering\r
+ * IRQ.\r
+ */\r
+ IPG_WRITE_INTENABLE(baseaddr, 0x0000);\r
+\r
+ /* Register the interrupt line to be used by the IPG within\r
+ * the Linux system.\r
+ */\r
+ if ((error = request_irq(sp->ipg_pci_device->irq,\r
+ &ipg_interrupt_handler,\r
+ SA_SHIRQ,\r
+ ipg_ethernet_device->name,\r
+ ipg_ethernet_device)) < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error when requesting interrupt.\n",\r
+ ipg_ethernet_device->name);\r
+ return error;\r
+ }\r
+\r
+ ipg_ethernet_device->irq = sp->ipg_pci_device->irq;\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /* Reserve memory for RFD list which must lie on an 8 byte\r
+ * boundary. So, allocated memory must be adjusted to account\r
+ * for this alignment.\r
+ */\r
+ sp->RFDList = kmalloc((sizeof(struct RFD) * IPG_RFDLIST_LENGTH) +\r
+ IPG_DMALIST_ALIGN_PAD, GFP_KERNEL);\r
+\r
+ if (sp->RFDList != NULL)\r
+ {\r
+ /* Adjust the start address of the allocated\r
+ * memory to assure it begins on an 8 byte\r
+ * boundary.\r
+ */\r
+ sp->RFDList = (void *)(((long)(sp->RFDList) +\r
+ IPG_DMALIST_ALIGN_PAD) &\r
+ ~IPG_DMALIST_ALIGN_PAD);\r
+ }\r
+\r
+ /* Reserve memory for TFD list which must lie on an 8 byte\r
+ * boundary. So, allocated memory must be adjusted to account\r
+ * for this alignment.\r
+ */\r
+ sp->TFDList = kmalloc((sizeof(struct TFD) * IPG_TFDLIST_LENGTH) +\r
+ IPG_DMALIST_ALIGN_PAD, GFP_KERNEL);\r
+\r
+ if (sp->TFDList != NULL)\r
+ {\r
+ /* Adjust the start address of the allocated\r
+ * memory to assure it begins on an 8 byte\r
+ * boundary.\r
+ */\r
+ sp->TFDList = (void *)(((long)(sp->TFDList) +\r
+ IPG_DMALIST_ALIGN_PAD) &\r
+ ~IPG_DMALIST_ALIGN_PAD);\r
+ }\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->RFDList = pci_alloc_consistent(sp->ipg_pci_device,\r
+ (sizeof(struct RFD) *\r
+ IPG_RFDLIST_LENGTH),\r
+ &sp->RFDListDMAhandle);\r
+\r
+ sp->TFDList = pci_alloc_consistent(sp->ipg_pci_device,\r
+ (sizeof(struct TFD) *\r
+ IPG_TFDLIST_LENGTH),\r
+ &sp->TFDListDMAhandle);\r
+#endif\r
+\r
+ if ((sp->RFDList == NULL) || (sp->TFDList == NULL))\r
+ {\r
+ printk(KERN_INFO "%s: No memory available for IP1000 RFD and/or TFD lists.\n", ipg_ethernet_device->name);\r
+ return -ENOMEM;\r
+ }\r
+\r
+ error = init_rfdlist(ipg_ethernet_device);\r
+ if (error < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error during configuration.\n",\r
+ ipg_ethernet_device->name);\r
+ return error;\r
+ }\r
+\r
+ error = init_tfdlist(ipg_ethernet_device);\r
+ if (error < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error during configuration.\n",\r
+ ipg_ethernet_device->name);\r
+ return error;\r
+ }\r
+ \r
+ /* Read MAC Address from EERPOM Jesse20040128EEPROM_VALUE */\r
+ sp->StationAddr0=read_eeprom(ipg_ethernet_device, 16);\r
+ sp->StationAddr1=read_eeprom(ipg_ethernet_device, 17);\r
+ sp->StationAddr2=read_eeprom(ipg_ethernet_device, 18);\r
+ /* Write MAC Address to Station Address */\r
+ IPG_WRITE_STATIONADDRESS0(baseaddr,sp->StationAddr0);\r
+ IPG_WRITE_STATIONADDRESS1(baseaddr,sp->StationAddr1);\r
+ IPG_WRITE_STATIONADDRESS2(baseaddr,sp->StationAddr2);\r
+\r
+ /* Set station address in ethernet_device structure. */\r
+ ipg_ethernet_device->dev_addr[0] =\r
+ IPG_READ_STATIONADDRESS0(baseaddr) & 0x00FF;\r
+ ipg_ethernet_device->dev_addr[1] =\r
+ (IPG_READ_STATIONADDRESS0(baseaddr) & 0xFF00) >> 8;\r
+ ipg_ethernet_device->dev_addr[2] =\r
+ IPG_READ_STATIONADDRESS1(baseaddr) & 0x00FF;\r
+ ipg_ethernet_device->dev_addr[3] =\r
+ (IPG_READ_STATIONADDRESS1(baseaddr) & 0xFF00) >> 8;\r
+ ipg_ethernet_device->dev_addr[4] =\r
+ IPG_READ_STATIONADDRESS2(baseaddr) & 0x00FF;\r
+ ipg_ethernet_device->dev_addr[5] =\r
+ (IPG_READ_STATIONADDRESS2(baseaddr) & 0xFF00) >> 8;\r
+\r
+ /* Configure IPG I/O registers. */\r
+ error = ipg_io_config(ipg_ethernet_device);\r
+ if (error < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Error during configuration.\n",\r
+ ipg_ethernet_device->name);\r
+ return error;\r
+ }\r
+\r
+ /* Resolve autonegotiation. */\r
+ if (ipg_config_autoneg(ipg_ethernet_device) < 0)\r
+ {\r
+ printk(KERN_INFO "%s: Auto-negotiation error.\n",\r
+ ipg_ethernet_device->name);\r
+ }\r
+\r
+#ifdef JUMBO_FRAME\r
+ /* initialize JUMBO Frame control variable */\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=0;\r
+ ipg_ethernet_device->mtu = IPG_TXFRAG_SIZE;\r
+#endif\r
+\r
+ /* Enable transmit and receive operation of the IPG. */\r
+ IPG_WRITE_MACCTRL(baseaddr, IPG_READ_MACCTRL(baseaddr) |\r
+ IPG_MC_RX_ENABLE | IPG_MC_TX_ENABLE);\r
+\r
+#ifdef IPG_LINUX2_2\r
+ ipg_ethernet_device->interrupt = 0;\r
+ ipg_ethernet_device->start = 1;\r
+ clear_bit(0,(void*)&ipg_ethernet_device->tbusy);\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ netif_start_queue(ipg_ethernet_device);\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ /* Increment the usage count for this IPG driver module. */\r
+ MOD_INC_USE_COUNT;\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+int init_rfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Initialize the RFDList. */\r
+\r
+ int i;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_init_rfdlist\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ /* Clear the receive buffer not ready flag. */\r
+ sp->RxBuffNotReady = 0;\r
+\r
+ for(i=0; i<IPG_RFDLIST_LENGTH; i++)\r
+ {\r
+ /* Free any allocated receive buffers. */\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[i].dmahandle,\r
+ sp->RxBuffDMAhandle[i].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+ if (sp->RxBuff[i] != NULL)\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[i]);\r
+ sp->RxBuff[i] = NULL;\r
+\r
+ /* Clear out the RFS field. */\r
+ sp->RFDList[i].RFS = 0x0000000000000000;\r
+\r
+ if (ipg_get_rxbuff(ipg_ethernet_device, i) < 0)\r
+ {\r
+ /* A receive buffer was not ready, break the\r
+ * RFD list here and set the receive buffer\r
+ * not ready flag.\r
+ */\r
+ sp->RxBuffNotReady = 1;\r
+\r
+ IPG_DEBUG_MSG("Cannot allocate Rx buffer.\n");\r
+\r
+ /* Just in case we cannot allocate a single RFD.\r
+ * Should not occur.\r
+ */\r
+ if (i == 0)\r
+ {\r
+ printk(KERN_ERR "%s: No memory available for RFD list.\n",\r
+ ipg_ethernet_device->name);\r
+ return -ENOMEM;\r
+ }\r
+ }\r
+\r
+ /* Set up RFDs to point to each other. A ring structure. */\r
+#ifdef IPG_LINUX2_2\r
+ sp->RFDList[i].RFDNextPtr = cpu_to_le64(\r
+ IPG_HOST2BUS_MAP(\r
+ &sp->RFDList[(i + 1) %\r
+ IPG_RFDLIST_LENGTH]));\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->RFDList[i].RFDNextPtr = cpu_to_le64(\r
+ sp->RFDListDMAhandle +\r
+ ((sizeof(struct RFD)) *\r
+ ((i + 1) %\r
+ IPG_RFDLIST_LENGTH)));\r
+#endif\r
+ }\r
+\r
+ sp->CurrentRFD = 0;\r
+ sp->LastRestoredRxBuff = i - 1;\r
+\r
+ /* Write the location of the RFDList to the IPG. */\r
+#ifdef IPG_LINUX2_2\r
+ IPG_WRITE_RFDLISTPTR0(baseaddr,\r
+ (u32)(IPG_HOST2BUS_MAP(sp->RFDList)));\r
+ IPG_WRITE_RFDLISTPTR1(baseaddr, 0x00000000);\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ IPG_WRITE_RFDLISTPTR0(baseaddr, (u32)(sp->RFDListDMAhandle));\r
+ IPG_WRITE_RFDLISTPTR1(baseaddr, 0x00000000);\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+int init_tfdlist(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Initialize TFDList. */\r
+\r
+ int i;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_init_tfdlist\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ for(i=0; i<IPG_TFDLIST_LENGTH; i++)\r
+ {\r
+#ifdef IPG_LINUX2_2\r
+ sp->TFDList[i].TFDNextPtr = cpu_to_le64(\r
+ IPG_HOST2BUS_MAP(\r
+ &sp->TFDList[(i + 1) %\r
+ IPG_TFDLIST_LENGTH]));\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->TFDList[i].TFDNextPtr = cpu_to_le64(\r
+ sp->TFDListDMAhandle +\r
+ ((sizeof(struct TFD)) *\r
+ ((i + 1) %\r
+ IPG_TFDLIST_LENGTH)));\r
+#endif\r
+\r
+ sp->TFDList[i].TFC = cpu_to_le64(IPG_TFC_TFDDONE);\r
+ if (sp->TxBuff[i] != NULL)\r
+ IPG_DEV_KFREE_SKB(sp->TxBuff[i]);\r
+ sp->TxBuff[i] = NULL;\r
+ }\r
+\r
+ sp->CurrentTFD = IPG_TFDLIST_LENGTH - 1;\r
+ sp->CurrentTxFrameID = 0;\r
+ sp->LastFreedTxBuff = IPG_TFDLIST_LENGTH - 1;\r
+\r
+ /* Write the location of the TFDList to the IPG. */\r
+#ifdef IPG_LINUX2_2\r
+ IPG_WRITE_TFDLISTPTR0(baseaddr, (u32)(IPG_HOST2BUS_MAP(\r
+ sp->TFDList)));\r
+ IPG_WRITE_TFDLISTPTR1(baseaddr, 0x00000000);\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ IPG_DDEBUG_MSG("Starting TFDListPtr = %8.8x\n",\r
+ (u32)(sp->TFDListDMAhandle));\r
+ IPG_WRITE_TFDLISTPTR0(baseaddr, (u32)(sp->TFDListDMAhandle));\r
+ IPG_WRITE_TFDLISTPTR1(baseaddr, 0x00000000);\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+int ipg_get_rxbuff(IPG_DEVICE_TYPE *ipg_ethernet_device, int rfd)\r
+{\r
+ /* Create a receive buffer within system memory and update\r
+ * NIC private structure appropriately.\r
+ */\r
+ u64 rxfragsize;\r
+ struct sk_buff *skb;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_get_rxbuff\n");\r
+\r
+ /* Allocate memory buffers for receive frames. Pad by\r
+ * 2 to account for IP field alignment.\r
+ */\r
+ skb = dev_alloc_skb(IPG_RXSUPPORT_SIZE + 2);\r
+\r
+ if (skb == NULL)\r
+ {\r
+ sp->RxBuff[rfd] = NULL;\r
+ return -ENOMEM;\r
+ }\r
+\r
+ /* Adjust the data start location within the buffer to\r
+ * align IP address field to a 16 byte boundary.\r
+ */\r
+ skb_reserve(skb, 2);\r
+\r
+ /* Associate the receive buffer with the IPG NIC. */\r
+ skb->dev = ipg_ethernet_device;\r
+\r
+ /* Save the address of the sk_buff structure. */\r
+ sp->RxBuff[rfd] = skb;\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /* The sk_buff struct "data" field holds the address\r
+ * of the beginning of valid octets. Assign this address\r
+ * to the fragement address field of the RFD.\r
+ */\r
+ sp->RFDList[rfd].FragInfo = cpu_to_le64(\r
+ IPG_HOST2BUS_MAP(skb->data));\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->RxBuffDMAhandle[rfd].len = IPG_RXSUPPORT_SIZE;\r
+ sp->RxBuffDMAhandle[rfd].dmahandle = pci_map_single(\r
+ sp->ipg_pci_device,\r
+ skb->data, IPG_RXSUPPORT_SIZE,\r
+ PCI_DMA_FROMDEVICE);\r
+ sp->RFDList[rfd].FragInfo = cpu_to_le64(\r
+ sp->RxBuffDMAhandle[rfd].dmahandle);\r
+#endif\r
+\r
+ /* Set the RFD fragment length. */\r
+ rxfragsize = IPG_RXFRAG_SIZE;\r
+ sp->RFDList[rfd].FragInfo |= cpu_to_le64((rxfragsize << 48) &\r
+ IPG_RFI_FRAGLEN);\r
+\r
+ return 0;\r
+}\r
+\r
+int ipg_nic_stop(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Release resources requested by driver open function. */\r
+\r
+ int i;\r
+ int error;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_stop\n");\r
+ \r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ netif_stop_queue(ipg_ethernet_device);\r
+ //netif_msg_ifdown(sp);\r
+#endif\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ IPG_DDEBUG_MSG("TFDunavailCount = %i\n", sp->TFDunavailCount);\r
+ IPG_DDEBUG_MSG("RFDlistendCount = %i\n", sp->RFDlistendCount);\r
+ IPG_DDEBUG_MSG("RFDListCheckedCount = %i\n", sp->RFDListCheckedCount);\r
+ IPG_DDEBUG_MSG("EmptyRFDListCount = %i\n", sp->EmptyRFDListCount);\r
+ IPG_DUMPTFDLIST(ipg_ethernet_device);\r
+\r
+ /* Reset all functions within the IPG to shut down the\r
+ IP1000* .\r
+ */\r
+ i = IPG_AC_GLOBAL_RESET | IPG_AC_RX_RESET |\r
+ IPG_AC_TX_RESET | IPG_AC_DMA |\r
+ IPG_AC_FIFO | IPG_AC_NETWORK |\r
+ IPG_AC_HOST | IPG_AC_AUTO_INIT;\r
+\r
+ error = ipg_reset(ipg_ethernet_device, i);\r
+ if (error < 0)\r
+ {\r
+ return error;\r
+ }\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ /* Decrement the usage count for this IPG driver module. */\r
+ MOD_DEC_USE_COUNT;\r
+#endif\r
+\r
+ /* Free all receive buffers. */\r
+ for(i=0; i<IPG_RFDLIST_LENGTH; i++)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[i].dmahandle,\r
+ sp->RxBuffDMAhandle[i].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+ if (sp->RxBuff[i] != NULL)\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[i]);\r
+ sp->RxBuff[i] = NULL;\r
+ }\r
+\r
+ /* Free all transmit buffers. */\r
+ for(i=0; i<IPG_TFDLIST_LENGTH; i++)\r
+ {\r
+ if (sp->TxBuff[i] != NULL)\r
+ IPG_DEV_KFREE_SKB(sp->TxBuff[i]);\r
+ sp->TxBuff[i] = NULL;\r
+ }\r
+\r
+#ifdef IPG_LINUX2_2\r
+ ipg_ethernet_device->start = 0;\r
+ set_bit(0,(void*)&ipg_ethernet_device->tbusy);\r
+\r
+ /* Free memory associated with the RFDList. */\r
+ kfree(sp->RFDList);\r
+\r
+ /* Free memory associated with the TFDList. */\r
+ kfree(sp->TFDList);\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ netif_stop_queue(ipg_ethernet_device);\r
+\r
+ /* Free memory associated with the RFDList. */\r
+ pci_free_consistent(sp->ipg_pci_device,\r
+ (sizeof(struct RFD) *\r
+ IPG_RFDLIST_LENGTH),\r
+ sp->RFDList, sp->RFDListDMAhandle);\r
+\r
+ /* Free memory associated with the TFDList. */\r
+ pci_free_consistent(sp->ipg_pci_device,\r
+ (sizeof(struct TFD) *\r
+ IPG_TFDLIST_LENGTH),\r
+ sp->TFDList, sp->TFDListDMAhandle);\r
+#endif\r
+\r
+ /* Release interrupt line. */\r
+ free_irq(ipg_ethernet_device->irq, ipg_ethernet_device);\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)\r
+ return 0;\r
+#endif\r
+\r
+#ifdef USE_IO_OPS\r
+\r
+ /* Release I/O range reserved for IPG registers. */\r
+ release_region(ipg_ethernet_device->base_addr,\r
+ IPG_IO_REG_RANGE);\r
+\r
+#else /* Not using I/O space. */\r
+\r
+ /* Unmap memory used for IPG registers. */\r
+\r
+ /* The following line produces strange results unless\r
+ * unregister_netdev precedes it.\r
+ */\r
+ iounmap((void *)ipg_ethernet_device->base_addr);\r
+\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+int ipg_nic_hard_start_xmit(struct sk_buff *skb,\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Transmit an Ethernet frame. */\r
+\r
+ u64 fraglen;\r
+ u64 vlanvid;\r
+ u64 vlancfi;\r
+ u64 vlanuserpriority;\r
+ unsigned long flags;\r
+ int NextTFD;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DDEBUG_MSG("_nic_hard_start_xmit\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /* If IPG NIC is already busy, return error. */\r
+ if (test_and_set_bit(0, (void*)&ipg_ethernet_device->tbusy) != 0)\r
+ {\r
+ IPG_DEBUG_MSG("Transmit busy!\n");\r
+\r
+ return -EBUSY;\r
+ }\r
+#endif\r
+\r
+ /* Disable interrupts. */\r
+ spin_lock_irqsave(&sp->lock, flags);\r
+\r
+#ifdef IPG_LINUX2_4\r
+ /* If in 10Mbps mode, stop the transmit queue so\r
+ * no more transmit frames are accepted.\r
+ */\r
+ if (sp->tenmbpsmode)\r
+ {\r
+ netif_stop_queue(ipg_ethernet_device);\r
+ }\r
+#endif\r
+\r
+ /* Next TFD is found by incrementing the CurrentTFD\r
+ * counter, modulus the length of the TFDList.\r
+ */\r
+ NextTFD = (sp->CurrentTFD + 1) % IPG_TFDLIST_LENGTH;\r
+\r
+ /* Check for availability of next TFD. */\r
+ if (!(le64_to_cpu(sp->TFDList[NextTFD].TFC) &\r
+ IPG_TFC_TFDDONE) || (NextTFD == sp->LastFreedTxBuff))\r
+ {\r
+ IPG_DEBUG_MSG("Next TFD not available.\n");\r
+\r
+ /* Attempt to free any used TFDs. */\r
+ ipg_nic_txfree(ipg_ethernet_device);\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /* Transmit no longer busy. */\r
+ clear_bit(0,(void*)&ipg_ethernet_device->tbusy);\r
+#endif\r
+\r
+ /* Restore interrupts. */\r
+ spin_unlock_irqrestore(&sp->lock, flags);\r
+\r
+#ifdef IPG_DEBUG\r
+ /* Increment the TFDunavailCount counter. */\r
+ sp->TFDunavailCount++;\r
+#endif\r
+\r
+ return -ENOMEM;\r
+ }\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->TxBuffDMAhandle[NextTFD].len = skb->len;\r
+ sp->TxBuffDMAhandle[NextTFD].dmahandle = pci_map_single(\r
+ sp->ipg_pci_device,\r
+ skb->data, skb->len,\r
+ PCI_DMA_TODEVICE);\r
+#endif\r
+\r
+ /* Save the sk_buff pointer so interrupt handler can later free\r
+ * memory occupied by buffer.\r
+ */\r
+ sp->TxBuff[NextTFD] = skb;\r
+\r
+ /* Clear all TFC fields, except TFDDONE. */\r
+ sp->TFDList[NextTFD].TFC = cpu_to_le64(IPG_TFC_TFDDONE);\r
+\r
+ /* Specify the TFC field within the TFD. */\r
+ sp->TFDList[NextTFD].\r
+ TFC |= cpu_to_le64(IPG_TFC_WORDALIGNDISABLED |\r
+ (IPG_TFC_FRAMEID &\r
+ cpu_to_le64(sp->CurrentTxFrameID)) |\r
+ (IPG_TFC_FRAGCOUNT & (1 << 24)));\r
+\r
+ /* Request TxComplete interrupts at an interval defined\r
+ * by the constant IPG_FRAMESBETWEENTXCOMPLETES.\r
+ * Request TxComplete interrupt for every frame\r
+ * if in 10Mbps mode to accomodate problem with 10Mbps\r
+ * processing.\r
+ */\r
+ if (sp->tenmbpsmode)\r
+ {\r
+ sp->TFDList[NextTFD].TFC |=\r
+ cpu_to_le64(IPG_TFC_TXINDICATE);\r
+ }\r
+ else if ((sp->CurrentTxFrameID %\r
+ IPG_FRAMESBETWEENTXDMACOMPLETES) == 0)\r
+ {\r
+ sp->TFDList[NextTFD].TFC |=\r
+ cpu_to_le64(IPG_TFC_TXDMAINDICATE);\r
+ }\r
+\r
+ /* Based on compilation option, determine if FCS is to be\r
+ * appended to transmit frame by IPG.\r
+ */\r
+ if (!(IPG_APPEND_FCS_ON_TX))\r
+ {\r
+ sp->TFDList[NextTFD].\r
+ TFC |= cpu_to_le64(IPG_TFC_FCSAPPENDDISABLE);\r
+ }\r
+\r
+ /* Based on compilation option, determine if IP, TCP and/or\r
+ * UDP checksums are to be added to transmit frame by IPG.\r
+ */\r
+ if (IPG_ADD_IPCHECKSUM_ON_TX)\r
+ {\r
+ sp->TFDList[NextTFD].\r
+ TFC |= cpu_to_le64(IPG_TFC_IPCHECKSUMENABLE);\r
+ }\r
+ if (IPG_ADD_TCPCHECKSUM_ON_TX)\r
+ {\r
+ sp->TFDList[NextTFD].\r
+ TFC |= cpu_to_le64(IPG_TFC_TCPCHECKSUMENABLE);\r
+ }\r
+ if (IPG_ADD_UDPCHECKSUM_ON_TX)\r
+ {\r
+ sp->TFDList[NextTFD].\r
+ TFC |= cpu_to_le64(IPG_TFC_UDPCHECKSUMENABLE);\r
+ }\r
+\r
+ /* Based on compilation option, determine if VLAN tag info is to be\r
+ * inserted into transmit frame by IPG.\r
+ */\r
+ if (IPG_INSERT_MANUAL_VLAN_TAG)\r
+ {\r
+ vlanvid = IPG_MANUAL_VLAN_VID;\r
+ vlancfi = IPG_MANUAL_VLAN_CFI;\r
+ vlanuserpriority = IPG_MANUAL_VLAN_USERPRIORITY;\r
+\r
+ sp->TFDList[NextTFD].TFC |= cpu_to_le64(\r
+ IPG_TFC_VLANTAGINSERT |\r
+ (vlanvid << 32) |\r
+ (vlancfi << 44) |\r
+ (vlanuserpriority << 45));\r
+ }\r
+\r
+ /* The fragment start location within system memory is defined\r
+ * by the sk_buff structure's data field. The physical address\r
+ * of this location within the system's virtual memory space\r
+ * is determined using the IPG_HOST2BUS_MAP function.\r
+ */\r
+#ifdef IPG_LINUX2_2\r
+ sp->TFDList[NextTFD].FragInfo = cpu_to_le64(\r
+ IPG_HOST2BUS_MAP(skb->data));\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ sp->TFDList[NextTFD].FragInfo = cpu_to_le64(\r
+ sp->TxBuffDMAhandle[NextTFD].dmahandle);\r
+#endif\r
+\r
+ /* The length of the fragment within system memory is defined by\r
+ * the sk_buff structure's len field.\r
+ */\r
+ fraglen = (u16)skb->len;\r
+ sp->TFDList[NextTFD].FragInfo |= cpu_to_le64(IPG_TFI_FRAGLEN &\r
+ (fraglen << 48));\r
+\r
+ /* Clear the TFDDone bit last to indicate the TFD is ready\r
+ * for transfer to the IPG.\r
+ */\r
+ sp->TFDList[NextTFD].TFC &= cpu_to_le64(~IPG_TFC_TFDDONE);\r
+\r
+ /* Record frame transmit start time (jiffies = Linux\r
+ * kernel current time stamp).\r
+ */\r
+ ipg_ethernet_device->trans_start = jiffies;\r
+\r
+ /* Update current TFD indicator. */\r
+ sp->CurrentTFD = NextTFD;\r
+\r
+ /* Calculate the new ID for the next transmit frame by\r
+ * incrementing the CurrentTxFrameID counter.\r
+ */\r
+ sp->CurrentTxFrameID++;\r
+\r
+ /* Force a transmit DMA poll event. */\r
+ IPG_WRITE_DMACTRL(baseaddr, IPG_DC_TX_DMA_POLL_NOW);\r
+\r
+ /* Restore interrupts. */\r
+ spin_unlock_irqrestore(&sp->lock, flags);\r
+\r
+#ifdef IPG_LINUX2_2\r
+ /* Transmit no longer busy.\r
+ * If in 10Mbps mode, do not indicate transmit is free\r
+ * until receive TxComplete interrupt.\r
+ */\r
+ if (!(sp->tenmbpsmode))\r
+ {\r
+ clear_bit(0,(void*)&ipg_ethernet_device->tbusy);\r
+ }\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+#ifdef JUMBO_FRAME\r
+\r
+/* use jumboindex and jumbosize to control jumbo frame status\r
+ initial status is jumboindex=-1 and jumbosize=0\r
+ 1. jumboindex = -1 and jumbosize=0 : previous jumbo frame has been done.\r
+ 2. jumboindex != -1 and jumbosize != 0 : jumbo frame is not over size and receiving\r
+ 3. jumboindex = -1 and jumbosize != 0 : jumbo frame is over size, already dump\r
+ previous receiving and need to continue dumping the current one\r
+*/\r
+enum {NormalPacket,ErrorPacket};\r
+enum {Frame_NoStart_NoEnd=0,Frame_WithStart=1,Frame_WithEnd=10,Frame_WithStart_WithEnd=11};\r
+inline void ipg_nic_rx__FreeSkb(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)ipg_ethernet_device->priv;\r
+ if (sp->RxBuff[sp->CurrentRFD] != NULL)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,sp->RxBuffDMAhandle[sp->CurrentRFD].dmahandle,sp->RxBuffDMAhandle[sp->CurrentRFD].len,PCI_DMA_FROMDEVICE);\r
+#endif\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[sp->CurrentRFD]);\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+ } \r
+}\r
+inline int ipg_nic_rx__CheckFrameSEType(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)ipg_ethernet_device->priv;\r
+ int FoundStartEnd=0;\r
+ \r
+ if(le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS)&IPG_RFS_FRAMESTART)FoundStartEnd+=1;\r
+ if(le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS)&IPG_RFS_FRAMEEND)FoundStartEnd+=10;\r
+ return FoundStartEnd; //Frame_NoStart_NoEnd=0,Frame_WithStart=1,Frame_WithEnd=10,Frame_WithStart_WithEnd=11\r
+}\r
+inline int ipg_nic_rx__CheckError(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)ipg_ethernet_device->priv;\r
+ \r
+ if (IPG_DROP_ON_RX_ETH_ERRORS &&\r
+ (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) &\r
+ (IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |\r
+ IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |\r
+ IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR)))\r
+ {\r
+ IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",\r
+ (unsigned long int) sp->RFDList[sp->CurrentRFD].RFS);\r
+\r
+ /* Increment general receive error statistic. */\r
+ sp->stats.rx_errors++;\r
+\r
+ /* Increment detailed receive error statistics. */\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RXFIFOOVERRUN)\r
+ {\r
+ IPG_DEBUG_MSG("RX FIFO overrun occured.\n");\r
+\r
+ sp->stats.rx_fifo_errors++;\r
+\r
+ if (sp->RxBuffNotReady == 1)\r
+ {\r
+ /* If experience a RxFIFO overrun, and\r
+ * the RxBuffNotReady flag is set,\r
+ * assume the FIFO overran due to lack\r
+ * of an RFD.\r
+ */\r
+ sp->stats.rx_dropped++;\r
+ }\r
+ }\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RXRUNTFRAME)\r
+ {\r
+ IPG_DEBUG_MSG("RX runt occured.\n");\r
+ sp->stats.rx_length_errors++;\r
+ }\r
+\r
+ /* Do nothing for IPG_RFS_RXOVERSIZEDFRAME,\r
+ * error count handled by a IPG statistic register.\r
+ */\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RXALIGNMENTERROR)\r
+ {\r
+ IPG_DEBUG_MSG("RX alignment error occured.\n");\r
+ sp->stats.rx_frame_errors++;\r
+ }\r
+\r
+\r
+ /* Do nothing for IPG_RFS_RXFCSERROR, error count\r
+ * handled by a IPG statistic register.\r
+ */\r
+\r
+ /* Free the memory associated with the RX\r
+ * buffer since it is erroneous and we will\r
+ * not pass it to higher layer processes.\r
+ */\r
+ if (sp->RxBuff[sp->CurrentRFD] != NULL)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].dmahandle,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[sp->CurrentRFD]);\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+ }\r
+ return ErrorPacket;\r
+ }\r
+ return NormalPacket;\r
+}\r
+int ipg_nic_rx(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Transfer received Ethernet frames to higher network layers. */\r
+\r
+ int maxrfdcount;\r
+ int framelen;\r
+ int ThisEndFrameLen;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+ struct sk_buff *skb;\r
+\r
+\r
+ IPG_DEBUG_MSG("_nic_rx\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ maxrfdcount = IPG_MAXRFDPROCESS_COUNT;\r
+\r
+ while(le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RFDDONE)\r
+ {\r
+ if (--maxrfdcount == 0)\r
+ {\r
+ /* There are more RFDs to process, however the\r
+ * allocated amount of RFD processing time has\r
+ * expired. Assert Interrupt Requested to make\r
+ * sure we come back to process the remaining RFDs.\r
+ */\r
+ IPG_WRITE_ASICCTRL(baseaddr,\r
+ IPG_READ_ASICCTRL(baseaddr) |\r
+ IPG_AC_INT_REQUEST);\r
+ break;\r
+ }\r
+\r
+ switch(ipg_nic_rx__CheckFrameSEType(ipg_ethernet_device))\r
+ { // Frame in one RFD\r
+//-----------\r
+ case Frame_WithStart_WithEnd: \r
+ if(sp->Jumbo.FoundStart)\r
+ {\r
+ IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=NULL;\r
+ }\r
+ if(ipg_nic_rx__CheckError(ipg_ethernet_device)==NormalPacket)//1: found error, 0 no error\r
+ { // accept this frame and send to upper layer\r
+ skb = sp->RxBuff[sp->CurrentRFD];\r
+ if(skb)\r
+ {\r
+ framelen=le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RXFRAMELEN;\r
+ if (framelen > IPG_RXFRAG_SIZE) framelen=IPG_RXFRAG_SIZE;\r
+ skb_put(skb, framelen);\r
+ /* Set the buffer's protocol field to Ehternet */\r
+ skb->protocol=eth_type_trans(skb, ipg_ethernet_device);\r
+ /* Not handle TCP/UDP/IP checksum */\r
+ skb->ip_summed=CHECKSUM_NONE;\r
+ netif_rx(skb);\r
+ ipg_ethernet_device->last_rx=jiffies;\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+ }\r
+ }//if(ipg_nic_rx__CheckError(ipg_ethernet_device)==0)//1: found error, 0 no error\r
+ break;//case Frame_WithStart_WithEnd: \r
+//-----------\r
+ case Frame_WithStart: \r
+ if(ipg_nic_rx__CheckError(ipg_ethernet_device)==NormalPacket)//1: found error, 0 no error\r
+ { // accept this frame and send to upper layer\r
+ skb = sp->RxBuff[sp->CurrentRFD];\r
+ if(skb)\r
+ {\r
+ if(sp->Jumbo.FoundStart)\r
+ {\r
+ IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ }\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].dmahandle,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+ sp->Jumbo.FoundStart=1;\r
+ sp->Jumbo.CurrentSize=IPG_RXFRAG_SIZE; \r
+ sp->Jumbo.skb=skb;\r
+ skb_put(sp->Jumbo.skb, IPG_RXFRAG_SIZE);\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+ ipg_ethernet_device->last_rx=jiffies;\r
+ }\r
+ }//if(ipg_nic_rx__CheckError(ipg_ethernet_device)==0)//1: found error, 0 no error\r
+ break;//case Frame_WithStart: \r
+//-----------\r
+ case Frame_WithEnd: \r
+ if(ipg_nic_rx__CheckError(ipg_ethernet_device)==NormalPacket)//1: found error, 0 no error\r
+ { // accept this frame and send to upper layer\r
+ skb = sp->RxBuff[sp->CurrentRFD];\r
+ if(skb)\r
+ {\r
+ if(sp->Jumbo.FoundStart)\r
+ {\r
+ framelen=le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) & IPG_RFS_RXFRAMELEN;\r
+ ThisEndFrameLen=framelen-sp->Jumbo.CurrentSize;\r
+ //if (framelen > IPG_RXFRAG_SIZE) framelen=IPG_RXFRAG_SIZE;\r
+ if(framelen>IPG_RXSUPPORT_SIZE)\r
+ {\r
+ IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ }\r
+ else\r
+ {\r
+ memcpy(skb_put(sp->Jumbo.skb,ThisEndFrameLen),skb->data,ThisEndFrameLen);\r
+ /* Set the buffer's protocol field to Ehternet */\r
+ sp->Jumbo.skb->protocol=eth_type_trans(sp->Jumbo.skb, ipg_ethernet_device);\r
+ /* Not handle TCP/UDP/IP checksum */\r
+ sp->Jumbo.skb->ip_summed=CHECKSUM_NONE;\r
+ netif_rx(sp->Jumbo.skb); \r
+ } \r
+ }//"if(sp->Jumbo.FoundStart)"\r
+ \r
+ ipg_ethernet_device->last_rx=jiffies;\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=NULL;\r
+ //Free this buffer(JC-ADVANCE)\r
+ ipg_nic_rx__FreeSkb(ipg_ethernet_device);\r
+ }//"if(skb)"\r
+ }//"if(ipg_nic_rx__CheckError(ipg_ethernet_device)==0)//1: found error, 0 no error"\r
+ else\r
+ {\r
+ IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=NULL;\r
+ }\r
+ break;//case Frame_WithEnd:\r
+//-----------\r
+ case Frame_NoStart_NoEnd: \r
+ if(ipg_nic_rx__CheckError(ipg_ethernet_device)==NormalPacket)//1: found error, 0 no error\r
+ { // accept this frame and send to upper layer\r
+ skb = sp->RxBuff[sp->CurrentRFD];\r
+ if(skb)\r
+ {\r
+ if(sp->Jumbo.FoundStart)\r
+ {\r
+ //if (framelen > IPG_RXFRAG_SIZE) framelen=IPG_RXFRAG_SIZE;\r
+ sp->Jumbo.CurrentSize+=IPG_RXFRAG_SIZE;\r
+ if(sp->Jumbo.CurrentSize>IPG_RXSUPPORT_SIZE)\r
+ {\r
+ /*IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=NULL;*/\r
+ }\r
+ else\r
+ {\r
+ memcpy(skb_put(sp->Jumbo.skb,IPG_RXFRAG_SIZE),skb->data,IPG_RXFRAG_SIZE);\r
+ }\r
+ }//"if(sp->Jumbo.FoundStart)"\r
+ ipg_ethernet_device->last_rx=jiffies;\r
+ ipg_nic_rx__FreeSkb(ipg_ethernet_device);\r
+ }\r
+ }//if(ipg_nic_rx__CheckError(ipg_ethernet_device)==0)//1: found error, 0 no error\r
+ else\r
+ {\r
+ IPG_DEV_KFREE_SKB(sp->Jumbo.skb);\r
+ sp->Jumbo.FoundStart=0;\r
+ sp->Jumbo.CurrentSize=0;\r
+ sp->Jumbo.skb=NULL;\r
+ }\r
+ break;//case Frame_NoStart_NoEnd:\r
+ }//switch(ipg_nic_rx__CheckFrameSEType(ipg_ethernet_device))\r
+\r
+ sp->CurrentRFD = (sp->CurrentRFD+1) % IPG_RFDLIST_LENGTH;\r
+ } /* end of while(IPG_RFS_RFDDONE)*/\r
+ \r
+ ipg_nic_rxrestore(ipg_ethernet_device);\r
+ return 0;\r
+}\r
+\r
+\r
+#else\r
+int ipg_nic_rx(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Transfer received Ethernet frames to higher network layers. */\r
+\r
+ int maxrfdcount;\r
+ int framelen;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+ struct sk_buff *skb;\r
+\r
+ IPG_DEBUG_MSG("_nic_rx\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ maxrfdcount = IPG_MAXRFDPROCESS_COUNT;\r
+\r
+ while((le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS)) &\r
+ IPG_RFS_RFDDONE &&\r
+ (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS)) &\r
+ IPG_RFS_FRAMESTART &&\r
+ (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS)) &\r
+ IPG_RFS_FRAMEEND &&\r
+ (sp->RxBuff[sp->CurrentRFD] != NULL))\r
+ {\r
+ if (--maxrfdcount == 0)\r
+ {\r
+ /* There are more RFDs to process, however the\r
+ * allocated amount of RFD processing time has\r
+ * expired. Assert Interrupt Requested to make\r
+ * sure we come back to process the remaining RFDs.\r
+ */\r
+ IPG_WRITE_ASICCTRL(baseaddr,\r
+ IPG_READ_ASICCTRL(baseaddr) |\r
+ IPG_AC_INT_REQUEST);\r
+ break;\r
+ }\r
+\r
+ /* Get received frame length. */\r
+ framelen = le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS) &\r
+ IPG_RFS_RXFRAMELEN;\r
+\r
+ /* Check for jumbo frame arrival with too small\r
+ * RXFRAG_SIZE.\r
+ */\r
+ if (framelen > IPG_RXFRAG_SIZE)\r
+ {\r
+ IPG_DEBUG_MSG("RFS FrameLen > allocated fragment size.\n");\r
+\r
+ framelen = IPG_RXFRAG_SIZE;\r
+ }\r
+\r
+ /* Get the received frame buffer. */\r
+ skb = sp->RxBuff[sp->CurrentRFD];\r
+\r
+ if ((IPG_DROP_ON_RX_ETH_ERRORS &&\r
+ (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ (IPG_RFS_RXFIFOOVERRUN |\r
+ IPG_RFS_RXRUNTFRAME |\r
+ IPG_RFS_RXALIGNMENTERROR |\r
+ IPG_RFS_RXFCSERROR |\r
+ IPG_RFS_RXOVERSIZEDFRAME |\r
+ IPG_RFS_RXLENGTHERROR)))))\r
+ {\r
+\r
+ IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n", (unsigned long int) sp->RFDList[sp->CurrentRFD].RFS);\r
+\r
+ /* Increment general receive error statistic. */\r
+ sp->stats.rx_errors++;\r
+\r
+ /* Increment detailed receive error statistics. */\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RXFIFOOVERRUN))\r
+ {\r
+ IPG_DEBUG_MSG("RX FIFO overrun occured.\n");\r
+\r
+ sp->stats.rx_fifo_errors++;\r
+\r
+ if (sp->RxBuffNotReady == 1)\r
+ {\r
+ /* If experience a RxFIFO overrun, and\r
+ * the RxBuffNotReady flag is set,\r
+ * assume the FIFO overran due to lack\r
+ * of an RFD.\r
+ */\r
+ sp->stats.rx_dropped++;\r
+ }\r
+ }\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RXRUNTFRAME))\r
+ {\r
+ IPG_DEBUG_MSG("RX runt occured.\n");\r
+ sp->stats.rx_length_errors++;\r
+ }\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RXOVERSIZEDFRAME));\r
+ /* Do nothing, error count handled by a IPG\r
+ * statistic register.\r
+ */\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RXALIGNMENTERROR))\r
+ {\r
+ IPG_DEBUG_MSG("RX alignment error occured.\n");\r
+ sp->stats.rx_frame_errors++;\r
+ }\r
+\r
+\r
+ if (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RXFCSERROR));\r
+ /* Do nothing, error count handled by a IPG\r
+ * statistic register.\r
+ */\r
+\r
+ /* Free the memory associated with the RX\r
+ * buffer since it is erroneous and we will\r
+ * not pass it to higher layer processes.\r
+ */\r
+ if (sp->RxBuff[sp->CurrentRFD] != NULL)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].dmahandle,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[sp->CurrentRFD]);\r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+\r
+ /* Adjust the new buffer length to accomodate the size\r
+ * of the received frame.\r
+ */\r
+ skb_put(skb, framelen);\r
+\r
+ /* Set the buffer's protocol field to Ethernet. */\r
+ skb->protocol = eth_type_trans(skb,\r
+ ipg_ethernet_device);\r
+\r
+ /* If the frame contains an IP/TCP/UDP frame,\r
+ * determine if upper layer must check IP/TCP/UDP\r
+ * checksums.\r
+ *\r
+ * NOTE: DO NOT RELY ON THE TCP/UDP CHECKSUM\r
+ * VERIFICATION FOR SILICON REVISIONS B3\r
+ * AND EARLIER!\r
+ *\r
+ if ((le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ (IPG_RFS_TCPDETECTED |\r
+ IPG_RFS_UDPDETECTED |\r
+ IPG_RFS_IPDETECTED))) &&\r
+ !(le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ (IPG_RFS_TCPERROR |\r
+ IPG_RFS_UDPERROR |\r
+ IPG_RFS_IPERROR))))\r
+ {\r
+ * Indicate IP checksums were performed\r
+ * by the IPG.\r
+ *\r
+ skb->ip_summed = CHECKSUM_UNNECESSARY;\r
+ }\r
+ else\r
+ */\r
+ if (1==1)\r
+ {\r
+ /* The IPG encountered an error with (or\r
+ * there were no) IP/TCP/UDP checksums.\r
+ * This may or may not indicate an invalid\r
+ * IP/TCP/UDP frame was received. Let the\r
+ * upper layer decide.\r
+ */\r
+ skb->ip_summed = CHECKSUM_NONE;\r
+ }\r
+\r
+ /* Hand off frame for higher layer processing.\r
+ * The function netif_rx() releases the sk_buff\r
+ * when processing completes.\r
+ */\r
+ netif_rx(skb);\r
+\r
+ /* Record frame receive time (jiffies = Linux\r
+ * kernel current time stamp).\r
+ */\r
+ ipg_ethernet_device->last_rx = jiffies;\r
+ }\r
+\r
+ /* Assure RX buffer is not reused by IPG. */\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+\r
+ /* Increment the current RFD counter. */\r
+ sp->CurrentRFD = (sp->CurrentRFD + 1) % IPG_RFDLIST_LENGTH;\r
+\r
+ }\r
+\r
+\r
+#ifdef IPG_DEBUG\r
+ /* Check if the RFD list contained no receive frame data. */\r
+ if (maxrfdcount == IPG_MAXRFDPROCESS_COUNT)\r
+ {\r
+ sp->EmptyRFDListCount++;\r
+ }\r
+#endif\r
+ while((le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_RFDDONE)) &&\r
+ !((le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_FRAMESTART)) &&\r
+ (le64_to_cpu(sp->RFDList[sp->CurrentRFD].RFS &\r
+ IPG_RFS_FRAMEEND))))\r
+ {\r
+ IPG_DEBUG_MSG("Frame requires multiple RFDs.\n");\r
+\r
+ /* An unexpected event, additional code needed to handle\r
+ * properly. So for the time being, just disregard the\r
+ * frame.\r
+ */\r
+\r
+ /* Free the memory associated with the RX\r
+ * buffer since it is erroneous and we will\r
+ * not pass it to higher layer processes.\r
+ */\r
+ if (sp->RxBuff[sp->CurrentRFD] != NULL)\r
+ {\r
+#ifdef IPG_LINUX2_4\r
+ pci_unmap_single(sp->ipg_pci_device,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].dmahandle,\r
+ sp->RxBuffDMAhandle[sp->CurrentRFD].len,\r
+ PCI_DMA_FROMDEVICE);\r
+#endif\r
+ IPG_DEV_KFREE_SKB(sp->RxBuff[sp->CurrentRFD]);\r
+ }\r
+\r
+ /* Assure RX buffer is not reused by IPG. */\r
+ sp->RxBuff[sp->CurrentRFD] = NULL;\r
+\r
+ /* Increment the current RFD counter. */\r
+ sp->CurrentRFD = (sp->CurrentRFD + 1) % IPG_RFDLIST_LENGTH;\r
+ }\r
+\r
+ /* Check to see if there are a minimum number of used\r
+ * RFDs before restoring any (should improve performance.)\r
+ */\r
+ if (((sp->CurrentRFD > sp->LastRestoredRxBuff) &&\r
+ ((sp->LastRestoredRxBuff + IPG_MINUSEDRFDSTOFREE) <=\r
+ sp->CurrentRFD)) ||\r
+ ((sp->CurrentRFD < sp->LastRestoredRxBuff) &&\r
+ ((sp->LastRestoredRxBuff + IPG_MINUSEDRFDSTOFREE) <=\r
+ (sp->CurrentRFD + IPG_RFDLIST_LENGTH))))\r
+ {\r
+ ipg_nic_rxrestore(ipg_ethernet_device);\r
+ }\r
+\r
+ return 0;\r
+}\r
+#endif\r
+int ipg_nic_rxrestore(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Restore used receive buffers. */\r
+\r
+ int i;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_rxrestore\n");\r
+\r
+ /* Assume receive buffers will be available. */\r
+ sp->RxBuffNotReady = 0;\r
+\r
+ while (sp->RxBuff[i = ((sp->LastRestoredRxBuff + 1) %\r
+ IPG_RFDLIST_LENGTH)] == NULL)\r
+ {\r
+ /* Generate a new receive buffer to replace the\r
+ * current buffer (which will be released by the\r
+ * Linux system).\r
+ */\r
+ if (ipg_get_rxbuff(ipg_ethernet_device, i) < 0)\r
+ {\r
+ IPG_DEBUG_MSG("Cannot allocate new Rx buffer.\n");\r
+\r
+ /* Mark a flag indicating a receive buffer\r
+ * was not available. Use this flag to update\r
+ * the rx_dropped Linux statistic.\r
+ */\r
+ sp->RxBuffNotReady = 1;\r
+\r
+ break;\r
+ }\r
+\r
+ /* Reset the RFS field. */\r
+ sp->RFDList[i].RFS = 0x0000000000000000;\r
+\r
+ sp->LastRestoredRxBuff = i;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+IPG_STATS_TYPE* ipg_nic_get_stats(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Provides statistical information about the IPG NIC. */\r
+\r
+ u16 temp1;\r
+ u16 temp2;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_get_stats\n");\r
+\r
+ /* Check to see if the NIC has been initialized via nic_open,\r
+ * before trying to read statistic registers.\r
+ */\r
+#ifdef IPG_LINUX2_2\r
+ if (ipg_ethernet_device->start == 0)\r
+ {\r
+#endif\r
+#ifdef IPG_LINUX2_4\r
+ if (!test_bit(__LINK_STATE_START, &ipg_ethernet_device->state))\r
+ {\r
+#endif\r
+ return &sp->stats;\r
+ }\r
+\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ sp->stats.rx_packets += IPG_READ_FRAMESRCVDOK(baseaddr);\r
+ sp->stats.tx_packets += IPG_READ_FRAMESXMTDOK(baseaddr);\r
+ sp->stats.rx_bytes += IPG_READ_OCTETRCVOK(baseaddr);\r
+ sp->stats.tx_bytes += IPG_READ_OCTETXMTOK(baseaddr);\r
+ temp1 = IPG_READ_FRAMESLOSTRXERRORS(baseaddr);\r
+ sp->stats.rx_errors += temp1;\r
+ sp->stats.rx_missed_errors += temp1;\r
+ temp1 = IPG_READ_SINGLECOLFRAMES(baseaddr) +\r
+ IPG_READ_MULTICOLFRAMES(baseaddr) +\r
+ IPG_READ_LATECOLLISIONS(baseaddr);\r
+ temp2 = IPG_READ_CARRIERSENSEERRORS(baseaddr);\r
+ sp->stats.collisions += temp1;\r
+ sp->stats.tx_dropped += IPG_READ_FRAMESABORTXSCOLLS(baseaddr);\r
+ sp->stats.tx_errors += IPG_READ_FRAMESWEXDEFERRAL(baseaddr) +\r
+ IPG_READ_FRAMESWDEFERREDXMT(baseaddr) +\r
+ temp1 + temp2;\r
+ sp->stats.multicast += IPG_READ_MCSTOCTETRCVDOK(baseaddr);\r
+\r
+ /* detailed tx_errors */\r
+ sp->stats.tx_carrier_errors += temp2;\r
+\r
+ /* detailed rx_errors */\r
+ sp->stats.rx_length_errors += IPG_READ_INRANGELENGTHERRORS(baseaddr) +\r
+ IPG_READ_FRAMETOOLONGERRRORS(baseaddr);\r
+ sp->stats.rx_crc_errors += IPG_READ_FRAMECHECKSEQERRORS(baseaddr);\r
+\r
+ /* Unutilized IPG statistic registers. */\r
+ IPG_READ_MCSTFRAMESRCVDOK(baseaddr);\r
+\r
+ /* Masked IPG statistic registers (need not be read to clear)\r
+ IPG_READ_MACCONTROLFRAMESXMTDOK(baseaddr);\r
+ IPG_READ_BCSTFRAMESXMTDOK(baseaddr);\r
+ IPG_READ_MCSTFRAMESXMTDOK(baseaddr);\r
+ IPG_READ_BCSTOCTETXMTOK(baseaddr);\r
+ IPG_READ_MCSTOCTETXMTOK(baseaddr);\r
+ IPG_READ_MACCONTROLFRAMESRCVD(baseaddr);\r
+ IPG_READ_BCSTFRAMESRCVDOK(baseaddr);\r
+ IPG_READ_BCSTOCTETRCVOK(baseaddr);\r
+ IPG_READ_TXJUMBOFRAMES(baseaddr);\r
+ IPG_READ_UDPCHECKSUMERRORS(baseaddr);\r
+ IPG_READ_IPCHECKSUMERRORS(baseaddr);\r
+ IPG_READ_TCPCHECKSUMERRORS(baseaddr);\r
+ IPG_READ_RXJUMBOFRAMES(baseaddr);\r
+ */\r
+\r
+\r
+ /* Unutilized RMON statistic registers. */\r
+\r
+ /* Masked IPG statistic registers (need not be read to clear)\r
+ IPG_READ_ETHERSTATSCOLLISIONS(baseaddr);\r
+ IPG_READ_ETHERSTATSOCTETSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS64OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS65TO127OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS128TO255OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS256TO511OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS512TO1023OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS1024TO1518OCTESTSTRANSMIT(baseaddr);\r
+ IPG_READ_ETHERSTATSCRCALIGNERRORS(baseaddr);\r
+ IPG_READ_ETHERSTATSUNDERSIZEPKTS(baseaddr);\r
+ IPG_READ_ETHERSTATSFRAGMENTS(baseaddr);\r
+ IPG_READ_ETHERSTATSJABBERS(baseaddr);\r
+ IPG_READ_ETHERSTATSOCTETS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS64OCTESTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS65TO127OCTESTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS128TO255OCTESTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS256TO511OCTESTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS512TO1023OCTESTS(baseaddr);\r
+ IPG_READ_ETHERSTATSPKTS1024TO1518OCTESTS(baseaddr);\r
+ */\r
+\r
+ return &sp->stats;\r
+}\r
+\r
+void ipg_nic_set_multicast_list(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Determine and configure multicast operation and set\r
+ * receive mode for IPG.\r
+ */\r
+ u8 receivemode;\r
+ u32 hashtable[2];\r
+ unsigned int hashindex;\r
+ u32 baseaddr;\r
+ struct dev_mc_list *mc_list_ptr;\r
+\r
+\r
+ IPG_DEBUG_MSG("_nic_set_multicast_list\n");\r
+\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ receivemode = IPG_RM_RECEIVEUNICAST |\r
+ IPG_RM_RECEIVEBROADCAST;\r
+\r
+ if (ipg_ethernet_device->flags & IFF_PROMISC)\r
+ {\r
+ /* NIC to be configured in promiscuous mode. */\r
+ receivemode = IPG_RM_RECEIVEALLFRAMES;\r
+ }\r
+ else if ((ipg_ethernet_device->flags & IFF_ALLMULTI) ||\r
+ (ipg_ethernet_device->flags & IFF_MULTICAST &\r
+ (ipg_ethernet_device->mc_count >\r
+ IPG_MULTICAST_HASHTABLE_SIZE)))\r
+ {\r
+ /* NIC to be configured to receive all multicast\r
+ * frames. */\r
+ receivemode |= IPG_RM_RECEIVEMULTICAST;\r
+ }\r
+ else if (ipg_ethernet_device->flags & IFF_MULTICAST &\r
+ (ipg_ethernet_device->mc_count > 0))\r
+ {\r
+ /* NIC to be configured to receive selected\r
+ * multicast addresses. */\r
+ receivemode |= IPG_RM_RECEIVEMULTICASTHASH;\r
+ }\r
+\r
+ /* Calculate the bits to set for the 64 bit, IPG HASHTABLE.\r
+ * The IPG applies a cyclic-redundancy-check (the same CRC\r
+ * used to calculate the frame data FCS) to the destination\r
+ * address all incoming multicast frames whose destination\r
+ * address has the multicast bit set. The least significant\r
+ * 6 bits of the CRC result are used as an addressing index\r
+ * into the hash table. If the value of the bit addressed by\r
+ * this index is a 1, the frame is passed to the host system.\r
+ */\r
+\r
+ /* Clear hashtable. */\r
+ hashtable[0] = 0x00000000;\r
+ hashtable[1] = 0x00000000;\r
+\r
+ /* Cycle through all multicast addresses to filter.*/\r
+ for (mc_list_ptr = ipg_ethernet_device->mc_list;\r
+ mc_list_ptr != NULL;\r
+ mc_list_ptr = mc_list_ptr->next)\r
+ {\r
+ /* Calculate CRC result for each multicast address. */\r
+ hashindex = ether_crc_le(ETH_ALEN, mc_list_ptr->dmi_addr);\r
+\r
+ /* Use only the least significant 6 bits. */\r
+ hashindex = hashindex & 0x3F;\r
+\r
+ /* Within "hashtable", set bit number "hashindex"\r
+ * to a logic 1.\r
+ */\r
+ set_bit(hashindex, (void*)hashtable);\r
+ }\r
+\r
+ /* Write the value of the hashtable, to the 4, 16 bit\r
+ * HASHTABLE IPG registers.\r
+ */\r
+ IPG_WRITE_HASHTABLE0(baseaddr, hashtable[0]);\r
+ IPG_WRITE_HASHTABLE1(baseaddr, hashtable[1]);\r
+\r
+ IPG_WRITE_RECEIVEMODE(baseaddr, receivemode);\r
+\r
+ IPG_DEBUG_MSG("ReceiveMode = %x\n",IPG_READ_RECEIVEMODE(baseaddr));\r
+\r
+ return;\r
+}\r
+\r
+/*\r
+ * The following code fragment was authored by Donald Becker.\r
+ */\r
+\r
+/* The little-endian AUTODIN II ethernet CRC calculations.\r
+ A big-endian version is also available.\r
+ This is slow but compact code. Do not use this routine for bulk data,\r
+ use a table-based routine instead.\r
+ This is common code and should be moved to net/core/crc.c.\r
+ Chips may use the upper or lower CRC bits, and may reverse and/or invert\r
+ them. Select the endian-ness that results in minimal calculations.\r
+*/\r
+unsigned const ethernet_polynomial_le = 0xedb88320U;\r
+unsigned ether_crc_le(int length, unsigned char *data)\r
+{\r
+ unsigned int crc = 0xffffffff; /* Initial value. */\r
+ while(--length >= 0) {\r
+ unsigned char current_octet = *data++;\r
+ int bit;\r
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {\r
+ if ((crc ^ current_octet) & 1) {\r
+ crc >>= 1;\r
+ crc ^= ethernet_polynomial_le;\r
+ } else\r
+ crc >>= 1;\r
+ }\r
+ }\r
+ return crc;\r
+}\r
+/*\r
+ * End of code fragment authored by Donald Becker.\r
+ */\r
+\r
+int ipg_nic_init(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ /* Initialize IPG NIC. */\r
+\r
+ struct ipg_nic_private *sp = NULL;\r
+\r
+ IPG_DEBUG_MSG("_nic_init\n");\r
+\r
+ /* Register the IPG NIC in the list of Ethernet devices. */\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ ipg_ethernet_device = init_etherdev(ipg_ethernet_device,\r
+ sizeof(struct ipg_nic_private));\r
+#else\r
+ ipg_ethernet_device=alloc_etherdev(sizeof(struct ipg_nic_private));\r
+#endif\r
+\r
+ if (ipg_ethernet_device == NULL)\r
+ {\r
+ printk(KERN_INFO "Could not initialize IP1000 based NIC.\n");\r
+ return -ENODEV;\r
+ }\r
+\r
+ /* Reserve memory for ipg_nic_private structure. */\r
+ sp = kmalloc(sizeof(struct ipg_nic_private),\r
+ GFP_KERNEL);\r
+\r
+ if (sp == NULL)\r
+ {\r
+ printk(KERN_INFO "%s: No memory available for IP1000 private strucutre.\n", ipg_ethernet_device->name);\r
+ return -ENOMEM;\r
+ }\r
+ else\r
+ {\r
+ /* Fill the allocated memory space with 0s.\r
+ * Essentially sets all ipg_nic_private\r
+ * structure fields to 0.\r
+ */\r
+ memset(sp, 0, sizeof(*sp));\r
+ ipg_ethernet_device->priv = sp;\r
+ }\r
+\r
+ /* Assign the new device to the list of IPG Ethernet devices. */\r
+ sp->next_ipg_ethernet_device = root_ipg_ethernet_device;\r
+ root_ipg_ethernet_device = ipg_ethernet_device;\r
+\r
+ /* Declare IPG NIC functions for Ethernet device methods.\r
+ */\r
+ ipg_ethernet_device->open = &ipg_nic_open;\r
+ ipg_ethernet_device->stop = &ipg_nic_stop;\r
+ ipg_ethernet_device->hard_start_xmit = &ipg_nic_hard_start_xmit;\r
+ ipg_ethernet_device->get_stats = &ipg_nic_get_stats;\r
+ ipg_ethernet_device->set_multicast_list =\r
+ &ipg_nic_set_multicast_list;\r
+ ipg_ethernet_device->do_ioctl = & ipg_nic_do_ioctl;\r
+ /* rebuild_header not defined. */\r
+ /* hard_header not defined. */\r
+ /* set_config not defined */\r
+ /* set_mac_address not defined. */\r
+ /* header_cache_bind not defined. */\r
+ /* header_cache_update not defined. */\r
+ ipg_ethernet_device->change_mtu = &ipg_nic_change_mtu;\r
+#ifdef IPG_LINUX2_4\r
+ /* ipg_ethernet_device->tx_timouet not defined. */\r
+ /* ipg_ethernet_device->watchdog_timeo not defined. */\r
+#endif\r
+\r
+ return 0;\r
+}\r
+\r
+int ipg_nic_do_ioctl(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ struct ifreq *req, int cmd)\r
+{\r
+ /* IOCTL commands for IPG NIC.\r
+ *\r
+ * SIOCDEVPRIVATE nothing\r
+ * SIOCDEVPRIVATE+1 register read\r
+ * ifr_data[0] = 0x08, 0x10, 0x20\r
+ * ifr_data[1] = register offset\r
+ * ifr_data[2] = value read\r
+ * SIOCDEVPRIVATE+2 register write\r
+ * ifr_data[0] = 0x08, 0x10, 0x20\r
+ * ifr_data[1] = register offset\r
+ * ifr_data[2] = value to write\r
+ * SIOCDEVPRIVATE+3 GMII register read\r
+ * ifr_data[1] = register offset\r
+ * SIOCDEVPRIVATE+4 GMII register write\r
+ * ifr_data[1] = register offset\r
+ * ifr_data[2] = value to write\r
+ * SIOCDEVPRIVATE+5 PCI register read\r
+ * ifr_data[0] = 0x08, 0x10, 0x20\r
+ * ifr_data[1] = register offset\r
+ * ifr_data[2] = value read\r
+ * SIOCDEVPRIVATE+6 PCI register write\r
+ * ifr_data[0] = 0x08, 0x10, 0x20\r
+ * ifr_data[1] = register offset\r
+ * ifr_data[2] = value to write\r
+ *\r
+ */\r
+\r
+ u8 val8;\r
+ u16 val16;\r
+ u32 val32;\r
+ unsigned int *data;\r
+ int phyaddr = 0;\r
+ u32 baseaddr;\r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+\r
+ IPG_DEBUG_MSG("_nic_do_ioctl\n");\r
+\r
+ data = (unsigned int *)&req->ifr_data;\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+\r
+ switch(cmd)\r
+ {\r
+ case SIOCDEVPRIVATE:\r
+ return 0;\r
+\r
+ case SIOCDEVPRIVATE+1:\r
+ switch(data[0])\r
+ {\r
+ case 0x08:\r
+ data[2] = IPG_READ_BYTEREG(baseaddr + data[1]);\r
+ return 0;\r
+\r
+ case 0x10:\r
+ data[2] = IPG_READ_WORDREG(baseaddr + data[1]);\r
+ return 0;\r
+\r
+ case 0x20:\r
+ data[2] = IPG_READ_LONGREG(baseaddr + data[1]);\r
+ return 0;\r
+\r
+ default:\r
+ data[2] = 0x00;\r
+ return -EINVAL;\r
+ }\r
+\r
+ case SIOCDEVPRIVATE+2:\r
+ switch(data[0])\r
+ {\r
+ case 0x08:\r
+ IPG_WRITE_BYTEREG(baseaddr + data[1], data[2]);\r
+ return 0;\r
+\r
+ case 0x10:\r
+ IPG_WRITE_WORDREG(baseaddr + data[1], data[2]);\r
+ return 0;\r
+\r
+ case 0x20:\r
+ IPG_WRITE_LONGREG(baseaddr + data[1], data[2]);\r
+ return 0;\r
+\r
+ default:\r
+ return -EINVAL;\r
+ }\r
+\r
+ case SIOCDEVPRIVATE+3:\r
+ phyaddr = ipg_find_phyaddr(ipg_ethernet_device);\r
+\r
+ if (phyaddr == -1)\r
+ return -EINVAL;\r
+\r
+ data[2] = read_phy_register(ipg_ethernet_device,\r
+ phyaddr, data[1]);\r
+\r
+ return 0;\r
+\r
+ case SIOCDEVPRIVATE+4:\r
+ phyaddr = ipg_find_phyaddr(ipg_ethernet_device);\r
+\r
+ if (phyaddr == -1)\r
+ return -EINVAL;\r
+\r
+ write_phy_register(ipg_ethernet_device,\r
+ phyaddr, data[1], (u16)data[2]);\r
+\r
+ return 0;\r
+\r
+ case SIOCDEVPRIVATE+5:\r
+ switch(data[0])\r
+ {\r
+ case 0x08:\r
+ pci_read_config_byte(sp->ipg_pci_device,data[1],\r
+ &val8);\r
+ data[2] = (unsigned int)val8;\r
+ return 0;\r
+\r
+ case 0x10:\r
+ pci_read_config_word(sp->ipg_pci_device,data[1],\r
+ &val16);\r
+ data[2] = (unsigned int)val16;\r
+ return 0;\r
+\r
+ case 0x20:\r
+ pci_read_config_dword(sp->ipg_pci_device,data[1],\r
+ &val32);\r
+ data[2] = (unsigned int)val32;\r
+ return 0;\r
+\r
+ default:\r
+ data[2] = 0x00;\r
+ return -EINVAL;\r
+ }\r
+\r
+ case SIOCDEVPRIVATE+6:\r
+ switch(data[0])\r
+ {\r
+ case 0x08:\r
+ pci_write_config_byte(sp->ipg_pci_device,data[1],\r
+ (u8)data[2]);\r
+ return 0;\r
+\r
+ case 0x10:\r
+ pci_write_config_word(sp->ipg_pci_device,data[1],\r
+ (u16)data[2]);\r
+ return 0;\r
+\r
+ case 0x20:\r
+ pci_write_config_dword(sp->ipg_pci_device,data[1],\r
+ (u32)data[2]);\r
+ return 0;\r
+\r
+ default:\r
+ return -EINVAL;\r
+ }\r
+\r
+ case SIOCSIFMTU:\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ default:\r
+ return -EOPNOTSUPP;\r
+ }\r
+}\r
+\r
+int ipg_nic_change_mtu(IPG_DEVICE_TYPE *ipg_ethernet_device,\r
+ int new_mtu)\r
+{\r
+ /* Function to accomodate changes to Maximum Transfer Unit\r
+ * (or MTU) of IPG NIC. Cannot use default function since\r
+ * the default will not allow for MTU > 1500 bytes.\r
+ */\r
+\r
+ IPG_DEBUG_MSG("_nic_change_mtu\n");\r
+\r
+ /* Check that the new MTU value is between 68 (14 byte header, 46\r
+ * byte payload, 4 byte FCS) and IPG_MAX_RXFRAME_SIZE, which\r
+ * corresponds to the MAXFRAMESIZE register in the IPG.\r
+ */\r
+ if ((new_mtu < 68) || (new_mtu > IPG_MAX_RXFRAME_SIZE))\r
+ {\r
+ return -EINVAL;\r
+ }\r
+\r
+ ipg_ethernet_device->mtu = new_mtu;\r
+\r
+ return 0;\r
+}\r
+\r
+#ifdef IPG_LINUX2_2\r
+int ipg_pcibussearch_linux2_2(void)\r
+{\r
+ /* Search for IPG based devices on the Ethernet bus.\r
+ * Code specific to the Linux 2.2 kernel.\r
+ */\r
+\r
+ int error;\r
+ int i;\r
+ int foundipgnic = 0;\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device;\r
+ struct ipg_nic_private *sp;\r
+ struct pci_dev *ipg_pci_device;\r
+\r
+ IPG_DEBUG_MSG("_pcibussearch_linux_2_2\n");\r
+\r
+ for(i=0; ; i++)\r
+ {\r
+ if (nics_supported[i].vendorid == 0xFFFF)\r
+ break;\r
+\r
+ /* Start with the list of all PCI devices. */\r
+ ipg_pci_device = pci_devices;\r
+\r
+ /* Check each entry in the list of all PCI devices. */\r
+ while (ipg_pci_device)\r
+ {\r
+ if ((ipg_pci_device->vendor ==\r
+ nics_supported[i].vendorid) &&\r
+ (ipg_pci_device->device ==\r
+ nics_supported[i].deviceid))\r
+ {\r
+ foundipgnic = 1;\r
+\r
+ printk(KERN_INFO "%s found.\n",\r
+ nics_supported[i].NICname);\r
+ printk(KERN_INFO "Bus %x Slot %x\n",\r
+ ipg_pci_device->bus->number,\r
+ PCI_SLOT(ipg_pci_device->devfn));\r
+\r
+ ipg_ethernet_device = NULL;\r
+\r
+ /* A IPG based NIC was found on the PCI bus.\r
+ * Initialize the NIC.\r
+ */\r
+ error = ipg_nic_init(ipg_ethernet_device);\r
+ if (error < 0)\r
+ {\r
+ printk(KERN_INFO "Could not intialize IP1000 based NIC.\n");\r
+ return error;\r
+ }\r
+ else\r
+ {\r
+ printk(KERN_INFO "Ethernet device registered as: %s\n",\r
+ root_ipg_ethernet_device->name);\r
+ }\r
+\r
+ sp = (struct ipg_nic_private *)\r
+ root_ipg_ethernet_device->priv;\r
+\r
+ /* Save the pointer to the PCI device\r
+ * information.\r
+ */\r
+ sp->ipg_pci_device = ipg_pci_device;\r
+ }\r
+\r
+ /* Move onto the next PCI device in the list. */\r
+ ipg_pci_device = ipg_pci_device->next;\r
+ }\r
+\r
+ }\r
+\r
+ return foundipgnic;\r
+}\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+\r
+/* PCI driver structure for Linux 2.4. */\r
+struct pci_driver ipg_pci_driver =\r
+{\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) \r
+ name: IPG_DRIVER_NAME,\r
+ id_table: pci_devices_supported,\r
+ probe: ipg_pciprobe_linux2_4,\r
+ remove: ipg_pciremove_linux2_4,\r
+#else\r
+ {NULL, NULL},\r
+ IPG_DRIVER_NAME,\r
+ pci_devices_supported,\r
+ ipg_pciprobe_linux2_4,\r
+ ipg_pciremove_linux2_4,\r
+ NULL,\r
+ NULL\r
+#endif\r
+};\r
+\r
+void ipg_pciremove_linux2_4(struct pci_dev *ipg_pci_device_to_remove)\r
+{\r
+ /* Remove function called when a IPG device is\r
+ * to be shut down.\r
+ */\r
+\r
+ IPG_DEVICE_TYPE *prev_ipg_ethernet_device = NULL;\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device = NULL;\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device_to_remove =\r
+ NULL;\r
+ struct ipg_nic_private *prev_sp = NULL;\r
+ struct ipg_nic_private *sp = NULL;\r
+ struct ipg_nic_private *sp_to_remove = NULL;\r
+\r
+ IPG_DEBUG_MSG("_pciremove_linux2_4\n");\r
+\r
+ ipg_ethernet_device = root_ipg_ethernet_device;\r
+\r
+ /* Move through list of Ethernet devices looking for\r
+ * a match.\r
+ */\r
+ while (ipg_ethernet_device)\r
+ {\r
+ sp = (struct ipg_nic_private *)\r
+ (ipg_ethernet_device->priv);\r
+\r
+ if (sp->ipg_pci_device == ipg_pci_device_to_remove)\r
+ {\r
+ /* Save the pointer to the previous Ethernet\r
+ * device.\r
+ */\r
+ ipg_ethernet_device_to_remove =\r
+ ipg_ethernet_device;\r
+\r
+ sp_to_remove = sp;\r
+\r
+ break;\r
+ }\r
+\r
+ /* Save the "previous" device in the list. */\r
+ prev_ipg_ethernet_device = ipg_ethernet_device;\r
+\r
+ /* Retrieve next Ethernet device to be\r
+ * released.\r
+ */\r
+ ipg_ethernet_device = sp->next_ipg_ethernet_device;\r
+ }\r
+\r
+ /* Check if there is a device to remove. */\r
+ if (ipg_ethernet_device_to_remove == NULL)\r
+ {\r
+ /* There are no Ethernet devices to remove. */\r
+ printk(KERN_INFO "A device remove request does not match with any Ethernet devices.\n");\r
+\r
+ return;\r
+ }\r
+\r
+ /* Check to see if we are removing the root device in the list. */\r
+ if (root_ipg_ethernet_device == ipg_ethernet_device_to_remove)\r
+ {\r
+ /* Change the root Ethernet device to the next device to be\r
+ * released.\r
+ */\r
+ root_ipg_ethernet_device =\r
+ sp_to_remove->next_ipg_ethernet_device;\r
+ }\r
+ else if (sp_to_remove->next_ipg_ethernet_device != NULL)\r
+ /* Check if we need to re-link the list of devices. */\r
+ {\r
+ /* If the "previous" Ethernet device is NULL,\r
+ * the device is at the head of the list, and\r
+ * no re-linking is needed.\r
+ */\r
+ prev_sp = (struct ipg_nic_private *)\r
+ (prev_ipg_ethernet_device->priv);\r
+\r
+ prev_sp->next_ipg_ethernet_device =\r
+ sp_to_remove->next_ipg_ethernet_device;\r
+ }\r
+\r
+ /* Free memory associated with Ethernet device's\r
+ * private data structure.\r
+ */\r
+ if (sp_to_remove)\r
+ {\r
+ kfree(sp_to_remove);\r
+ }\r
+\r
+ printk(KERN_INFO "Un-registering Ethernet device %s\n",\r
+ ipg_ethernet_device_to_remove->name);\r
+\r
+ /* Un-register Ethernet device. */\r
+ unregister_netdev(ipg_ethernet_device_to_remove);\r
+\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) \r
+#ifdef USE_IO_OPS\r
+ pci_release_regions(ipg_pci_device_to_remove);\r
+#else\r
+ iounmap((void *)ipg_ethernet_device->base_addr);\r
+#endif\r
+#endif\r
+\r
+\r
+ /* Free memory associated with Ethernet device. */\r
+ if (ipg_ethernet_device_to_remove)\r
+ {\r
+ kfree(ipg_ethernet_device_to_remove);\r
+ }\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) \r
+ pci_set_drvdata(ipg_pci_device_to_remove,NULL);\r
+#endif\r
+ return;\r
+}\r
+\r
+int ipg_pciprobe_linux2_4(struct pci_dev *ipg_pci_device,\r
+ const struct pci_device_id *id)\r
+{\r
+ /* Probe function called when a IPG device is found\r
+ * on the PCI bus.\r
+ */\r
+\r
+ int error;\r
+ int i;\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device = NULL;\r
+ struct ipg_nic_private *sp;\r
+\r
+ IPG_DEBUG_MSG("_pciprobe_linux2_4\n");\r
+\r
+ /* Enable IPG PCI device in Linux system. */\r
+ error = pci_enable_device(ipg_pci_device);\r
+ if (error < 0)\r
+ {\r
+ return error;\r
+ }\r
+\r
+ /* Get the index for the driver description string. */\r
+ i = id->driver_data;\r
+\r
+ printk(KERN_INFO "%s found.\n",\r
+ nics_supported[i].NICname);\r
+ printk(KERN_INFO "Bus %x Slot %x\n",\r
+ ipg_pci_device->bus->number,\r
+ PCI_SLOT(ipg_pci_device->devfn));\r
+\r
+ /* Configure IPG PCI device within Linux system as\r
+ * a bus master.\r
+ */\r
+ pci_set_master(ipg_pci_device);\r
+\r
+ /* Indicate that we can supply 32 bits of address\r
+ * during PCI bus mastering.\r
+ */\r
+ if (pci_dma_supported(ipg_pci_device, 0xFFFFFFFF) < 0)\r
+ {\r
+ printk(KERN_INFO "pci_dma_supported failed.\n");\r
+ return -ENODEV;\r
+ }\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ /* A IPG based NIC was found on the PCI bus.\r
+ * Initialize the NIC.\r
+ */\r
+ if ((ipg_nic_init(ipg_ethernet_device)) < 0)\r
+ {\r
+ printk(KERN_INFO "Could not intialize IP1000 based NIC.\n");\r
+ return -ENODEV;\r
+ }\r
+ else\r
+ {\r
+ printk(KERN_INFO "Ethernet device registered as: %s\n",\r
+ root_ipg_ethernet_device->name);\r
+ }\r
+ sp = (struct ipg_nic_private *)root_ipg_ethernet_device->priv;\r
+ /* Save the pointer to the PCI device information. */\r
+ sp->ipg_pci_device = ipg_pci_device;\r
+#else \r
+ if ((ipg_nic_init(ipg_ethernet_device)) < 0)\r
+ {\r
+ printk(KERN_INFO "Could not intialize IP1000 based NIC.\n");\r
+ return -ENODEV;\r
+ }\r
+ ipg_ethernet_device=root_ipg_ethernet_device;\r
+ SET_MODULE_OWNER(ipg_ethernet_device);\r
+ pci_request_regions(ipg_pci_device,DRV_NAME);\r
+// if(pci_request_regions(ipg_pci_device,DRV_NAME)=err) goto xxx;\r
+\r
+#ifdef USE_IO_OPS\r
+ ipg_ethernet_device->base_addr = pci_resource_start(ipg_pci_device, 0)&0xffffff80;////20040826Jesse_mask_BaseAddr:Mask IOBaseAddr[bit0~6]\r
+#else\r
+ ipg_ethernet_device->base_addr = pci_resource_start(ipg_pci_device, 1)&0xffffff80;//20040826Jesse_mask_BaseAddr:Mask MemBaseAddr[bit0~6]\r
+ ipg_ethernet_device->base_addr = (long) ioremap (ipg_ethernet_device->base_addr, netdev_io_size);\r
+// if (!ioaddr)goto err_out_res;\r
+#endif//#ifdef USE_IO_OPS\r
+ sp = (struct ipg_nic_private *)root_ipg_ethernet_device->priv;\r
+ /* Save the pointer to the PCI device information. */\r
+ sp->ipg_pci_device = ipg_pci_device;\r
+ \r
+ pci_set_drvdata(ipg_pci_device,ipg_ethernet_device);\r
+ \r
+ i = register_netdev(ipg_ethernet_device);\r
+// if (i)goto err_out_unmap_rx;\r
+\r
+ printk(KERN_INFO "Ethernet device registered as: %s\n",\r
+ ipg_ethernet_device->name);\r
+#endif//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+\r
+\r
+ return 0;\r
+}\r
+#endif //#ifdef IPG_LINUX2_4\r
+\r
+\r
+\r
+#ifdef MODULE\r
+/* A modularized driver, i.e. not part of mainstream Linux\r
+ * kernel distribution.\r
+ */\r
+int init_module(void)\r
+{\r
+ /* Initialize the IPG driver module. */\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) \r
+ int foundipgnic = 0;\r
+#endif\r
+\r
+ IPG_DEBUG_MSG("init_module\n");\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) \r
+ return pci_module_init(&ipg_pci_driver);\r
+#else\r
+ \r
+ printk(KERN_INFO "%s", version);\r
+\r
+ /* Define EXPORT_NO_SYMBOLS macro for specifying no\r
+ * symbols to be exported.\r
+ */\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)\r
+ EXPORT_NO_SYMBOLS;\r
+#endif\r
+\r
+ /* The CONFIG_PCI macro is defined for systems which\r
+ * have a PCI bus. If the macro is undefined, there is no\r
+ * PCI bus and the ipg cannot be utilized.\r
+ */\r
+#ifdef CONFIG_PCI\r
+\r
+ printk(KERN_INFO "IPG module searching for Ethernet devices on PCI bus...\n");\r
+\r
+#ifdef IPG_LINUX2_2\r
+ foundipgnic = ipg_pcibussearch_linux2_2();\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ if (pci_register_driver(&ipg_pci_driver) != 0)\r
+ {\r
+ foundipgnic = 1;\r
+ }\r
+ else\r
+ {\r
+ foundipgnic = 0;\r
+ }\r
+#endif\r
+\r
+ if (foundipgnic == 0)\r
+ {\r
+ printk(KERN_INFO " IP1000 based Ethernet device not found on PCI bus.\n");\r
+ return -ENODEV;\r
+ }\r
+\r
+#else /* PCI not available. */\r
+ /* PCI not supported in system. */\r
+ printk(KERN_INFO "No PCI bus, required by IP1000.\n");\r
+ return -ENODEV;\r
+#endif\r
+\r
+ printk(KERN_INFO "IPG module loaded.\n");\r
+\r
+ return 0;\r
+#endif\r
+}\r
+\r
+void cleanup_module(void)\r
+{\r
+ /* Clean up system modifications made by IPG driver\r
+ * module.\r
+ */\r
+\r
+#ifdef IPG_LINUX2_2\r
+\r
+ IPG_DEVICE_TYPE *next_ipg_ethernet_device;\r
+ struct ipg_nic_private *sp;\r
+\r
+ IPG_DEBUG_MSG("cleanup_module\n");\r
+\r
+ /* Unregister all IPG NICs from the list of Ethernet\r
+ * devices.\r
+ */\r
+ while (root_ipg_ethernet_device)\r
+ {\r
+ sp = (struct ipg_nic_private *)\r
+ (root_ipg_ethernet_device->priv);\r
+\r
+ /* Retrieve next Ethernet device to be\r
+ * released.\r
+ */\r
+ next_ipg_ethernet_device = sp->next_ipg_ethernet_device;\r
+\r
+ printk(KERN_INFO "Un-registering Ethernet device %s\n",\r
+ root_ipg_ethernet_device->name);\r
+\r
+ /* Un-register Ethernet device. */\r
+ unregister_netdev(root_ipg_ethernet_device);\r
+\r
+ /* Free memory associated with Ethernet device's\r
+ * private data structure.\r
+ */\r
+ kfree(sp);\r
+\r
+ /* Free memory associated with Ethernet device. */\r
+ kfree(root_ipg_ethernet_device);\r
+\r
+ /* Move to next Ethernet device. */\r
+ root_ipg_ethernet_device = next_ipg_ethernet_device;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef IPG_LINUX2_4\r
+ IPG_DEBUG_MSG("cleanup_module\n");\r
+\r
+ pci_unregister_driver(&ipg_pci_driver);\r
+#endif\r
+\r
+ printk(KERN_INFO "IPG module unloaded.\n");\r
+}\r
+#else /* not MODULE */\r
+\r
+int ipg_nic_probe(IPG_DEVICE_TYPE *dev)\r
+{\r
+ int error;\r
+\r
+ IPG_DEBUG_MSG("_nic_probe\n");\r
+\r
+ error = ipg_nic_init(dev);\r
+ if (error < 0)\r
+ return -ENODEV;\r
+ printk(KERN_INFO "%s", version);\r
+\r
+ return 0;\r
+}\r
+\r
+#endif\r
+void bSetPhyDefaultParam(unsigned char Rev,\r
+ IPG_DEVICE_TYPE *ipg_ethernet_device,int phy_address)\r
+{\r
+ unsigned short Length;\r
+ unsigned char Revision;\r
+ unsigned short *pPHYParam;\r
+ unsigned short address,value;\r
+ \r
+ pPHYParam = &DefaultPhyParam[0];\r
+ Length = *pPHYParam & 0x00FF;\r
+ Revision = (unsigned char) ((*pPHYParam) >> 8);\r
+ pPHYParam++;\r
+ while(Length != 0)\r
+ {\r
+ if(Rev == Revision)\r
+ {\r
+ while(Length >1)\r
+ {\r
+ address=*pPHYParam; \r
+ value=*(pPHYParam+1);\r
+ pPHYParam+=2;\r
+ write_phy_register(ipg_ethernet_device,phy_address,address, value);\r
+ Length -= 4;\r
+ }\r
+\r
+ break;\r
+ }\r
+ else // advanced to next revision\r
+ {\r
+ pPHYParam += Length/2;\r
+ Length = *pPHYParam & 0x00FF;\r
+ Revision = (unsigned char) ((*pPHYParam) >> 8);\r
+ pPHYParam++;\r
+ }\r
+ }\r
+ return;\r
+}\r
+\r
+/*JES20040127EEPROM*/\r
+int read_eeprom(IPG_DEVICE_TYPE *ipg_ethernet_device, int eep_addr)\r
+{\r
+ u32 baseaddr;\r
+ int i = 1000;\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ \r
+ IPG_WRITE_EEPROMCTRL(baseaddr, IPG_EC_EEPROM_READOPCODE | (eep_addr & 0xff));\r
+ while (i-- > 0) {\r
+ mdelay(10);\r
+ if (!(IPG_READ_EEPROMCTRL(baseaddr)&IPG_EC_EEPROM_BUSY)) {\r
+ return IPG_READ_EEPROMDATA (baseaddr);\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/* Write EEPROM JES20040127EEPROM */\r
+//void Eeprom_Write(unsigned int card_index, unsigned eep_addr, unsigned writedata)\r
+void write_eeprom(IPG_DEVICE_TYPE *ipg_ethernet_device, unsigned int eep_addr, unsigned int writedata)\r
+{\r
+/* \r
+ u32 baseaddr;\r
+ baseaddr = ipg_ethernet_device->base_addr;\r
+ IPG_WRITE_EEPROMDATA(baseaddr, writedata );\r
+ while ( (IPG_READ_EEPROMCTRL(baseaddr) & IPG_EC_EEPROM_BUSY ) ) { \r
+ }\r
+ IPG_WRITE_EEPROMCTRL (baseaddr, 0xC0 );\r
+ while ( (IPG_READ_EEPROMCTRL(baseaddr)&IPG_EC_EEPROM_BUSY) ) { \r
+ }\r
+ IPG_WRITE_EEPROMCTRL(baseaddr, IPG_EC_EEPROM_WRITEOPCODE | (eep_addr & 0xff) );\r
+ while ( (IPG_READ_EEPROMCTRL(baseaddr)&IPG_EC_EEPROM_BUSY) ) { \r
+ }\r
+ \r
+ return;*/\r
+}\r
+\r
+/* Set LED_Mode JES20040127EEPROM */\r
+void Set_LED_Mode(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ u32 LED_Mode_Value;\r
+ u32 baseaddr; \r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+ baseaddr = ipg_ethernet_device->base_addr; \r
+ \r
+ LED_Mode_Value=IPG_READ_ASICCTRL(baseaddr);\r
+ LED_Mode_Value &= ~(IPG_AC_LED_MODE_BIT_1 | IPG_AC_LED_MODE |IPG_AC_LED_SPEED);\r
+ \r
+ if((sp->LED_Mode & 0x03) > 1){\r
+ /* Write Asic Control Bit 29 */\r
+ LED_Mode_Value |=IPG_AC_LED_MODE_BIT_1;\r
+ }\r
+ if((sp->LED_Mode & 0x01) == 1){\r
+ /* Write Asic Control Bit 14 */\r
+ LED_Mode_Value |=IPG_AC_LED_MODE;\r
+ }\r
+ if((sp->LED_Mode & 0x08) == 8){\r
+ /* Write Asic Control Bit 27 */\r
+ LED_Mode_Value |=IPG_AC_LED_SPEED;\r
+ } \r
+ IPG_WRITE_ASICCTRL(baseaddr,LED_Mode_Value); \r
+ \r
+ return;\r
+}\r
+\r
+/* Set PHYSet JES20040127EEPROM */\r
+void Set_PHYSet(IPG_DEVICE_TYPE *ipg_ethernet_device)\r
+{\r
+ int PHYSet_Value;\r
+ u32 baseaddr; \r
+ struct ipg_nic_private *sp = (struct ipg_nic_private *)\r
+ ipg_ethernet_device->priv;\r
+ baseaddr = ipg_ethernet_device->base_addr; \r
+ \r
+ PHYSet_Value=IPG_READ_PHYSET(baseaddr);\r
+ PHYSet_Value &= ~(IPG_PS_MEM_LENB9B | IPG_PS_MEM_LEN9 |IPG_PS_NON_COMPDET);\r
+ PHYSet_Value |= ((sp->LED_Mode & 0x70) >> 4);\r
+ IPG_WRITE_PHYSET(baseaddr,PHYSet_Value);\r
+\r
+ return;\r
+}\r
+/* end ipg.c */\r